Compare commits
39 Commits
bfd3de6647
...
3e2be115b0
Author | SHA1 | Date |
---|---|---|
|
3e2be115b0 | |
|
eb4062cf63 | |
|
d9012740a1 | |
|
1b776a7e7e | |
|
e0b7c56458 | |
|
2787158a04 | |
|
2bbc0878e7 | |
|
fb1542429f | |
|
05b23eda59 | |
|
ddd6d72dd7 | |
|
25db8c2fa9 | |
|
2ad7b010e4 | |
|
7c06ffc422 | |
|
df3ed93f84 | |
|
b030e33856 | |
|
7fd3adedee | |
|
ae59578115 | |
|
609a37742c | |
|
d9d796b204 | |
|
6c2c3f08be | |
|
e5e4390494 | |
|
5a09e7a8b4 | |
|
88e6bd1533 | |
|
7d8c98767a | |
|
490222fb78 | |
|
1af4081090 | |
|
33e76f9dfc | |
|
8dd8f636dc | |
|
3451c9a0de | |
|
fc88410c0d | |
|
3682c0069c | |
|
2b42e8f171 | |
|
e990831a09 | |
|
18355a0838 | |
|
811077ca66 | |
|
1c26dae39f | |
|
c37f9d38a3 | |
|
dec3e86e5e | |
|
0c14250678 |
|
@ -38,6 +38,7 @@
|
||||||
- Enhance: プラグインの管理が強化されました
|
- Enhance: プラグインの管理が強化されました
|
||||||
- インストール/アンインストール/設定の変更時にリロード不要になりました
|
- インストール/アンインストール/設定の変更時にリロード不要になりました
|
||||||
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
|
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
|
||||||
|
- Enhance: アイコンのスクロール追従を無効化してパフォーマンス向上できるように
|
||||||
- Enhance: CWの注釈テキストが入力されていない場合, Postボタンを非アクティブに
|
- Enhance: CWの注釈テキストが入力されていない場合, Postボタンを非アクティブに
|
||||||
- Enhance: CWを無効にした場合, 注釈テキストが最大入力文字数を超えていても投稿できるように
|
- Enhance: CWを無効にした場合, 注釈テキストが最大入力文字数を超えていても投稿できるように
|
||||||
- Enhance: テーマ設定画面のデザインを改善
|
- Enhance: テーマ設定画面のデザインを改善
|
||||||
|
|
|
@ -1335,6 +1335,7 @@ information: "Informació"
|
||||||
chat: "Xat"
|
chat: "Xat"
|
||||||
migrateOldSettings: "Migració de la configuració antiga "
|
migrateOldSettings: "Migració de la configuració antiga "
|
||||||
migrateOldSettings_description: "Normalment això es fa automàticament, però si la transició no es fa, el procés es pot iniciar manualment. S'esborrarà la configuració actual."
|
migrateOldSettings_description: "Normalment això es fa automàticament, però si la transició no es fa, el procés es pot iniciar manualment. S'esborrarà la configuració actual."
|
||||||
|
compress: "Comprimir "
|
||||||
_chat:
|
_chat:
|
||||||
noMessagesYet: "Encara no tens missatges "
|
noMessagesYet: "Encara no tens missatges "
|
||||||
newMessage: "Missatge nou"
|
newMessage: "Missatge nou"
|
||||||
|
@ -1363,6 +1364,8 @@ _chat:
|
||||||
newline: "Línia nova "
|
newline: "Línia nova "
|
||||||
muteThisRoom: "Silenciar aquesta sala"
|
muteThisRoom: "Silenciar aquesta sala"
|
||||||
deleteRoom: "Esborrar la sala"
|
deleteRoom: "Esborrar la sala"
|
||||||
|
chatNotAvailableForThisAccountOrServer: "El xat no està disponible per aquest servidor o aquest compte."
|
||||||
|
chatNotAvailableInOtherAccount: "La funció de xat es troba desactivada al compte de l'altre usuari."
|
||||||
cannotChatWithTheUser: "No pots xatejar amb aquest usuari"
|
cannotChatWithTheUser: "No pots xatejar amb aquest usuari"
|
||||||
cannotChatWithTheUser_description: "El xat està desactivat o l'altra part encara no l'ha obert."
|
cannotChatWithTheUser_description: "El xat està desactivat o l'altra part encara no l'ha obert."
|
||||||
chatWithThisUser: "Xateja amb aquest usuari"
|
chatWithThisUser: "Xateja amb aquest usuari"
|
||||||
|
|
|
@ -301,6 +301,7 @@ uploadFromUrlMayTakeTime: "Es kann eine Weile dauern, bis das Hochladen abgeschl
|
||||||
explore: "Erkunden"
|
explore: "Erkunden"
|
||||||
messageRead: "Gelesen"
|
messageRead: "Gelesen"
|
||||||
noMoreHistory: "Kein weiterer Verlauf vorhanden"
|
noMoreHistory: "Kein weiterer Verlauf vorhanden"
|
||||||
|
startChat: "Chat starten"
|
||||||
nUsersRead: "Von {n} Benutzern gelesen"
|
nUsersRead: "Von {n} Benutzern gelesen"
|
||||||
agreeTo: "Ich stimme {0} zu"
|
agreeTo: "Ich stimme {0} zu"
|
||||||
agree: "Zustimmen"
|
agree: "Zustimmen"
|
||||||
|
@ -1318,25 +1319,55 @@ noName: "Kein Name"
|
||||||
skip: "Überspringen"
|
skip: "Überspringen"
|
||||||
restore: "Wiederherstellen"
|
restore: "Wiederherstellen"
|
||||||
syncBetweenDevices: "Zwischen Geräten synchronisieren"
|
syncBetweenDevices: "Zwischen Geräten synchronisieren"
|
||||||
|
preferenceSyncConflictTitle: "Der konfigurierte Wert ist auf dem Server bereits vorhanden."
|
||||||
paste: "Einfügen"
|
paste: "Einfügen"
|
||||||
postForm: "Notizfenster"
|
postForm: "Notizfenster"
|
||||||
textCount: "Zeichenanzahl"
|
textCount: "Zeichenanzahl"
|
||||||
information: "Über"
|
information: "Über"
|
||||||
|
chat: "Chat"
|
||||||
|
compress: "Komprimieren"
|
||||||
_chat:
|
_chat:
|
||||||
|
noMessagesYet: "Noch keine Nachrichten"
|
||||||
|
createRoom: "Raum erstellen"
|
||||||
|
inviteUserToChat: "Lade Benutzer ein, um mit dem Chatten zu beginnen"
|
||||||
|
yourRooms: "Erstellte Räume"
|
||||||
invitations: "Einladen"
|
invitations: "Einladen"
|
||||||
|
noInvitations: "Keine Einladungen"
|
||||||
|
history: "Verlauf"
|
||||||
noHistory: "Kein Verlauf gefunden"
|
noHistory: "Kein Verlauf gefunden"
|
||||||
|
noRooms: "Keine Räume gefunden"
|
||||||
|
inviteUser: "Benutzer einladen"
|
||||||
|
sentInvitations: "Verschickte Einladungen"
|
||||||
|
join: "Beitreten"
|
||||||
|
ignore: "Ignorieren"
|
||||||
|
leave: "Raum verlassen"
|
||||||
members: "Mitglieder"
|
members: "Mitglieder"
|
||||||
|
searchMessages: "Nachrichten suchen"
|
||||||
home: "Startseite"
|
home: "Startseite"
|
||||||
send: "Senden"
|
send: "Senden"
|
||||||
|
muteThisRoom: "Raum stummschalten"
|
||||||
_emojiPalette:
|
_emojiPalette:
|
||||||
palettes: "Palette"
|
palettes: "Palette"
|
||||||
enableSyncBetweenDevicesForPalettes: "Synchronisierung der Paletten zwischen Geräten aktivieren"
|
enableSyncBetweenDevicesForPalettes: "Synchronisierung der Paletten zwischen Geräten aktivieren"
|
||||||
paletteForMain: "Hauptpalette"
|
paletteForMain: "Hauptpalette"
|
||||||
_settings:
|
_settings:
|
||||||
|
driveBanner: "Du kannst den Drive verwalten und konfigurieren, die Auslastung überprüfen und Einstellungen für das Hochladen von Dateien vornehmen."
|
||||||
|
pluginBanner: "Du kannst die Funktionen des Clients mit Plugins erweitern. Plugins können installiert, individuell konfiguriert und verwaltet werden."
|
||||||
api: "API"
|
api: "API"
|
||||||
webhook: "Webhook"
|
webhook: "Webhook"
|
||||||
|
serviceConnectionBanner: "Du kannst Zugriffstoken und Webhooks für die Integration mit externen Anwendungen und Diensten verwalten und konfigurieren."
|
||||||
accountData: "Kontodaten"
|
accountData: "Kontodaten"
|
||||||
|
accountDataBanner: "Export/Import und Verwaltung von Kontodatenarchiven."
|
||||||
|
muteAndBlockBanner: "Du kannst Einstellungen konfigurieren und verwalten, um Inhalte auszublenden und Aktionen für bestimmte Benutzer zu beschränken."
|
||||||
|
accessibilityBanner: "Die Clients können personalisiert und für eine optimale Nutzung im Hinblick auf ihre Darstellung und ihr Verhalten eingerichtet werden."
|
||||||
|
privacyBanner: "Du kannst Einstellungen für die Privatsphäre deines Kontos vornehmen, z. B. inwieweit Inhalte veröffentlicht werden, wie leicht sie zu finden sind und ob Follower genehmigt werden müssen."
|
||||||
|
securityBanner: "Du kannst Einstellungen für die Kontosicherheit konfigurieren, z. B. Passwörter, Anmeldemethoden, Authentifizierungs-Apps und Passkeys."
|
||||||
|
_chat:
|
||||||
|
showSenderName: "Name des Absenders anzeigen"
|
||||||
|
sendOnEnter: "Eingabetaste sendet Nachricht"
|
||||||
_preferencesProfile:
|
_preferencesProfile:
|
||||||
|
profileName: "Profilname"
|
||||||
|
profileNameDescription: "Lege einen Namen fest, der dieses Gerät identifiziert."
|
||||||
profileNameDescription2: "Beispiel: \"Haupt-PC\", \"Smartphone\""
|
profileNameDescription2: "Beispiel: \"Haupt-PC\", \"Smartphone\""
|
||||||
_preferencesBackup:
|
_preferencesBackup:
|
||||||
autoBackup: "Automatische Sicherung"
|
autoBackup: "Automatische Sicherung"
|
||||||
|
@ -1353,9 +1384,11 @@ _accountSettings:
|
||||||
requireSigninToViewContentsDescription2: "Der Inhalt wird nicht in URL-Vorschauen (OGP), eingebettet in Webseiten oder auf Servern, die keine Zitate unterstützen, angezeigt."
|
requireSigninToViewContentsDescription2: "Der Inhalt wird nicht in URL-Vorschauen (OGP), eingebettet in Webseiten oder auf Servern, die keine Zitate unterstützen, angezeigt."
|
||||||
requireSigninToViewContentsDescription3: "Diese Einschränkungen gelten möglicherweise nicht für föderierte Inhalte von anderen Servern."
|
requireSigninToViewContentsDescription3: "Diese Einschränkungen gelten möglicherweise nicht für föderierte Inhalte von anderen Servern."
|
||||||
makeNotesFollowersOnlyBefore: "Macht frühere Notizen nur für Follower sichtbar"
|
makeNotesFollowersOnlyBefore: "Macht frühere Notizen nur für Follower sichtbar"
|
||||||
|
makeNotesFollowersOnlyBeforeDescription: "Solange diese Funktion aktiviert ist, sind Notizen, die nach dem eingestellten Datum und der eingestellten Zeit liegen oder die eingestellte Zeit abgelaufen ist, nur für Follower sichtbar. Bei Deaktivierung wird auch der öffentliche Status der Notiz wiederhergestellt."
|
||||||
makeNotesHiddenBefore: "Frühere Notizen privat machen"
|
makeNotesHiddenBefore: "Frühere Notizen privat machen"
|
||||||
makeNotesHiddenBeforeDescription: ""
|
makeNotesHiddenBeforeDescription: ""
|
||||||
mayNotEffectForFederatedNotes: "Dies hat möglicherweise keine Auswirkungen auf Notizen, die an andere Server föderiert werden."
|
mayNotEffectForFederatedNotes: "Dies hat möglicherweise keine Auswirkungen auf Notizen, die an andere Server föderiert werden."
|
||||||
|
mayNotEffectSomeSituations: "Diese Einschränkungen sind vereinfacht. Sie gelten möglicherweise nicht in allen Situationen, z. B. bei der Anzeige auf einem fremden Server oder während der Moderation."
|
||||||
notesOlderThanSpecifiedDateAndTime: "Notizen vor einem bestimmtem Datum und Uhrzeit"
|
notesOlderThanSpecifiedDateAndTime: "Notizen vor einem bestimmtem Datum und Uhrzeit"
|
||||||
_abuseUserReport:
|
_abuseUserReport:
|
||||||
forward: "Weiterleiten"
|
forward: "Weiterleiten"
|
||||||
|
@ -1364,6 +1397,7 @@ _abuseUserReport:
|
||||||
accept: "Akzeptieren"
|
accept: "Akzeptieren"
|
||||||
reject: "Ablehnen"
|
reject: "Ablehnen"
|
||||||
_delivery:
|
_delivery:
|
||||||
|
status: "Auslieferungsstatus"
|
||||||
stop: "Gesperrt"
|
stop: "Gesperrt"
|
||||||
_type:
|
_type:
|
||||||
none: "Wird veröffentlicht"
|
none: "Wird veröffentlicht"
|
||||||
|
@ -1465,7 +1499,9 @@ _initialTutorial:
|
||||||
title: "Du hast das Tutorial abgeschlossen! 🎉"
|
title: "Du hast das Tutorial abgeschlossen! 🎉"
|
||||||
description: "Die hier beschriebenen Funktionen sind nur ein kleiner Teil dessen, was Misskey zu bieten hat; um mehr darüber zu erfahren, wie du Misskey benutzen kannst, besuche bitte {link}."
|
description: "Die hier beschriebenen Funktionen sind nur ein kleiner Teil dessen, was Misskey zu bieten hat; um mehr darüber zu erfahren, wie du Misskey benutzen kannst, besuche bitte {link}."
|
||||||
_timelineDescription:
|
_timelineDescription:
|
||||||
|
home: "In der Startseiten-Chronik kannst du Notizen von Konten sehen, denen du folgst."
|
||||||
local: "In der lokalen Chronik siehst du Notizen von allen Benutzern auf diesem Server."
|
local: "In der lokalen Chronik siehst du Notizen von allen Benutzern auf diesem Server."
|
||||||
|
social: "Die soziale Chronik zeigt Notizen von der Startseite und der lokalen Chronik."
|
||||||
global: "In der globalen Chronik siehst du Notizen von allen föderierten Servern."
|
global: "In der globalen Chronik siehst du Notizen von allen föderierten Servern."
|
||||||
_serverRules:
|
_serverRules:
|
||||||
description: "Eine Reihe von Regeln, die vor der Registrierung angezeigt werden. Eine Zusammenfassung der Nutzungsbedingungen anzuzeigen ist empfohlen."
|
description: "Eine Reihe von Regeln, die vor der Registrierung angezeigt werden. Eine Zusammenfassung der Nutzungsbedingungen anzuzeigen ist empfohlen."
|
||||||
|
@ -1483,6 +1519,7 @@ _serverSettings:
|
||||||
fanoutTimelineDbFallbackDescription: "Ist diese Option aktiviert, wird die Chronik auf zusätzliche Abfragen in der Datenbank zurückgreifen, wenn sich die Chronik nicht im Cache befindet. Eine Deaktivierung führt zu geringerer Serverlast, aber schränkt den Zeitraum der abrufbaren Chronik ein. "
|
fanoutTimelineDbFallbackDescription: "Ist diese Option aktiviert, wird die Chronik auf zusätzliche Abfragen in der Datenbank zurückgreifen, wenn sich die Chronik nicht im Cache befindet. Eine Deaktivierung führt zu geringerer Serverlast, aber schränkt den Zeitraum der abrufbaren Chronik ein. "
|
||||||
reactionsBufferingDescription: "Wenn diese Option aktiviert ist, kann sie die Leistung beim Erstellen von Reaktionen erheblich verbessern und die Belastung der Datenbank verringern. Allerdings steigt die Speichernutzung von Redis."
|
reactionsBufferingDescription: "Wenn diese Option aktiviert ist, kann sie die Leistung beim Erstellen von Reaktionen erheblich verbessern und die Belastung der Datenbank verringern. Allerdings steigt die Speichernutzung von Redis."
|
||||||
inquiryUrl: "Kontakt-URL"
|
inquiryUrl: "Kontakt-URL"
|
||||||
|
inquiryUrlDescription: "Gib eine URL für das Kontaktformular der Serverbetreiber oder eine Webseite an, die Kontaktinformationen enthält."
|
||||||
openRegistrationWarning: "Das Aktivieren von Registrierungen ist riskant. Es wird empfohlen, sie nur dann zu aktivieren, wenn der Server ständig überwacht wird und im Falle eines Problems sofort reagiert werden kann."
|
openRegistrationWarning: "Das Aktivieren von Registrierungen ist riskant. Es wird empfohlen, sie nur dann zu aktivieren, wenn der Server ständig überwacht wird und im Falle eines Problems sofort reagiert werden kann."
|
||||||
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "Wenn über einen bestimmten Zeitraum keine Moderatorenaktivität festgestellt wird, wird diese Einstellung automatisch deaktiviert, um Spam zu verhindern."
|
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "Wenn über einen bestimmten Zeitraum keine Moderatorenaktivität festgestellt wird, wird diese Einstellung automatisch deaktiviert, um Spam zu verhindern."
|
||||||
_accountMigration:
|
_accountMigration:
|
||||||
|
@ -1800,6 +1837,7 @@ _role:
|
||||||
canManageAvatarDecorations: "Profilbilddekorationen verwalten"
|
canManageAvatarDecorations: "Profilbilddekorationen verwalten"
|
||||||
driveCapacity: "Drive-Kapazität"
|
driveCapacity: "Drive-Kapazität"
|
||||||
alwaysMarkNsfw: "Dateien immer als NSFW markieren"
|
alwaysMarkNsfw: "Dateien immer als NSFW markieren"
|
||||||
|
canUpdateBioMedia: "Kann ein Profil- oder ein Bannerbild bearbeiten"
|
||||||
pinMax: "Maximale Anzahl an angehefteten Notizen"
|
pinMax: "Maximale Anzahl an angehefteten Notizen"
|
||||||
antennaMax: "Maximale Anzahl an Antennen"
|
antennaMax: "Maximale Anzahl an Antennen"
|
||||||
wordMuteMax: "Maximale Zeichenlänge für Wortstummschaltungen"
|
wordMuteMax: "Maximale Zeichenlänge für Wortstummschaltungen"
|
||||||
|
@ -1815,12 +1853,19 @@ _role:
|
||||||
canUseTranslator: "Verwendung des Übersetzers"
|
canUseTranslator: "Verwendung des Übersetzers"
|
||||||
avatarDecorationLimit: "Maximale Anzahl an Profilbilddekorationen, die angebracht werden können"
|
avatarDecorationLimit: "Maximale Anzahl an Profilbilddekorationen, die angebracht werden können"
|
||||||
canImportAntennas: "Importieren von Antennen erlauben"
|
canImportAntennas: "Importieren von Antennen erlauben"
|
||||||
|
canImportBlocking: "Importieren von Blockierungen zulassen"
|
||||||
|
canImportFollowing: "Importieren von Gefolgten zulassen"
|
||||||
|
canImportMuting: "Importieren von Stummgeschalteten zulassen"
|
||||||
canImportUserLists: "Importieren von Listen erlauben"
|
canImportUserLists: "Importieren von Listen erlauben"
|
||||||
|
canChat: "Chatten erlauben"
|
||||||
_condition:
|
_condition:
|
||||||
|
roleAssignedTo: "Manuellen Rollen zugewiesen"
|
||||||
isLocal: "Lokaler Benutzer"
|
isLocal: "Lokaler Benutzer"
|
||||||
isRemote: "Benutzer fremder Instanz"
|
isRemote: "Benutzer fremder Instanz"
|
||||||
isCat: "Katzen-Benutzer"
|
isCat: "Katzen-Benutzer"
|
||||||
isBot: "Bot-Benutzer"
|
isBot: "Bot-Benutzer"
|
||||||
|
isSuspended: "Gesperrter Benutzer"
|
||||||
|
isExplorable: "Benutzer, die ihr Konto im \"Erkunden\"-Bereich sichtbar machen"
|
||||||
createdLessThan: "Kontoerstellung liegt weniger als X zurück"
|
createdLessThan: "Kontoerstellung liegt weniger als X zurück"
|
||||||
createdMoreThan: "Kontoerstellung liegt mehr als X zurück"
|
createdMoreThan: "Kontoerstellung liegt mehr als X zurück"
|
||||||
followersLessThanOrEq: "Hat X oder weniger Follower"
|
followersLessThanOrEq: "Hat X oder weniger Follower"
|
||||||
|
@ -2172,7 +2217,9 @@ _permissions:
|
||||||
"read:admin:ad": "Werbung ansehen"
|
"read:admin:ad": "Werbung ansehen"
|
||||||
"write:invite-codes": "Einladungscodes erstellen"
|
"write:invite-codes": "Einladungscodes erstellen"
|
||||||
"read:invite-codes": "Einladungscodes anzeigen"
|
"read:invite-codes": "Einladungscodes anzeigen"
|
||||||
|
"write:report-abuse": "Verstöße melden"
|
||||||
"write:chat": "Chats bedienen"
|
"write:chat": "Chats bedienen"
|
||||||
|
"read:chat": "Chats durchsuchen"
|
||||||
_auth:
|
_auth:
|
||||||
shareAccessTitle: "Verteilung von App-Berechtigungen"
|
shareAccessTitle: "Verteilung von App-Berechtigungen"
|
||||||
shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?"
|
shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?"
|
||||||
|
@ -2295,6 +2342,7 @@ _profile:
|
||||||
avatarDecorationMax: "Du kannst bis zu {max} Dekorationen hinzufügen."
|
avatarDecorationMax: "Du kannst bis zu {max} Dekorationen hinzufügen."
|
||||||
followedMessage: "Nachricht, wenn dir jemand folgt"
|
followedMessage: "Nachricht, wenn dir jemand folgt"
|
||||||
followedMessageDescription: "Du kannst eine kurze Nachricht festlegen, die dem Empfänger angezeigt wird, wenn er dir folgt."
|
followedMessageDescription: "Du kannst eine kurze Nachricht festlegen, die dem Empfänger angezeigt wird, wenn er dir folgt."
|
||||||
|
followedMessageDescriptionForLockedAccount: "Wenn Folgeanfragen deine Genehmigung brauchen, wird dies beim Genehmigen einer Anfrage angezeigt."
|
||||||
_exportOrImport:
|
_exportOrImport:
|
||||||
allNotes: "Alle Notizen"
|
allNotes: "Alle Notizen"
|
||||||
favoritedNotes: "Als Favorit markierte Notizen"
|
favoritedNotes: "Als Favorit markierte Notizen"
|
||||||
|
@ -2352,6 +2400,7 @@ _play:
|
||||||
title: "Titel"
|
title: "Titel"
|
||||||
script: "Skript"
|
script: "Skript"
|
||||||
summary: "Beschreibung"
|
summary: "Beschreibung"
|
||||||
|
visibilityDescription: "Wenn du die Sichtbarkeit auf Privat stellst, wird der Play nicht auf deinem Profil sichtbar sein, aber jeder, der die URL hat, kann ihn trotzdem aufrufen."
|
||||||
_pages:
|
_pages:
|
||||||
newPage: "Seite erstellen"
|
newPage: "Seite erstellen"
|
||||||
editPage: "Seite bearbeiten"
|
editPage: "Seite bearbeiten"
|
||||||
|
@ -2383,6 +2432,7 @@ _pages:
|
||||||
eyeCatchingImageSet: "Vorschaubild festlegen"
|
eyeCatchingImageSet: "Vorschaubild festlegen"
|
||||||
eyeCatchingImageRemove: "Vorschaubild entfernen"
|
eyeCatchingImageRemove: "Vorschaubild entfernen"
|
||||||
chooseBlock: "Block hinzufügen"
|
chooseBlock: "Block hinzufügen"
|
||||||
|
enterSectionTitle: "Titel des Abschnitts eingeben"
|
||||||
selectType: "Typ auswählen"
|
selectType: "Typ auswählen"
|
||||||
contentBlocks: "Inhalt"
|
contentBlocks: "Inhalt"
|
||||||
inputBlocks: "Eingabe"
|
inputBlocks: "Eingabe"
|
||||||
|
@ -2393,6 +2443,8 @@ _pages:
|
||||||
section: "Abschnitt"
|
section: "Abschnitt"
|
||||||
image: "Bild"
|
image: "Bild"
|
||||||
button: "Knopf"
|
button: "Knopf"
|
||||||
|
dynamic: "Dynamische Bausteine"
|
||||||
|
dynamicDescription: "Dieser Baustein wurde abgeschafft. Bitte verwende von nun an {play}."
|
||||||
note: "Eingebettete Notiz"
|
note: "Eingebettete Notiz"
|
||||||
_note:
|
_note:
|
||||||
id: "Notiz-ID"
|
id: "Notiz-ID"
|
||||||
|
@ -2415,6 +2467,7 @@ _notification:
|
||||||
newNote: "Neue Notiz"
|
newNote: "Neue Notiz"
|
||||||
unreadAntennaNote: "Antenne {name}"
|
unreadAntennaNote: "Antenne {name}"
|
||||||
roleAssigned: "Rolle zugewiesen"
|
roleAssigned: "Rolle zugewiesen"
|
||||||
|
chatRoomInvitationReceived: "Du wurdest in einen Chatraum eingeladen"
|
||||||
emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert"
|
emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert"
|
||||||
achievementEarned: "Errungenschaft freigeschaltet"
|
achievementEarned: "Errungenschaft freigeschaltet"
|
||||||
testNotification: "Testbenachrichtigung"
|
testNotification: "Testbenachrichtigung"
|
||||||
|
|
|
@ -301,6 +301,7 @@ uploadFromUrlMayTakeTime: "It may take some time until the upload is complete."
|
||||||
explore: "Explore"
|
explore: "Explore"
|
||||||
messageRead: "Read"
|
messageRead: "Read"
|
||||||
noMoreHistory: "There is no further history"
|
noMoreHistory: "There is no further history"
|
||||||
|
startChat: "Start chat"
|
||||||
nUsersRead: "read by {n}"
|
nUsersRead: "read by {n}"
|
||||||
agreeTo: "I agree to {0}"
|
agreeTo: "I agree to {0}"
|
||||||
agree: "Agree"
|
agree: "Agree"
|
||||||
|
@ -1331,12 +1332,55 @@ emojiPalette: "Emoji palette"
|
||||||
postForm: "Posting form"
|
postForm: "Posting form"
|
||||||
textCount: "Character count"
|
textCount: "Character count"
|
||||||
information: "About"
|
information: "About"
|
||||||
|
chat: "Chat"
|
||||||
|
migrateOldSettings: "Migrate old client settings"
|
||||||
|
migrateOldSettings_description: "This should be done automatically but if for some reason the migration was not successful, you can trigger the migration process yourself manually. The current configuration information will be overwritten."
|
||||||
|
compress: "Compress"
|
||||||
_chat:
|
_chat:
|
||||||
|
noMessagesYet: "No messages yet"
|
||||||
|
newMessage: "New message"
|
||||||
|
individualChat: "Private Chat"
|
||||||
|
individualChat_description: "Have a private chat with another person."
|
||||||
|
roomChat: "Room Chat"
|
||||||
|
roomChat_description: "A chat room which can have multiple people.\nYou can also invite people who don't allow private chats if they accept the invite."
|
||||||
|
createRoom: "Create Room"
|
||||||
|
inviteUserToChat: "Invite users to start chatting"
|
||||||
|
yourRooms: "Created rooms"
|
||||||
|
joiningRooms: "Joined rooms"
|
||||||
invitations: "Invite"
|
invitations: "Invite"
|
||||||
|
noInvitations: "No invitations"
|
||||||
|
history: "History"
|
||||||
noHistory: "No history available"
|
noHistory: "No history available"
|
||||||
|
noRooms: "No rooms found"
|
||||||
|
inviteUser: "Invite Users"
|
||||||
|
sentInvitations: "Sent Invites"
|
||||||
|
join: "Join"
|
||||||
|
ignore: "Ignore"
|
||||||
|
leave: "Leave room"
|
||||||
members: "Members"
|
members: "Members"
|
||||||
|
searchMessages: "Search messages"
|
||||||
home: "Home"
|
home: "Home"
|
||||||
send: "Send"
|
send: "Send"
|
||||||
|
newline: "New line"
|
||||||
|
muteThisRoom: "Mute room"
|
||||||
|
deleteRoom: "Delete room"
|
||||||
|
chatNotAvailableForThisAccountOrServer: "Chat is not enabled on this server or for this account."
|
||||||
|
chatNotAvailableInOtherAccount: "The chat function is disabled for the other user."
|
||||||
|
cannotChatWithTheUser: "Cannot start a chat with this user"
|
||||||
|
cannotChatWithTheUser_description: "Chat is either unavailable or the other party has not enabled chat."
|
||||||
|
chatWithThisUser: "Chat with user"
|
||||||
|
thisUserAllowsChatOnlyFromFollowers: "This user accepts chats from followers only."
|
||||||
|
thisUserAllowsChatOnlyFromFollowing: "This user accepts chats only from users they follow."
|
||||||
|
thisUserAllowsChatOnlyFromMutualFollowing: "This user only accepts chats from users who are mutual followers."
|
||||||
|
thisUserNotAllowedChatAnyone: "This user is not accepting chats from anyone."
|
||||||
|
chatAllowedUsers: "Who to allow chatting with"
|
||||||
|
chatAllowedUsers_note: "You can chat with anyone to whom you have sent a chat message regardless of this setting."
|
||||||
|
_chatAllowedUsers:
|
||||||
|
everyone: "Everyone"
|
||||||
|
followers: "Only your followers"
|
||||||
|
following: "Only users you are following"
|
||||||
|
mutual: "Mutual followers only"
|
||||||
|
none: "Nobody"
|
||||||
_emojiPalette:
|
_emojiPalette:
|
||||||
palettes: "Palette"
|
palettes: "Palette"
|
||||||
enableSyncBetweenDevicesForPalettes: "Enable palette sync between devices"
|
enableSyncBetweenDevicesForPalettes: "Enable palette sync between devices"
|
||||||
|
@ -1362,6 +1406,12 @@ _settings:
|
||||||
timelineAndNote: "Timeline and note"
|
timelineAndNote: "Timeline and note"
|
||||||
makeEveryTextElementsSelectable: "Make all text elements selectable"
|
makeEveryTextElementsSelectable: "Make all text elements selectable"
|
||||||
makeEveryTextElementsSelectable_description: "Enabling this may reduce usability in some situations."
|
makeEveryTextElementsSelectable_description: "Enabling this may reduce usability in some situations."
|
||||||
|
showNavbarSubButtons: "Show sub-buttons on the navigation bar"
|
||||||
|
ifOn: "When turned on"
|
||||||
|
ifOff: "When turned off"
|
||||||
|
_chat:
|
||||||
|
showSenderName: "Show sender's name"
|
||||||
|
sendOnEnter: "Press Enter to send"
|
||||||
_preferencesProfile:
|
_preferencesProfile:
|
||||||
profileName: "Profile name"
|
profileName: "Profile name"
|
||||||
profileNameDescription: "Set a name that identifies this device."
|
profileNameDescription: "Set a name that identifies this device."
|
||||||
|
@ -1871,6 +1921,7 @@ _role:
|
||||||
canImportFollowing: "Allow importing following"
|
canImportFollowing: "Allow importing following"
|
||||||
canImportMuting: "Allow importing muting"
|
canImportMuting: "Allow importing muting"
|
||||||
canImportUserLists: "Allow importing lists"
|
canImportUserLists: "Allow importing lists"
|
||||||
|
canChat: "Allow Chat"
|
||||||
_condition:
|
_condition:
|
||||||
roleAssignedTo: "Assigned to manual roles"
|
roleAssignedTo: "Assigned to manual roles"
|
||||||
isLocal: "Local user"
|
isLocal: "Local user"
|
||||||
|
@ -2101,6 +2152,7 @@ _sfx:
|
||||||
noteMy: "Own note"
|
noteMy: "Own note"
|
||||||
notification: "Notifications"
|
notification: "Notifications"
|
||||||
reaction: "On choosing a reaction"
|
reaction: "On choosing a reaction"
|
||||||
|
chatMessage: "Chat Messages"
|
||||||
_soundSettings:
|
_soundSettings:
|
||||||
driveFile: "Use an audio file in Drive."
|
driveFile: "Use an audio file in Drive."
|
||||||
driveFileWarn: "Select an audio file from Drive."
|
driveFileWarn: "Select an audio file from Drive."
|
||||||
|
@ -2248,6 +2300,7 @@ _permissions:
|
||||||
"read:federation": "Get federation data"
|
"read:federation": "Get federation data"
|
||||||
"write:report-abuse": "Report violation"
|
"write:report-abuse": "Report violation"
|
||||||
"write:chat": "Compose or delete chat messages"
|
"write:chat": "Compose or delete chat messages"
|
||||||
|
"read:chat": "Browse Chat"
|
||||||
_auth:
|
_auth:
|
||||||
shareAccessTitle: "Granting application permissions"
|
shareAccessTitle: "Granting application permissions"
|
||||||
shareAccess: "Would you like to authorize \"{name}\" to access this account?"
|
shareAccess: "Would you like to authorize \"{name}\" to access this account?"
|
||||||
|
@ -2496,6 +2549,7 @@ _notification:
|
||||||
newNote: "New note"
|
newNote: "New note"
|
||||||
unreadAntennaNote: "Antenna {name}"
|
unreadAntennaNote: "Antenna {name}"
|
||||||
roleAssigned: "Role given"
|
roleAssigned: "Role given"
|
||||||
|
chatRoomInvitationReceived: "You have been invited to a chat room"
|
||||||
emptyPushNotificationMessage: "Push notifications have been updated"
|
emptyPushNotificationMessage: "Push notifications have been updated"
|
||||||
achievementEarned: "Achievement unlocked"
|
achievementEarned: "Achievement unlocked"
|
||||||
testNotification: "Test notification"
|
testNotification: "Test notification"
|
||||||
|
@ -2524,6 +2578,7 @@ _notification:
|
||||||
receiveFollowRequest: "Received follow requests"
|
receiveFollowRequest: "Received follow requests"
|
||||||
followRequestAccepted: "Accepted follow requests"
|
followRequestAccepted: "Accepted follow requests"
|
||||||
roleAssigned: "Role given"
|
roleAssigned: "Role given"
|
||||||
|
chatRoomInvitationReceived: "Invited to chat room"
|
||||||
achievementEarned: "Achievement unlocked"
|
achievementEarned: "Achievement unlocked"
|
||||||
exportCompleted: "The export has been completed"
|
exportCompleted: "The export has been completed"
|
||||||
login: "Sign In"
|
login: "Sign In"
|
||||||
|
@ -2663,6 +2718,7 @@ _moderationLogTypes:
|
||||||
deletePage: "Page deleted"
|
deletePage: "Page deleted"
|
||||||
deleteFlash: "Play deleted"
|
deleteFlash: "Play deleted"
|
||||||
deleteGalleryPost: "Gallery post deleted"
|
deleteGalleryPost: "Gallery post deleted"
|
||||||
|
deleteChatRoom: "Deleted Chat Room"
|
||||||
updateProxyAccountDescription: "Update the description of the proxy account"
|
updateProxyAccountDescription: "Update the description of the proxy account"
|
||||||
_fileViewer:
|
_fileViewer:
|
||||||
title: "File details"
|
title: "File details"
|
||||||
|
|
|
@ -5472,6 +5472,14 @@ export interface Locale extends ILocale {
|
||||||
* ルームを削除
|
* ルームを削除
|
||||||
*/
|
*/
|
||||||
"deleteRoom": string;
|
"deleteRoom": string;
|
||||||
|
/**
|
||||||
|
* このサーバー、またはこのアカウントでチャットは有効化されていません。
|
||||||
|
*/
|
||||||
|
"chatNotAvailableForThisAccountOrServer": string;
|
||||||
|
/**
|
||||||
|
* 相手のアカウントでチャット機能が使えない状態になっています。
|
||||||
|
*/
|
||||||
|
"chatNotAvailableInOtherAccount": string;
|
||||||
/**
|
/**
|
||||||
* このユーザーとのチャットを開始できません
|
* このユーザーとのチャットを開始できません
|
||||||
*/
|
*/
|
||||||
|
@ -5626,6 +5634,10 @@ export interface Locale extends ILocale {
|
||||||
* 有効にすると、一部のシチュエーションでのユーザビリティが低下する場合があります。
|
* 有効にすると、一部のシチュエーションでのユーザビリティが低下する場合があります。
|
||||||
*/
|
*/
|
||||||
"makeEveryTextElementsSelectable_description": string;
|
"makeEveryTextElementsSelectable_description": string;
|
||||||
|
/**
|
||||||
|
* アイコンをスクロールに追従させる
|
||||||
|
*/
|
||||||
|
"useStickyIcons": string;
|
||||||
/**
|
/**
|
||||||
* ナビゲーションバーに副ボタンを表示
|
* ナビゲーションバーに副ボタンを表示
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1335,6 +1335,7 @@ information: "Informazioni"
|
||||||
chat: "Chat"
|
chat: "Chat"
|
||||||
migrateOldSettings: "Migrare le vecchie impostazioni"
|
migrateOldSettings: "Migrare le vecchie impostazioni"
|
||||||
migrateOldSettings_description: "Di solito, viene fatto automaticamente. Se per qualche motivo non fossero migrate con successo, è possibile avviare il processo di migrazione manualmente, sovrascrivendo le configurazioni attuali."
|
migrateOldSettings_description: "Di solito, viene fatto automaticamente. Se per qualche motivo non fossero migrate con successo, è possibile avviare il processo di migrazione manualmente, sovrascrivendo le configurazioni attuali."
|
||||||
|
compress: "Comprimi"
|
||||||
_chat:
|
_chat:
|
||||||
noMessagesYet: "Ancora nessun messaggio"
|
noMessagesYet: "Ancora nessun messaggio"
|
||||||
newMessage: "Nuovo messaggio"
|
newMessage: "Nuovo messaggio"
|
||||||
|
@ -1363,6 +1364,8 @@ _chat:
|
||||||
newline: "Nuova riga"
|
newline: "Nuova riga"
|
||||||
muteThisRoom: "Silenzia stanza"
|
muteThisRoom: "Silenzia stanza"
|
||||||
deleteRoom: "Elimina stanza"
|
deleteRoom: "Elimina stanza"
|
||||||
|
chatNotAvailableForThisAccountOrServer: "Questo server, o questo profilo ha disabilitato la chat."
|
||||||
|
chatNotAvailableInOtherAccount: "La chat non è disponibile nel profilo dell'altra persona."
|
||||||
cannotChatWithTheUser: "Impossibile chattare con questa persona"
|
cannotChatWithTheUser: "Impossibile chattare con questa persona"
|
||||||
cannotChatWithTheUser_description: "La chat potrebbe non essere disponibile, oppure l'altra persona potrebbe non esserlo."
|
cannotChatWithTheUser_description: "La chat potrebbe non essere disponibile, oppure l'altra persona potrebbe non esserlo."
|
||||||
chatWithThisUser: "Chatta con questa persona"
|
chatWithThisUser: "Chatta con questa persona"
|
||||||
|
|
|
@ -1365,6 +1365,8 @@ _chat:
|
||||||
newline: "改行"
|
newline: "改行"
|
||||||
muteThisRoom: "このルームをミュート"
|
muteThisRoom: "このルームをミュート"
|
||||||
deleteRoom: "ルームを削除"
|
deleteRoom: "ルームを削除"
|
||||||
|
chatNotAvailableForThisAccountOrServer: "このサーバー、またはこのアカウントでチャットは有効化されていません。"
|
||||||
|
chatNotAvailableInOtherAccount: "相手のアカウントでチャット機能が使えない状態になっています。"
|
||||||
cannotChatWithTheUser: "このユーザーとのチャットを開始できません"
|
cannotChatWithTheUser: "このユーザーとのチャットを開始できません"
|
||||||
cannotChatWithTheUser_description: "チャットが使えない状態になっているか、相手がチャットを開放していません。"
|
cannotChatWithTheUser_description: "チャットが使えない状態になっているか、相手がチャットを開放していません。"
|
||||||
chatWithThisUser: "チャットする"
|
chatWithThisUser: "チャットする"
|
||||||
|
@ -1407,6 +1409,7 @@ _settings:
|
||||||
timelineAndNote: "タイムラインとノート"
|
timelineAndNote: "タイムラインとノート"
|
||||||
makeEveryTextElementsSelectable: "全てのテキスト要素を選択可能にする"
|
makeEveryTextElementsSelectable: "全てのテキスト要素を選択可能にする"
|
||||||
makeEveryTextElementsSelectable_description: "有効にすると、一部のシチュエーションでのユーザビリティが低下する場合があります。"
|
makeEveryTextElementsSelectable_description: "有効にすると、一部のシチュエーションでのユーザビリティが低下する場合があります。"
|
||||||
|
useStickyIcons: "アイコンをスクロールに追従させる"
|
||||||
showNavbarSubButtons: "ナビゲーションバーに副ボタンを表示"
|
showNavbarSubButtons: "ナビゲーションバーに副ボタンを表示"
|
||||||
ifOn: "オンのとき"
|
ifOn: "オンのとき"
|
||||||
ifOff: "オフのとき"
|
ifOff: "オフのとき"
|
||||||
|
|
|
@ -1335,6 +1335,7 @@ information: "关于"
|
||||||
chat: "聊天"
|
chat: "聊天"
|
||||||
migrateOldSettings: "迁移旧设置信息"
|
migrateOldSettings: "迁移旧设置信息"
|
||||||
migrateOldSettings_description: "通常设置信息将自动迁移。但如果由于某种原因迁移不成功,则可以手动触发迁移过程。当前的配置信息将被覆盖。"
|
migrateOldSettings_description: "通常设置信息将自动迁移。但如果由于某种原因迁移不成功,则可以手动触发迁移过程。当前的配置信息将被覆盖。"
|
||||||
|
compress: "压缩"
|
||||||
_chat:
|
_chat:
|
||||||
noMessagesYet: "还没有消息"
|
noMessagesYet: "还没有消息"
|
||||||
newMessage: "新消息"
|
newMessage: "新消息"
|
||||||
|
@ -1363,6 +1364,8 @@ _chat:
|
||||||
newline: "换行"
|
newline: "换行"
|
||||||
muteThisRoom: "静音此房间"
|
muteThisRoom: "静音此房间"
|
||||||
deleteRoom: "删除房间"
|
deleteRoom: "删除房间"
|
||||||
|
chatNotAvailableForThisAccountOrServer: "此服务器或者账户还未开启聊天功能。"
|
||||||
|
chatNotAvailableInOtherAccount: "对方账户目前处于无法使用聊天的状态。"
|
||||||
cannotChatWithTheUser: "无法与此用户聊天"
|
cannotChatWithTheUser: "无法与此用户聊天"
|
||||||
cannotChatWithTheUser_description: "可能现在无法使用聊天,或者对方未开启聊天。"
|
cannotChatWithTheUser_description: "可能现在无法使用聊天,或者对方未开启聊天。"
|
||||||
chatWithThisUser: "聊天"
|
chatWithThisUser: "聊天"
|
||||||
|
|
|
@ -1335,6 +1335,7 @@ information: "關於"
|
||||||
chat: "聊天"
|
chat: "聊天"
|
||||||
migrateOldSettings: "遷移舊設定資訊"
|
migrateOldSettings: "遷移舊設定資訊"
|
||||||
migrateOldSettings_description: "通常情況下,這會自動進行,但若因某些原因未能順利遷移,您可以手動觸發遷移處理。請注意,當前的設定資訊將會被覆寫。"
|
migrateOldSettings_description: "通常情況下,這會自動進行,但若因某些原因未能順利遷移,您可以手動觸發遷移處理。請注意,當前的設定資訊將會被覆寫。"
|
||||||
|
compress: "壓縮"
|
||||||
_chat:
|
_chat:
|
||||||
noMessagesYet: "尚無訊息"
|
noMessagesYet: "尚無訊息"
|
||||||
newMessage: "新訊息"
|
newMessage: "新訊息"
|
||||||
|
@ -1363,6 +1364,8 @@ _chat:
|
||||||
newline: "換行"
|
newline: "換行"
|
||||||
muteThisRoom: "此聊天室已靜音"
|
muteThisRoom: "此聊天室已靜音"
|
||||||
deleteRoom: "刪除聊天室"
|
deleteRoom: "刪除聊天室"
|
||||||
|
chatNotAvailableForThisAccountOrServer: "這個伺服器或這個帳號的聊天功能尚未啟用。"
|
||||||
|
chatNotAvailableInOtherAccount: "對方的帳號無法使用聊天功能。"
|
||||||
cannotChatWithTheUser: "無法與此使用者聊天"
|
cannotChatWithTheUser: "無法與此使用者聊天"
|
||||||
cannotChatWithTheUser_description: "聊天功能目前無法使用,或對方尚未開放聊天功能。"
|
cannotChatWithTheUser_description: "聊天功能目前無法使用,或對方尚未開放聊天功能。"
|
||||||
chatWithThisUser: "聊天"
|
chatWithThisUser: "聊天"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "2025.3.2-beta.14",
|
"version": "2025.3.2-beta.16",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -463,6 +463,7 @@ export class WebhookTestService {
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
chatScope: 'mutual',
|
chatScope: 'mutual',
|
||||||
|
canChat: true,
|
||||||
twoFactorEnabled: false,
|
twoFactorEnabled: false,
|
||||||
usePasswordLessLogin: false,
|
usePasswordLessLogin: false,
|
||||||
securityKeys: false,
|
securityKeys: false,
|
||||||
|
|
|
@ -557,6 +557,7 @@ export class UserEntityService implements OnModuleInit {
|
||||||
followersVisibility: profile!.followersVisibility,
|
followersVisibility: profile!.followersVisibility,
|
||||||
followingVisibility: profile!.followingVisibility,
|
followingVisibility: profile!.followingVisibility,
|
||||||
chatScope: user.chatScope,
|
chatScope: user.chatScope,
|
||||||
|
canChat: this.roleService.getUserPolicies(user.id).then(r => r.canChat),
|
||||||
roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).sort((a, b) => b.displayOrder - a.displayOrder).map(role => ({
|
roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).sort((a, b) => b.displayOrder - a.displayOrder).map(role => ({
|
||||||
id: role.id,
|
id: role.id,
|
||||||
name: role.name,
|
name: role.name,
|
||||||
|
|
|
@ -363,6 +363,10 @@ export const packedUserDetailedNotMeOnlySchema = {
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
enum: ['everyone', 'following', 'followers', 'mutual', 'none'],
|
enum: ['everyone', 'following', 'followers', 'mutual', 'none'],
|
||||||
},
|
},
|
||||||
|
canChat: {
|
||||||
|
type: 'boolean',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
},
|
||||||
roles: {
|
roles: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
|
|
|
@ -84,6 +84,7 @@ describe('ユーザー', () => {
|
||||||
followingVisibility: user.followingVisibility,
|
followingVisibility: user.followingVisibility,
|
||||||
followersVisibility: user.followersVisibility,
|
followersVisibility: user.followersVisibility,
|
||||||
chatScope: user.chatScope,
|
chatScope: user.chatScope,
|
||||||
|
canChat: user.canChat,
|
||||||
roles: user.roles,
|
roles: user.roles,
|
||||||
memo: user.memo,
|
memo: user.memo,
|
||||||
});
|
});
|
||||||
|
@ -346,6 +347,7 @@ describe('ユーザー', () => {
|
||||||
assert.strictEqual(response.followingVisibility, 'public');
|
assert.strictEqual(response.followingVisibility, 'public');
|
||||||
assert.strictEqual(response.followersVisibility, 'public');
|
assert.strictEqual(response.followersVisibility, 'public');
|
||||||
assert.strictEqual(response.chatScope, 'mutual');
|
assert.strictEqual(response.chatScope, 'mutual');
|
||||||
|
assert.strictEqual(response.canChat, true);
|
||||||
assert.deepStrictEqual(response.roles, []);
|
assert.deepStrictEqual(response.roles, []);
|
||||||
assert.strictEqual(response.memo, null);
|
assert.strictEqual(response.memo, null);
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { computed, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue';
|
import { computed, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { useDocumentVisibility } from '@@/js/use-document-visibility.js';
|
import { useDocumentVisibility } from '@@/js/use-document-visibility.js';
|
||||||
import { onScrollTop, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isTailVisible, isHeadVisible } from '@@/js/scroll.js';
|
import { onScrollTop, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scrollInContainer, isTailVisible, isHeadVisible } from '@@/js/scroll.js';
|
||||||
import type { ComputedRef } from 'vue';
|
import type { ComputedRef } from 'vue';
|
||||||
import { misskeyApi } from '@/misskey-api.js';
|
import { misskeyApi } from '@/misskey-api.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
@ -252,7 +252,7 @@ const fetchMore = async (): Promise<void> => {
|
||||||
|
|
||||||
return nextTick(() => {
|
return nextTick(() => {
|
||||||
if (scrollableElement.value) {
|
if (scrollableElement.value) {
|
||||||
scroll(scrollableElement.value, { top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), behavior: 'instant' });
|
scrollInContainer(scrollableElement.value, { top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), behavior: 'instant' });
|
||||||
} else {
|
} else {
|
||||||
window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' });
|
window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' });
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance = 1
|
||||||
return removeListener;
|
return removeListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scroll(el: HTMLElement, options: ScrollToOptions | undefined) {
|
export function scrollInContainer(el: HTMLElement, options: ScrollToOptions | undefined) {
|
||||||
const container = getScrollContainer(el);
|
const container = getScrollContainer(el);
|
||||||
if (container == null) {
|
if (container == null) {
|
||||||
window.scroll(options);
|
window.scroll(options);
|
||||||
|
@ -108,7 +108,7 @@ export function scroll(el: HTMLElement, options: ScrollToOptions | undefined) {
|
||||||
* @param options Scroll options
|
* @param options Scroll options
|
||||||
*/
|
*/
|
||||||
export function scrollToTop(el: HTMLElement, options: { behavior?: ScrollBehavior; } = {}) {
|
export function scrollToTop(el: HTMLElement, options: { behavior?: ScrollBehavior; } = {}) {
|
||||||
scroll(el, { top: 0, ...options });
|
scrollInContainer(el, { top: 0, ...options });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -58,7 +58,12 @@ export default [
|
||||||
// location ... window.locationと衝突 or 紛らわしい
|
// location ... window.locationと衝突 or 紛らわしい
|
||||||
// document ... window.documentと衝突 or 紛らわしい
|
// document ... window.documentと衝突 or 紛らわしい
|
||||||
// history ... window.historyと衝突 or 紛らわしい
|
// history ... window.historyと衝突 or 紛らわしい
|
||||||
'id-denylist': ['warn', 'window', 'e', 'close', 'open', 'fetch', 'location', 'document', 'history'],
|
// scroll ... window.scrollと衝突 or 紛らわしい
|
||||||
|
// setTimeout ... window.setTimeoutと衝突 or 紛らわしい
|
||||||
|
// setInterval ... window.setIntervalと衝突 or 紛らわしい
|
||||||
|
// clearTimeout ... window.clearTimeoutと衝突 or 紛らわしい
|
||||||
|
// clearInterval ... window.clearIntervalと衝突 or 紛らわしい
|
||||||
|
'id-denylist': ['warn', 'window', 'e', 'close', 'open', 'fetch', 'location', 'document', 'history', 'scroll', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval'],
|
||||||
'no-restricted-globals': [
|
'no-restricted-globals': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
|
@ -85,6 +90,26 @@ export default [
|
||||||
'name': 'history',
|
'name': 'history',
|
||||||
'message': 'Use `window.history`.',
|
'message': 'Use `window.history`.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'name': 'scroll',
|
||||||
|
'message': 'Use `window.scroll`.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'setTimeout',
|
||||||
|
'message': 'Use `window.setTimeout`.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'setInterval',
|
||||||
|
'message': 'Use `window.setInterval`.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'clearTimeout',
|
||||||
|
'message': 'Use `window.clearTimeout`.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'clearInterval',
|
||||||
|
'message': 'Use `window.clearInterval`.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'name': 'name',
|
'name': 'name',
|
||||||
'message': 'Use `window.name`. もしくは name という変数名を定義し忘れている',
|
'message': 'Use `window.name`. もしくは name という変数名を定義し忘れている',
|
||||||
|
|
|
@ -29,7 +29,7 @@ import { fetchCustomEmojis } from '@/custom-emojis.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
import { $i } from '@/i.js';
|
import { $i } from '@/i.js';
|
||||||
|
|
||||||
export async function common(createVue: () => App<Element>) {
|
export async function common(createVue: () => Promise<App<Element>>) {
|
||||||
console.info(`Misskey v${version}`);
|
console.info(`Misskey v${version}`);
|
||||||
|
|
||||||
if (_DEV_) {
|
if (_DEV_) {
|
||||||
|
@ -263,7 +263,7 @@ export async function common(createVue: () => App<Element>) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const app = createVue();
|
const app = await createVue();
|
||||||
|
|
||||||
if (_DEV_) {
|
if (_DEV_) {
|
||||||
app.config.performance = true;
|
app.config.performance = true;
|
||||||
|
|
|
@ -32,7 +32,7 @@ import { signout } from '@/signout.js';
|
||||||
import { migrateOldSettings } from '@/pref-migrate.js';
|
import { migrateOldSettings } from '@/pref-migrate.js';
|
||||||
|
|
||||||
export async function mainBoot() {
|
export async function mainBoot() {
|
||||||
const { isClientUpdated, lastVersion } = await common(() => {
|
const { isClientUpdated, lastVersion } = await common(async () => {
|
||||||
let uiStyle = ui;
|
let uiStyle = ui;
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
@ -46,19 +46,19 @@ export async function mainBoot() {
|
||||||
let rootComponent: Component;
|
let rootComponent: Component;
|
||||||
switch (uiStyle) {
|
switch (uiStyle) {
|
||||||
case 'zen':
|
case 'zen':
|
||||||
rootComponent = defineAsyncComponent(() => import('@/ui/zen.vue'));
|
rootComponent = await import('@/ui/zen.vue').then(x => x.default);
|
||||||
break;
|
break;
|
||||||
case 'deck':
|
case 'deck':
|
||||||
rootComponent = defineAsyncComponent(() => import('@/ui/deck.vue'));
|
rootComponent = await import('@/ui/deck.vue').then(x => x.default);
|
||||||
break;
|
break;
|
||||||
case 'visitor':
|
case 'visitor':
|
||||||
rootComponent = defineAsyncComponent(() => import('@/ui/visitor.vue'));
|
rootComponent = await import('@/ui/visitor.vue').then(x => x.default);
|
||||||
break;
|
break;
|
||||||
case 'classic':
|
case 'classic':
|
||||||
rootComponent = defineAsyncComponent(() => import('@/ui/classic.vue'));
|
rootComponent = await import('@/ui/classic.vue').then(x => x.default);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
rootComponent = defineAsyncComponent(() => import('@/ui/universal.vue'));
|
rootComponent = await import('@/ui/universal.vue').then(x => x.default);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,10 @@
|
||||||
import { createApp, defineAsyncComponent } from 'vue';
|
import { createApp, defineAsyncComponent } from 'vue';
|
||||||
import { common } from './common.js';
|
import { common } from './common.js';
|
||||||
import { emojiPicker } from '@/utility/emoji-picker.js';
|
import { emojiPicker } from '@/utility/emoji-picker.js';
|
||||||
|
import UiMinimum from '@/ui/minimum.vue';
|
||||||
|
|
||||||
export async function subBoot() {
|
export async function subBoot() {
|
||||||
const { isClientUpdated } = await common(() => createApp(
|
const { isClientUpdated } = await common(async () => createApp(UiMinimum));
|
||||||
defineAsyncComponent(() => import('@/ui/minimum.vue')),
|
|
||||||
));
|
|
||||||
|
|
||||||
emojiPicker.init();
|
emojiPicker.init();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,17 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
|
||||||
/* eslint-disable import/no-default-export */
|
|
||||||
import type { StoryObj } from '@storybook/vue3';
|
|
||||||
import { HttpResponse, http } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { expect, userEvent, within } from '@storybook/test';
|
import { expect, userEvent, within } from '@storybook/test';
|
||||||
import { channel } from '../../.storybook/fakes.js';
|
import { channel } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkChannelFollowButton from './MkChannelFollowButton.vue';
|
import MkChannelFollowButton from './MkChannelFollowButton.vue';
|
||||||
|
import type { StoryObj } from '@storybook/vue3';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
function sleep(ms: number) {
|
function sleep(ms: number) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => window.setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Default = {
|
export const Default = {
|
||||||
|
|
|
@ -3,17 +3,15 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
|
||||||
/* eslint-disable import/no-default-export */
|
|
||||||
import type { StoryObj } from '@storybook/vue3';
|
|
||||||
import { HttpResponse, http } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { expect, userEvent, within } from '@storybook/test';
|
import { expect, userEvent, within } from '@storybook/test';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkClickerGame from './MkClickerGame.vue';
|
import MkClickerGame from './MkClickerGame.vue';
|
||||||
|
import type { StoryObj } from '@storybook/vue3';
|
||||||
|
|
||||||
function sleep(ms: number) {
|
function sleep(ms: number) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => window.setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Default = {
|
export const Default = {
|
||||||
|
|
|
@ -79,7 +79,7 @@ function opening() {
|
||||||
picker.value?.focus();
|
picker.value?.focus();
|
||||||
|
|
||||||
// 何故かちょっと待たないとフォーカスされない
|
// 何故かちょっと待たないとフォーカスされない
|
||||||
setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
picker.value?.focus();
|
picker.value?.focus();
|
||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ function touchMove(event: TouchEvent) {
|
||||||
|
|
||||||
pullDistance.value = 0;
|
pullDistance.value = 0;
|
||||||
isSwiping.value = false;
|
isSwiping.value = false;
|
||||||
setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
isSwipingForClass.value = false;
|
isSwipingForClass.value = false;
|
||||||
}, 400);
|
}, 400);
|
||||||
|
|
||||||
|
|
|
@ -339,7 +339,7 @@ const bufferedDataRatio = computed(() => {
|
||||||
// MediaControl Events
|
// MediaControl Events
|
||||||
function onMouseOver() {
|
function onMouseOver() {
|
||||||
if (controlStateTimer) {
|
if (controlStateTimer) {
|
||||||
clearTimeout(controlStateTimer);
|
window.clearTimeout(controlStateTimer);
|
||||||
}
|
}
|
||||||
isHoverring.value = true;
|
isHoverring.value = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ import { deviceKind } from '@/utility/device-kind.js';
|
||||||
import { focusTrap } from '@/utility/focus-trap.js';
|
import { focusTrap } from '@/utility/focus-trap.js';
|
||||||
import { focusParent } from '@/utility/focus.js';
|
import { focusParent } from '@/utility/focus.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
|
import { DI } from '@/di.js';
|
||||||
|
|
||||||
function getFixedContainer(el: Element | null): Element | null {
|
function getFixedContainer(el: Element | null): Element | null {
|
||||||
if (el == null || el.tagName === 'BODY') return null;
|
if (el == null || el.tagName === 'BODY') return null;
|
||||||
|
@ -94,7 +95,7 @@ const emit = defineEmits<{
|
||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
provide('modal', true);
|
provide(DI.inModal, true);
|
||||||
|
|
||||||
const maxHeight = ref<number>();
|
const maxHeight = ref<number>();
|
||||||
const fixed = ref(false);
|
const fixed = ref(false);
|
||||||
|
|
|
@ -14,8 +14,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
>
|
>
|
||||||
<MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
<MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
||||||
<div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div>
|
<div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div>
|
||||||
<!--<div v-if="appearNote._prId_" class="tip"><i class="ti ti-speakerphone"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>-->
|
|
||||||
<!--<div v-if="appearNote._featuredId_" class="tip"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div>-->
|
|
||||||
<div v-if="isRenote" :class="$style.renote">
|
<div v-if="isRenote" :class="$style.renote">
|
||||||
<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
|
<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
|
||||||
<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
|
<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
|
||||||
|
@ -47,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
|
<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
|
||||||
<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
|
<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
|
||||||
<MkAvatar :class="$style.avatar" :user="appearNote.user" :link="!mock" :preview="!mock"/>
|
<MkAvatar :class="[$style.avatar, prefer.s.useStickyIcons ? $style.useSticky : null]" :user="appearNote.user" :link="!mock" :preview="!mock"/>
|
||||||
<div :class="$style.main">
|
<div :class="$style.main">
|
||||||
<MkNoteHeader :note="appearNote" :mini="true"/>
|
<MkNoteHeader :note="appearNote" :mini="true"/>
|
||||||
<MkInstanceTicker v-if="showTicker" :host="appearNote.user.host" :instance="appearNote.user.instance"/>
|
<MkInstanceTicker v-if="showTicker" :host="appearNote.user.host" :instance="appearNote.user.instance"/>
|
||||||
|
@ -852,10 +850,13 @@ function emitUpdReaction(emoji: string, delta: number) {
|
||||||
margin: 0 14px 0 0;
|
margin: 0 14px 0 0;
|
||||||
width: 58px;
|
width: 58px;
|
||||||
height: 58px;
|
height: 58px;
|
||||||
|
|
||||||
|
&.useSticky {
|
||||||
position: sticky !important;
|
position: sticky !important;
|
||||||
top: calc(22px + var(--MI-stickyTop, 0px));
|
top: calc(22px + var(--MI-stickyTop, 0px));
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -1041,9 +1042,12 @@ function emitUpdReaction(emoji: string, delta: number) {
|
||||||
margin: 0 10px 0 0;
|
margin: 0 10px 0 0;
|
||||||
width: 46px;
|
width: 46px;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
|
|
||||||
|
&.useSticky {
|
||||||
top: calc(14px + var(--MI-stickyTop, 0px));
|
top: calc(14px + var(--MI-stickyTop, 0px));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@container (max-width: 400px) {
|
@container (max-width: 400px) {
|
||||||
.root:not(.showActionsOnlyHover) {
|
.root:not(.showActionsOnlyHover) {
|
||||||
|
|
|
@ -13,32 +13,26 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #default="{ items: notes }">
|
<template #default="{ items: notes }">
|
||||||
<div :class="[$style.root, { [$style.noGap]: noGap }]">
|
<div :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap }]">
|
||||||
<MkDateSeparatedList
|
<template v-for="(note, i) in notes" :key="note.id">
|
||||||
ref="notes"
|
<MkNote :class="$style.note" :note="note" :withHardMute="true"/>
|
||||||
v-slot="{ item: note }"
|
<div v-if="note._shouldInsertAd_" :class="$style.ad">
|
||||||
:items="notes"
|
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
|
||||||
:direction="pagination.reversed ? 'up' : 'down'"
|
</div>
|
||||||
:reversed="pagination.reversed"
|
</template>
|
||||||
:noGap="noGap"
|
|
||||||
:ad="true"
|
|
||||||
:class="$style.notes"
|
|
||||||
>
|
|
||||||
<MkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true"/>
|
|
||||||
</MkDateSeparatedList>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useTemplateRef } from 'vue';
|
import { useTemplateRef, TransitionGroup } from 'vue';
|
||||||
import type { Paging } from '@/components/MkPagination.vue';
|
import type { Paging } from '@/components/MkPagination.vue';
|
||||||
import MkNote from '@/components/MkNote.vue';
|
import MkNote from '@/components/MkNote.vue';
|
||||||
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
|
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { infoImageUrl } from '@/instance.js';
|
import { infoImageUrl } from '@/instance.js';
|
||||||
|
import { prefer } from '@/preferences.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
pagination: Paging;
|
pagination: Paging;
|
||||||
|
@ -55,14 +49,24 @@ defineExpose({
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.root {
|
.root {
|
||||||
|
container-type: inline-size;
|
||||||
|
|
||||||
&.noGap {
|
&.noGap {
|
||||||
> .notes {
|
|
||||||
background: var(--MI_THEME-panel);
|
background: var(--MI_THEME-panel);
|
||||||
|
|
||||||
|
.note {
|
||||||
|
border-bottom: solid 0.5px var(--MI_THEME-divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ad {
|
||||||
|
padding: 8px;
|
||||||
|
background-size: auto auto;
|
||||||
|
background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--MI_THEME-bg) 8px, var(--MI_THEME-bg) 14px);
|
||||||
|
border-bottom: solid 0.5px var(--MI_THEME-divider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.noGap) {
|
&:not(.noGap) {
|
||||||
> .notes {
|
|
||||||
background: var(--MI_THEME-bg);
|
background: var(--MI_THEME-bg);
|
||||||
|
|
||||||
.note {
|
.note {
|
||||||
|
@ -71,5 +75,4 @@ defineExpose({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,10 +14,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #default="{ items: notifications }">
|
<template #default="{ items: notifications }">
|
||||||
<MkDateSeparatedList v-slot="{ item: notification }" :class="$style.list" :items="notifications" :noGap="true">
|
<div :class="$style.notifications">
|
||||||
<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id + ':note'" :note="notification.note" :withHardMute="true"/>
|
<template v-for="(notification, i) in notifications" :key="notification.id">
|
||||||
<XNotification v-else :key="notification.id" :notification="notification" :withTime="true" :full="true" class="_panel"/>
|
<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :class="$style.item" :note="notification.note" :withHardMute="true"/>
|
||||||
</MkDateSeparatedList>
|
<XNotification v-else :class="$style.item" :notification="notification" :withTime="true" :full="true"/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</MkPullToRefresh>
|
</MkPullToRefresh>
|
||||||
|
@ -29,7 +31,6 @@ import * as Misskey from 'misskey-js';
|
||||||
import type { notificationTypes } from '@@/js/const.js';
|
import type { notificationTypes } from '@@/js/const.js';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
import XNotification from '@/components/MkNotification.vue';
|
import XNotification from '@/components/MkNotification.vue';
|
||||||
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
|
|
||||||
import MkNote from '@/components/MkNote.vue';
|
import MkNote from '@/components/MkNote.vue';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
@ -84,28 +85,22 @@ onMounted(() => {
|
||||||
connection.on('notificationFlushed', reload);
|
connection.on('notificationFlushed', reload);
|
||||||
});
|
});
|
||||||
|
|
||||||
onActivated(() => {
|
|
||||||
pagingComponent.value?.reload();
|
|
||||||
connection = useStream().useChannel('main');
|
|
||||||
connection.on('notification', onNotification);
|
|
||||||
connection.on('notificationFlushed', reload);
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (connection) connection.dispose();
|
if (connection) connection.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
onDeactivated(() => {
|
|
||||||
if (connection) connection.dispose();
|
|
||||||
});
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
reload,
|
reload,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.list {
|
.notifications {
|
||||||
|
container-type: inline-size;
|
||||||
background: var(--MI_THEME-panel);
|
background: var(--MI_THEME-panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
border-bottom: solid 0.5px var(--MI_THEME-divider);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<MkError v-else-if="error" @retry="init()"/>
|
<MkError v-else-if="error" @retry="init()"/>
|
||||||
|
|
||||||
<div v-else-if="empty" key="_empty_" class="empty">
|
<div v-else-if="empty" key="_empty_">
|
||||||
<slot name="empty">
|
<slot name="empty">
|
||||||
<div class="_fullinfo">
|
<div class="_fullinfo">
|
||||||
<img :src="infoImageUrl" draggable="false"/>
|
<img :src="infoImageUrl" draggable="false"/>
|
||||||
|
@ -29,14 +29,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkButton v-if="!moreFetching" v-appear="(enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMoreAhead : null" :class="$style.more" :wait="moreFetching" primary rounded @click="fetchMoreAhead">
|
<MkButton v-if="!moreFetching" v-appear="(enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMoreAhead : null" :class="$style.more" :wait="moreFetching" primary rounded @click="fetchMoreAhead">
|
||||||
{{ i18n.ts.loadMore }}
|
{{ i18n.ts.loadMore }}
|
||||||
</MkButton>
|
</MkButton>
|
||||||
<MkLoading v-else class="loading"/>
|
<MkLoading v-else/>
|
||||||
</div>
|
</div>
|
||||||
<slot :items="Array.from(items.values())" :fetching="fetching || moreFetching"></slot>
|
<slot :items="Array.from(items.values())" :fetching="fetching || moreFetching"></slot>
|
||||||
<div v-show="!pagination.reversed && more" key="_more_">
|
<div v-show="!pagination.reversed && more" key="_more_">
|
||||||
<MkButton v-if="!moreFetching" v-appear="(enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMore : null" :class="$style.more" :wait="moreFetching" primary rounded @click="fetchMore">
|
<MkButton v-if="!moreFetching" v-appear="(enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMore : null" :class="$style.more" :wait="moreFetching" primary rounded @click="fetchMore">
|
||||||
{{ i18n.ts.loadMore }}
|
{{ i18n.ts.loadMore }}
|
||||||
</MkButton>
|
</MkButton>
|
||||||
<MkLoading v-else class="loading"/>
|
<MkLoading v-else/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { computed, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, useTemplateRef, watch } from 'vue';
|
import { computed, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, useTemplateRef, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { useDocumentVisibility } from '@@/js/use-document-visibility.js';
|
import { useDocumentVisibility } from '@@/js/use-document-visibility.js';
|
||||||
import { onScrollTop, isHeadVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isTailVisible } from '@@/js/scroll.js';
|
import { onScrollTop, isHeadVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scrollInContainer, isTailVisible } from '@@/js/scroll.js';
|
||||||
import type { ComputedRef } from 'vue';
|
import type { ComputedRef } from 'vue';
|
||||||
import type { MisskeyEntity } from '@/types/date-separated-list.js';
|
import type { MisskeyEntity } from '@/types/date-separated-list.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
@ -258,7 +258,7 @@ const fetchMore = async (): Promise<void> => {
|
||||||
|
|
||||||
return nextTick(() => {
|
return nextTick(() => {
|
||||||
if (scrollableElement.value) {
|
if (scrollableElement.value) {
|
||||||
scroll(scrollableElement.value, { top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), behavior: 'instant' });
|
scrollInContainer(scrollableElement.value, { top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), behavior: 'instant' });
|
||||||
} else {
|
} else {
|
||||||
window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' });
|
window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' });
|
||||||
}
|
}
|
||||||
|
@ -357,7 +357,7 @@ watch(visibility, () => {
|
||||||
BACKGROUND_PAUSE_WAIT_SEC * 1000);
|
BACKGROUND_PAUSE_WAIT_SEC * 1000);
|
||||||
} else { // 'visible'
|
} else { // 'visible'
|
||||||
if (timerForSetPause) {
|
if (timerForSetPause) {
|
||||||
clearTimeout(timerForSetPause);
|
window.clearTimeout(timerForSetPause);
|
||||||
timerForSetPause = null;
|
timerForSetPause = null;
|
||||||
} else {
|
} else {
|
||||||
isPausingUpdate = false;
|
isPausingUpdate = false;
|
||||||
|
@ -453,11 +453,11 @@ onBeforeMount(() => {
|
||||||
init().then(() => {
|
init().then(() => {
|
||||||
if (props.pagination.reversed) {
|
if (props.pagination.reversed) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
setTimeout(toBottom, 800);
|
window.setTimeout(toBottom, 800);
|
||||||
|
|
||||||
// scrollToBottomでmoreFetchingボタンが画面外まで出るまで
|
// scrollToBottomでmoreFetchingボタンが画面外まで出るまで
|
||||||
// more = trueを遅らせる
|
// more = trueを遅らせる
|
||||||
setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
moreFetching.value = false;
|
moreFetching.value = false;
|
||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
|
@ -467,11 +467,11 @@ onBeforeMount(() => {
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (timerForSetPause) {
|
if (timerForSetPause) {
|
||||||
clearTimeout(timerForSetPause);
|
window.clearTimeout(timerForSetPause);
|
||||||
timerForSetPause = null;
|
timerForSetPause = null;
|
||||||
}
|
}
|
||||||
if (preventAppearFetchMoreTimer.value) {
|
if (preventAppearFetchMoreTimer.value) {
|
||||||
clearTimeout(preventAppearFetchMoreTimer.value);
|
window.clearTimeout(preventAppearFetchMoreTimer.value);
|
||||||
preventAppearFetchMoreTimer.value = null;
|
preventAppearFetchMoreTimer.value = null;
|
||||||
}
|
}
|
||||||
scrollObserver.value?.disconnect();
|
scrollObserver.value?.disconnect();
|
||||||
|
|
|
@ -140,7 +140,7 @@ import { DI } from '@/di.js';
|
||||||
|
|
||||||
const $i = ensureSignin();
|
const $i = ensureSignin();
|
||||||
|
|
||||||
const modal = inject('modal');
|
const modal = inject(DI.inModal, false);
|
||||||
|
|
||||||
const props = withDefaults(defineProps<PostFormProps & {
|
const props = withDefaults(defineProps<PostFormProps & {
|
||||||
fixed?: boolean;
|
fixed?: boolean;
|
||||||
|
|
|
@ -16,10 +16,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :class="{ [$style.slotClip]: isPullStart }">
|
|
||||||
<slot/>
|
<slot/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -82,11 +81,11 @@ function moveBySystem(to: number): Promise<void> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
let intervalId = setInterval(() => {
|
let intervalId = window.setInterval(() => {
|
||||||
const time = Date.now() - startTime;
|
const time = Date.now() - startTime;
|
||||||
if (time > RELEASE_TRANSITION_DURATION) {
|
if (time > RELEASE_TRANSITION_DURATION) {
|
||||||
pullDistance.value = to;
|
pullDistance.value = to;
|
||||||
clearInterval(intervalId);
|
window.clearInterval(intervalId);
|
||||||
r();
|
r();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -261,8 +260,4 @@ defineExpose({
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.slotClip {
|
|
||||||
overflow-y: clip;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -25,7 +25,7 @@ const props = defineProps<{
|
||||||
menuReaction?: boolean;
|
menuReaction?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const react = inject(DI.mfmEmojiReactCallback);
|
const react = inject(DI.mfmEmojiReactCallback, null);
|
||||||
|
|
||||||
const char2path = prefer.s.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath;
|
const char2path = prefer.s.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath;
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
|
||||||
import { waitFor } from '@storybook/test';
|
import { waitFor } from '@storybook/test';
|
||||||
import type { StoryObj } from '@storybook/vue3';
|
|
||||||
import MkPageHeader from './MkPageHeader.vue';
|
import MkPageHeader from './MkPageHeader.vue';
|
||||||
|
import type { StoryObj } from '@storybook/vue3';
|
||||||
export const Empty = {
|
export const Empty = {
|
||||||
render(args) {
|
render(args) {
|
||||||
return {
|
return {
|
||||||
|
@ -29,7 +28,7 @@ export const Empty = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async play() {
|
async play() {
|
||||||
const wait = new Promise((resolve) => setTimeout(resolve, 800));
|
const wait = new Promise((resolve) => window.setTimeout(resolve, 800));
|
||||||
await waitFor(async () => await wait);
|
await waitFor(async () => await wait);
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
|
|
|
@ -133,7 +133,7 @@ async function enter(el: Element) {
|
||||||
entering = false;
|
entering = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(renderTab, 170);
|
window.setTimeout(renderTab, 170);
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterEnter(el: Element) {
|
function afterEnter(el: Element) {
|
||||||
|
|
|
@ -69,7 +69,6 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const viewId = inject(DI.viewId);
|
const viewId = inject(DI.viewId);
|
||||||
const viewTransitionName = computed(() => `${viewId}---pageHeader`);
|
|
||||||
const injectedPageMetadata = inject(DI.pageMetadata);
|
const injectedPageMetadata = inject(DI.pageMetadata);
|
||||||
const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value);
|
const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value);
|
||||||
|
|
||||||
|
@ -130,7 +129,6 @@ onUnmounted(() => {
|
||||||
backdrop-filter: var(--MI-blur, blur(15px));
|
backdrop-filter: var(--MI-blur, blur(15px));
|
||||||
border-bottom: solid 0.5px var(--MI_THEME-divider);
|
border-bottom: solid 0.5px var(--MI_THEME-divider);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
view-transition-name: v-bind(viewTransitionName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.upper,
|
.upper,
|
||||||
|
|
|
@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="[$style.root, reversed ? '_pageScrollableReversed' : '_pageScrollable']">
|
<div ref="rootEl" :class="[$style.root, reversed ? '_pageScrollableReversed' : '_pageScrollable']">
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="actions" :tabs="tabs"/></template>
|
<template #header><MkPageHeader v-model:tab="tab" :actions="actions" :tabs="tabs"/></template>
|
||||||
<div :class="$style.body">
|
<div :class="$style.body">
|
||||||
|
@ -16,6 +16,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { useTemplateRef } from 'vue';
|
||||||
|
import { scrollInContainer } from '@@/js/scroll.js';
|
||||||
import type { PageHeaderItem } from '@/types/page-header.js';
|
import type { PageHeaderItem } from '@/types/page-header.js';
|
||||||
import type { Tab } from './MkPageHeader.tabs.vue';
|
import type { Tab } from './MkPageHeader.tabs.vue';
|
||||||
|
|
||||||
|
@ -31,6 +33,13 @@ const props = withDefaults(defineProps<{
|
||||||
});
|
});
|
||||||
|
|
||||||
const tab = defineModel<string>('tab');
|
const tab = defineModel<string>('tab');
|
||||||
|
const rootEl = useTemplateRef('rootEl');
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
scrollToTop: () => {
|
||||||
|
if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'smooth' });
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -44,7 +44,9 @@ provide(DI.routerCurrentDepth, currentDepth + 1);
|
||||||
|
|
||||||
const rootEl = useTemplateRef('rootEl');
|
const rootEl = useTemplateRef('rootEl');
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
if (prefer.s.animation) {
|
||||||
rootEl.value.style.viewTransitionName = viewId; // view-transition-nameにcss varが使えないっぽいため直接代入
|
rootEl.value.style.viewTransitionName = viewId; // view-transition-nameにcss varが使えないっぽいため直接代入
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// view-transition-newなどの<pt-name-selector>にはcss varが使えず、v-bindできないため直接スタイルを生成
|
// view-transition-newなどの<pt-name-selector>にはcss varが使えず、v-bindできないため直接スタイルを生成
|
||||||
|
|
|
@ -50,6 +50,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref, toRefs, watch } from 'vue';
|
import { computed, onMounted, ref, toRefs, watch } from 'vue';
|
||||||
|
import type { DataSource, GridSetting, GridState, Size } from '@/components/grid/grid.js';
|
||||||
|
import type { CellAddress, CellValue, GridCell } from '@/components/grid/cell.js';
|
||||||
|
import type { GridContext, GridEvent } from '@/components/grid/grid-event.js';
|
||||||
|
import type { GridColumn } from '@/components/grid/column.js';
|
||||||
|
import type { GridRow, GridRowSetting } from '@/components/grid/row.js';
|
||||||
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import { GridEventEmitter } from '@/components/grid/grid.js';
|
import { GridEventEmitter } from '@/components/grid/grid.js';
|
||||||
import MkDataRow from '@/components/grid/MkDataRow.vue';
|
import MkDataRow from '@/components/grid/MkDataRow.vue';
|
||||||
import MkHeaderRow from '@/components/grid/MkHeaderRow.vue';
|
import MkHeaderRow from '@/components/grid/MkHeaderRow.vue';
|
||||||
|
@ -68,13 +74,6 @@ import { createColumn } from '@/components/grid/column.js';
|
||||||
import { createRow, defaultGridRowSetting, resetRow } from '@/components/grid/row.js';
|
import { createRow, defaultGridRowSetting, resetRow } from '@/components/grid/row.js';
|
||||||
import { handleKeyEvent } from '@/utility/key-event.js';
|
import { handleKeyEvent } from '@/utility/key-event.js';
|
||||||
|
|
||||||
import type { DataSource, GridSetting, GridState, Size } from '@/components/grid/grid.js';
|
|
||||||
import type { CellAddress, CellValue, GridCell } from '@/components/grid/cell.js';
|
|
||||||
import type { GridContext, GridEvent } from '@/components/grid/grid-event.js';
|
|
||||||
import type { GridColumn } from '@/components/grid/column.js';
|
|
||||||
import type { GridRow, GridRowSetting } from '@/components/grid/row.js';
|
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
|
||||||
|
|
||||||
type RowHolder = {
|
type RowHolder = {
|
||||||
row: GridRow,
|
row: GridRow,
|
||||||
cells: GridCell[],
|
cells: GridCell[],
|
||||||
|
@ -130,7 +129,7 @@ const bus = new GridEventEmitter();
|
||||||
*
|
*
|
||||||
* @see {@link onResize}
|
* @see {@link onResize}
|
||||||
*/
|
*/
|
||||||
const resizeObserver = new ResizeObserver((entries) => setTimeout(() => onResize(entries)));
|
const resizeObserver = new ResizeObserver((entries) => window.setTimeout(() => onResize(entries)));
|
||||||
|
|
||||||
const rootEl = ref<InstanceType<typeof HTMLTableElement>>();
|
const rootEl = ref<InstanceType<typeof HTMLTableElement>>();
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,4 +15,5 @@ export const DI = {
|
||||||
currentStickyTop: Symbol() as InjectionKey<Ref<number>>,
|
currentStickyTop: Symbol() as InjectionKey<Ref<number>>,
|
||||||
currentStickyBottom: Symbol() as InjectionKey<Ref<number>>,
|
currentStickyBottom: Symbol() as InjectionKey<Ref<number>>,
|
||||||
mfmEmojiReactCallback: Symbol() as InjectionKey<(emoji: string) => void>,
|
mfmEmojiReactCallback: Symbol() as InjectionKey<(emoji: string) => void>,
|
||||||
|
inModal: Symbol() as InjectionKey<boolean>,
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,8 @@ import type { ComponentProps as CP } from 'vue-component-type-helpers';
|
||||||
import type { Form, GetFormResultType } from '@/utility/form.js';
|
import type { Form, GetFormResultType } from '@/utility/form.js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import type { PostFormProps } from '@/types/post-form.js';
|
import type { PostFormProps } from '@/types/post-form.js';
|
||||||
|
import type MkRoleSelectDialog_TypeReferenceOnly from '@/components/MkRoleSelectDialog.vue';
|
||||||
|
import type MkEmojiPickerDialog_TypeReferenceOnly from '@/components/MkEmojiPickerDialog.vue';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
@ -23,8 +25,6 @@ import MkToast from '@/components/MkToast.vue';
|
||||||
import MkDialog from '@/components/MkDialog.vue';
|
import MkDialog from '@/components/MkDialog.vue';
|
||||||
import MkPopupMenu from '@/components/MkPopupMenu.vue';
|
import MkPopupMenu from '@/components/MkPopupMenu.vue';
|
||||||
import MkContextMenu from '@/components/MkContextMenu.vue';
|
import MkContextMenu from '@/components/MkContextMenu.vue';
|
||||||
import type MkRoleSelectDialog_TypeReferenceOnly from '@/components/MkRoleSelectDialog.vue';
|
|
||||||
import type MkEmojiPickerDialog_TypeReferenceOnly from '@/components/MkEmojiPickerDialog.vue';
|
|
||||||
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
|
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
|
||||||
import { pleaseLogin } from '@/utility/please-login.js';
|
import { pleaseLogin } from '@/utility/please-login.js';
|
||||||
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
||||||
|
|
|
@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch, ref, useTemplateRef } from 'vue';
|
import { computed, watch, ref, useTemplateRef } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { scroll } from '@@/js/scroll.js';
|
import { scrollInContainer } from '@@/js/scroll.js';
|
||||||
import MkTimeline from '@/components/MkTimeline.vue';
|
import MkTimeline from '@/components/MkTimeline.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
@ -49,7 +49,7 @@ function queueUpdated(q) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function top() {
|
function top() {
|
||||||
scroll(rootEl.value, { top: 0 });
|
scrollInContainer(rootEl.value, { top: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function timetravel() {
|
async function timetravel() {
|
||||||
|
|
|
@ -242,6 +242,10 @@ function showMenu(ev: MouseEvent, contextmenu = false) {
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fukidashi {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
|
|
|
@ -5,7 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkButton primary gradate rounded :class="$style.start" @click="start"><i class="ti ti-plus"></i> {{ i18n.ts.startChat }}</MkButton>
|
<MkButton v-if="$i.policies.canChat" primary gradate rounded :class="$style.start" @click="start"><i class="ti ti-plus"></i> {{ i18n.ts.startChat }}</MkButton>
|
||||||
|
|
||||||
|
<MkInfo v-else>{{ i18n.ts._chat.chatNotAvailableForThisAccountOrServer }}</MkInfo>
|
||||||
|
|
||||||
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
|
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
|
||||||
|
|
||||||
|
@ -78,6 +80,7 @@ import * as os from '@/os.js';
|
||||||
import { updateCurrentAccountPartial } from '@/accounts.js';
|
import { updateCurrentAccountPartial } from '@/accounts.js';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
||||||
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
|
|
||||||
const $i = ensureSignin();
|
const $i = ensureSignin();
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<XMessage v-for="message in messages.toReversed()" :key="message.id" :message="message"/>
|
<XMessage v-for="message in messages.toReversed()" :key="message.id" :message="message"/>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="user && (!user.canChat || user.host !== null)">
|
||||||
|
<MkInfo warn>{{ i18n.ts._chat.chatNotAvailableInOtherAccount }}</MkInfo>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MkInfo v-if="!$i.policies.canChat" warn>{{ i18n.ts._chat.chatNotAvailableForThisAccountOrServer }}</MkInfo>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
|
||||||
<MkSpacer v-else-if="tab === 'search'" :contentMax="700">
|
<MkSpacer v-else-if="tab === 'search'" :contentMax="700">
|
||||||
|
@ -93,6 +99,7 @@ import { prefer } from '@/preferences.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { useRouter } from '@/router.js';
|
import { useRouter } from '@/router.js';
|
||||||
import { useMutationObserver } from '@/use/use-mutation-observer.js';
|
import { useMutationObserver } from '@/use/use-mutation-observer.js';
|
||||||
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
|
|
||||||
const $i = ensureSignin();
|
const $i = ensureSignin();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
|
@ -6,7 +6,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template>
|
<template>
|
||||||
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
|
||||||
<div v-if="tab === 'all'">
|
<div v-if="tab === 'all'">
|
||||||
<XNotifications :class="$style.notifications" :excludeTypes="excludeTypes"/>
|
<XNotifications :class="$style.notifications" :excludeTypes="excludeTypes"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,7 +15,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div v-else-if="tab === 'directNotes'">
|
<div v-else-if="tab === 'directNotes'">
|
||||||
<MkNotes :pagination="directNotesPagination"/>
|
<MkNotes :pagination="directNotesPagination"/>
|
||||||
</div>
|
</div>
|
||||||
</MkHorizontalSwipe>
|
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</PageWithHeader>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -145,13 +145,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { computed, onActivated, onDeactivated, onMounted, onUnmounted, ref, shallowRef, triggerRef, watch } from 'vue';
|
import { computed, onActivated, onDeactivated, onMounted, onUnmounted, ref, shallowRef, triggerRef, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import * as Reversi from 'misskey-reversi';
|
import * as Reversi from 'misskey-reversi';
|
||||||
|
import { useInterval } from '@@/js/use-interval.js';
|
||||||
|
import { url } from '@@/js/config.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import { deepClone } from '@/utility/clone.js';
|
import { deepClone } from '@/utility/clone.js';
|
||||||
import { useInterval } from '@@/js/use-interval.js';
|
|
||||||
import { ensureSignin } from '@/i.js';
|
import { ensureSignin } from '@/i.js';
|
||||||
import { url } from '@@/js/config.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { userPage } from '@/filters/user.js';
|
import { userPage } from '@/filters/user.js';
|
||||||
|
@ -424,7 +424,7 @@ function autoplay() {
|
||||||
const tick = () => {
|
const tick = () => {
|
||||||
const log = logs[i];
|
const log = logs[i];
|
||||||
const time = log.time - previousLog.time;
|
const time = log.time - previousLog.time;
|
||||||
setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
i++;
|
i++;
|
||||||
logPos.value++;
|
logPos.value++;
|
||||||
previousLog = log;
|
previousLog = log;
|
||||||
|
|
|
@ -42,22 +42,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</SearchMarker>
|
</SearchMarker>
|
||||||
|
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<SearchMarker :keywords="['blur']">
|
|
||||||
<MkPreferenceContainer k="useBlurEffect">
|
|
||||||
<MkSwitch v-model="useBlurEffect">
|
|
||||||
<template #label><SearchLabel>{{ i18n.ts.useBlurEffect }}</SearchLabel></template>
|
|
||||||
</MkSwitch>
|
|
||||||
</MkPreferenceContainer>
|
|
||||||
</SearchMarker>
|
|
||||||
|
|
||||||
<SearchMarker :keywords="['blur', 'modal']">
|
|
||||||
<MkPreferenceContainer k="useBlurEffectForModal">
|
|
||||||
<MkSwitch v-model="useBlurEffectForModal">
|
|
||||||
<template #label><SearchLabel>{{ i18n.ts.useBlurEffectForModal }}</SearchLabel></template>
|
|
||||||
</MkSwitch>
|
|
||||||
</MkPreferenceContainer>
|
|
||||||
</SearchMarker>
|
|
||||||
|
|
||||||
<SearchMarker :keywords="['avatar', 'icon', 'decoration', 'show']">
|
<SearchMarker :keywords="['avatar', 'icon', 'decoration', 'show']">
|
||||||
<MkPreferenceContainer k="showAvatarDecorations">
|
<MkPreferenceContainer k="showAvatarDecorations">
|
||||||
<MkSwitch v-model="showAvatarDecorations">
|
<MkSwitch v-model="showAvatarDecorations">
|
||||||
|
@ -395,40 +379,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
</SearchMarker>
|
</SearchMarker>
|
||||||
|
|
||||||
<SearchMarker :keywords="['datasaver']">
|
|
||||||
<MkFolder>
|
|
||||||
<template #label><SearchLabel>{{ i18n.ts.dataSaver }}</SearchLabel></template>
|
|
||||||
<template #icon><i class="ti ti-antenna-bars-3"></i></template>
|
|
||||||
|
|
||||||
<div class="_gaps_m">
|
|
||||||
<MkInfo>{{ i18n.ts.reloadRequiredToApplySettings }}</MkInfo>
|
|
||||||
|
|
||||||
<div class="_buttons">
|
|
||||||
<MkButton inline @click="enableAllDataSaver">{{ i18n.ts.enableAll }}</MkButton>
|
|
||||||
<MkButton inline @click="disableAllDataSaver">{{ i18n.ts.disableAll }}</MkButton>
|
|
||||||
</div>
|
|
||||||
<div class="_gaps_m">
|
|
||||||
<MkSwitch v-model="dataSaver.media">
|
|
||||||
{{ i18n.ts._dataSaver._media.title }}
|
|
||||||
<template #caption>{{ i18n.ts._dataSaver._media.description }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
<MkSwitch v-model="dataSaver.avatar">
|
|
||||||
{{ i18n.ts._dataSaver._avatar.title }}
|
|
||||||
<template #caption>{{ i18n.ts._dataSaver._avatar.description }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
<MkSwitch v-model="dataSaver.urlPreview">
|
|
||||||
{{ i18n.ts._dataSaver._urlPreview.title }}
|
|
||||||
<template #caption>{{ i18n.ts._dataSaver._urlPreview.description }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
<MkSwitch v-model="dataSaver.code">
|
|
||||||
{{ i18n.ts._dataSaver._code.title }}
|
|
||||||
<template #caption>{{ i18n.ts._dataSaver._code.description }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
</SearchMarker>
|
|
||||||
|
|
||||||
<SearchMarker :keywords="['chat', 'messaging']">
|
<SearchMarker :keywords="['chat', 'messaging']">
|
||||||
<MkFolder>
|
<MkFolder>
|
||||||
<template #label><SearchLabel>{{ i18n.ts.chat }}</SearchLabel></template>
|
<template #label><SearchLabel>{{ i18n.ts.chat }}</SearchLabel></template>
|
||||||
|
@ -468,6 +418,76 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
</SearchMarker>
|
</SearchMarker>
|
||||||
|
|
||||||
|
<SearchMarker :keywords="['performance']">
|
||||||
|
<MkFolder>
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.performance }}</SearchLabel></template>
|
||||||
|
<template #icon><i class="ti ti-battery-vertical-eco"></i></template>
|
||||||
|
|
||||||
|
<div class="_gaps_s">
|
||||||
|
<SearchMarker :keywords="['blur']">
|
||||||
|
<MkPreferenceContainer k="useBlurEffect">
|
||||||
|
<MkSwitch v-model="useBlurEffect">
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.useBlurEffect }}</SearchLabel></template>
|
||||||
|
<template #caption><SearchLabel>{{ i18n.ts.turnOffToImprovePerformance }}</SearchLabel></template>
|
||||||
|
</MkSwitch>
|
||||||
|
</MkPreferenceContainer>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
|
<SearchMarker :keywords="['blur', 'modal']">
|
||||||
|
<MkPreferenceContainer k="useBlurEffectForModal">
|
||||||
|
<MkSwitch v-model="useBlurEffectForModal">
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.useBlurEffectForModal }}</SearchLabel></template>
|
||||||
|
<template #caption><SearchLabel>{{ i18n.ts.turnOffToImprovePerformance }}</SearchLabel></template>
|
||||||
|
</MkSwitch>
|
||||||
|
</MkPreferenceContainer>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
|
<SearchMarker :keywords="['sticky']">
|
||||||
|
<MkPreferenceContainer k="useStickyIcons">
|
||||||
|
<MkSwitch v-model="useStickyIcons">
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts._settings.useStickyIcons }}</SearchLabel></template>
|
||||||
|
<template #caption><SearchLabel>{{ i18n.ts.turnOffToImprovePerformance }}</SearchLabel></template>
|
||||||
|
</MkSwitch>
|
||||||
|
</MkPreferenceContainer>
|
||||||
|
</SearchMarker>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
|
<SearchMarker :keywords="['datasaver']">
|
||||||
|
<MkFolder>
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts.dataSaver }}</SearchLabel></template>
|
||||||
|
<template #icon><i class="ti ti-antenna-bars-3"></i></template>
|
||||||
|
|
||||||
|
<div class="_gaps_m">
|
||||||
|
<MkInfo>{{ i18n.ts.reloadRequiredToApplySettings }}</MkInfo>
|
||||||
|
|
||||||
|
<div class="_buttons">
|
||||||
|
<MkButton inline @click="enableAllDataSaver">{{ i18n.ts.enableAll }}</MkButton>
|
||||||
|
<MkButton inline @click="disableAllDataSaver">{{ i18n.ts.disableAll }}</MkButton>
|
||||||
|
</div>
|
||||||
|
<div class="_gaps_m">
|
||||||
|
<MkSwitch v-model="dataSaver.media">
|
||||||
|
{{ i18n.ts._dataSaver._media.title }}
|
||||||
|
<template #caption>{{ i18n.ts._dataSaver._media.description }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<MkSwitch v-model="dataSaver.avatar">
|
||||||
|
{{ i18n.ts._dataSaver._avatar.title }}
|
||||||
|
<template #caption>{{ i18n.ts._dataSaver._avatar.description }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<MkSwitch v-model="dataSaver.urlPreview">
|
||||||
|
{{ i18n.ts._dataSaver._urlPreview.title }}
|
||||||
|
<template #caption>{{ i18n.ts._dataSaver._urlPreview.description }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<MkSwitch v-model="dataSaver.code">
|
||||||
|
{{ i18n.ts._dataSaver._code.title }}
|
||||||
|
<template #caption>{{ i18n.ts._dataSaver._code.description }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
</SearchMarker>
|
||||||
|
|
||||||
<SearchMarker :keywords="['other']">
|
<SearchMarker :keywords="['other']">
|
||||||
<MkFolder>
|
<MkFolder>
|
||||||
<template #label><SearchLabel>{{ i18n.ts.other }}</SearchLabel></template>
|
<template #label><SearchLabel>{{ i18n.ts.other }}</SearchLabel></template>
|
||||||
|
@ -650,6 +670,7 @@ const useBlurEffect = prefer.model('useBlurEffect');
|
||||||
const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies');
|
const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies');
|
||||||
const chatShowSenderName = prefer.model('chat.showSenderName');
|
const chatShowSenderName = prefer.model('chat.showSenderName');
|
||||||
const chatSendOnEnter = prefer.model('chat.sendOnEnter');
|
const chatSendOnEnter = prefer.model('chat.sendOnEnter');
|
||||||
|
const useStickyIcons = prefer.model('useStickyIcons');
|
||||||
|
|
||||||
watch(lang, () => {
|
watch(lang, () => {
|
||||||
miLocalStorage.setItem('lang', lang.value as string);
|
miLocalStorage.setItem('lang', lang.value as string);
|
||||||
|
@ -678,6 +699,7 @@ watch([
|
||||||
highlightSensitiveMedia,
|
highlightSensitiveMedia,
|
||||||
enableSeasonalScreenEffect,
|
enableSeasonalScreenEffect,
|
||||||
chatShowSenderName,
|
chatShowSenderName,
|
||||||
|
useStickyIcons,
|
||||||
], async () => {
|
], async () => {
|
||||||
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
|
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
|
||||||
});
|
});
|
||||||
|
@ -786,7 +808,7 @@ function testNotification(): void {
|
||||||
smashCount = 0;
|
smashCount = 0;
|
||||||
}
|
}
|
||||||
if (smashTimer) {
|
if (smashTimer) {
|
||||||
clearTimeout(smashTimer);
|
window.clearTimeout(smashTimer);
|
||||||
}
|
}
|
||||||
smashTimer = window.setTimeout(() => {
|
smashTimer = window.setTimeout(() => {
|
||||||
smashCount = 0;
|
smashCount = 0;
|
||||||
|
|
|
@ -4,19 +4,19 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PageWithHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true">
|
<div ref="rootEl" class="_pageScrollable">
|
||||||
|
<MkStickyContainer>
|
||||||
|
<template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin"/></template>
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<MkHorizontalSwipe v-model:tab="src" :tabs="$i ? headerTabs : headerTabsWhenNotLogin">
|
|
||||||
<div ref="rootEl">
|
|
||||||
<MkInfo v-if="isBasicTimeline(src) && !store.r.timelineTutorials.value[src]" style="margin-bottom: var(--MI-margin);" closable @close="closeTutorial()">
|
<MkInfo v-if="isBasicTimeline(src) && !store.r.timelineTutorials.value[src]" style="margin-bottom: var(--MI-margin);" closable @close="closeTutorial()">
|
||||||
{{ i18n.ts._timelineDescription[src] }}
|
{{ i18n.ts._timelineDescription[src] }}
|
||||||
</MkInfo>
|
</MkInfo>
|
||||||
<MkPostForm v-if="prefer.r.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--MI-margin);"/>
|
<MkPostForm v-if="prefer.r.showFixedPostForm.value" :class="$style.postForm" class="_panel" fixed style="margin-bottom: var(--MI-margin);"/>
|
||||||
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
|
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
|
||||||
<div :class="$style.tl">
|
|
||||||
<MkTimeline
|
<MkTimeline
|
||||||
ref="tlComponent"
|
ref="tlComponent"
|
||||||
:key="src + withRenotes + withReplies + onlyFiles + withSensitive"
|
:key="src + withRenotes + withReplies + onlyFiles + withSensitive"
|
||||||
|
:class="$style.tl"
|
||||||
:src="src.split(':')[0]"
|
:src="src.split(':')[0]"
|
||||||
:list="src.split(':')[1]"
|
:list="src.split(':')[1]"
|
||||||
:withRenotes="withRenotes"
|
:withRenotes="withRenotes"
|
||||||
|
@ -26,25 +26,21 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:sound="true"
|
:sound="true"
|
||||||
@queue="queueUpdated"
|
@queue="queueUpdated"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</MkHorizontalSwipe>
|
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</PageWithHeader>
|
</MkStickyContainer>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch, provide, useTemplateRef, ref, onMounted, onActivated } from 'vue';
|
import { computed, watch, provide, useTemplateRef, ref, onMounted, onActivated } from 'vue';
|
||||||
import { scroll } from '@@/js/scroll.js';
|
import { scrollInContainer } from '@@/js/scroll.js';
|
||||||
import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
|
import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import type { BasicTimelineType } from '@/timelines.js';
|
import type { BasicTimelineType } from '@/timelines.js';
|
||||||
import MkTimeline from '@/components/MkTimeline.vue';
|
import MkTimeline from '@/components/MkTimeline.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import MkPostForm from '@/components/MkPostForm.vue';
|
import MkPostForm from '@/components/MkPostForm.vue';
|
||||||
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
|
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
|
||||||
import { store } from '@/store.js';
|
import { store } from '@/store.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { $i } from '@/i.js';
|
import { $i } from '@/i.js';
|
||||||
|
@ -133,7 +129,7 @@ function queueUpdated(q: number): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
function top(): void {
|
function top(): void {
|
||||||
if (rootEl.value) scroll(rootEl.value, { top: 0, behavior: 'smooth' });
|
if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'instant' });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function chooseList(ev: MouseEvent): Promise<void> {
|
async function chooseList(ev: MouseEvent): Promise<void> {
|
||||||
|
|
|
@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch, ref, useTemplateRef } from 'vue';
|
import { computed, watch, ref, useTemplateRef } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { scroll } from '@@/js/scroll.js';
|
import { scrollInContainer } from '@@/js/scroll.js';
|
||||||
import MkTimeline from '@/components/MkTimeline.vue';
|
import MkTimeline from '@/components/MkTimeline.vue';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
|
@ -54,7 +54,7 @@ function queueUpdated(q) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function top() {
|
function top() {
|
||||||
scroll(rootEl.value, { top: 0 });
|
scrollInContainer(rootEl.value, { top: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function settings() {
|
function settings() {
|
||||||
|
|
|
@ -198,6 +198,9 @@ export const PREF_DEF = {
|
||||||
useBlurEffect: {
|
useBlurEffect: {
|
||||||
default: DEFAULT_DEVICE_KIND === 'desktop',
|
default: DEFAULT_DEVICE_KIND === 'desktop',
|
||||||
},
|
},
|
||||||
|
useStickyIcons: {
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
showFixedPostForm: {
|
showFixedPostForm: {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
|
|
@ -178,6 +178,14 @@ rt {
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
overscroll-behavior: contain;
|
overscroll-behavior: contain;
|
||||||
|
|
||||||
|
/*
|
||||||
|
理屈は知らないけど、ここでbackgroundを設定しておかないと
|
||||||
|
スクロールコンテナーが少なくともChromeにおいて
|
||||||
|
main thread scrolling になってしまい、パフォーマンスが(多分)落ちる。
|
||||||
|
backgroundが透明だと裏側を描画しないといけなくなるとかそういう理由かもしれない
|
||||||
|
*/
|
||||||
|
background: var(--MI_THEME-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
._pageScrollableReversed {
|
._pageScrollableReversed {
|
||||||
|
|
|
@ -226,13 +226,17 @@ html,
|
||||||
body {
|
body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: clip;
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
overscroll-behavior: none;
|
overscroll-behavior: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
/* NOTE: htmlにも overflow: clip を設定したいところだが、設定すると何故か少なくともChromeで html が main thread scrolling になりパフォーマンスが(多分)落ちる */
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
#misskey_app {
|
#misskey_app {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -377,7 +377,7 @@ function onDrop(ev) {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
color: var(--MI_THEME-panelHeaderFg);
|
color: var(--MI_THEME-panelHeaderFg);
|
||||||
background: var(--MI_THEME-panelHeaderBg);
|
background: var(--MI_THEME-panelHeaderBg);
|
||||||
box-shadow: 0 1px 0 0 var(--MI_THEME-panelHeaderDivider);
|
box-shadow: 0 0.5px 0 0 var(--MI_THEME-panelHeaderDivider);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,119 +5,30 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
<XSidebar v-if="!isMobile" :class="$style.sidebar" :showWidgetButton="!isDesktop" @widgetButtonClick="widgetsShowing = true"/>
|
|
||||||
|
|
||||||
<div :class="$style.contents" @contextmenu.stop="onContextmenu">
|
<div :class="$style.contents" @contextmenu.stop="onContextmenu">
|
||||||
<div>
|
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :class="$style.content"/>
|
||||||
<XPreferenceRestore v-if="shouldSuggestRestoreBackup"/>
|
<RouterView v-else :class="$style.content"/>
|
||||||
<XAnnouncements v-if="$i"/>
|
|
||||||
<XStatusBars :class="$style.statusbars"/>
|
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.content">
|
|
||||||
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']"/>
|
|
||||||
<RouterView v-else/>
|
|
||||||
</div>
|
|
||||||
<div v-if="isMobile" ref="navFooter" :class="$style.nav">
|
|
||||||
<button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator" class="_blink"><i class="_indicatorCircle"></i></span></button>
|
|
||||||
<button :class="$style.navButton" class="_button" @click="mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button>
|
|
||||||
<button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')">
|
|
||||||
<i :class="$style.navButtonIcon" class="ti ti-bell"></i>
|
|
||||||
<span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator" class="_blink">
|
|
||||||
<span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
<button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" class="ti ti-apps"></i></button>
|
|
||||||
<button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ti ti-pencil"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="isDesktop && !pageMetadata?.needWideArea" :class="$style.widgets">
|
|
||||||
<XWidgets/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Transition
|
|
||||||
:enterActiveClass="prefer.s.animation ? $style.transition_menuDrawerBg_enterActive : ''"
|
|
||||||
:leaveActiveClass="prefer.s.animation ? $style.transition_menuDrawerBg_leaveActive : ''"
|
|
||||||
:enterFromClass="prefer.s.animation ? $style.transition_menuDrawerBg_enterFrom : ''"
|
|
||||||
:leaveToClass="prefer.s.animation ? $style.transition_menuDrawerBg_leaveTo : ''"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="drawerMenuShowing"
|
|
||||||
:class="$style.menuDrawerBg"
|
|
||||||
class="_modalBg"
|
|
||||||
@click="drawerMenuShowing = false"
|
|
||||||
@touchstart.passive="drawerMenuShowing = false"
|
|
||||||
></div>
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<Transition
|
|
||||||
:enterActiveClass="prefer.s.animation ? $style.transition_menuDrawer_enterActive : ''"
|
|
||||||
:leaveActiveClass="prefer.s.animation ? $style.transition_menuDrawer_leaveActive : ''"
|
|
||||||
:enterFromClass="prefer.s.animation ? $style.transition_menuDrawer_enterFrom : ''"
|
|
||||||
:leaveToClass="prefer.s.animation ? $style.transition_menuDrawer_leaveTo : ''"
|
|
||||||
>
|
|
||||||
<div v-if="drawerMenuShowing" :class="$style.menuDrawer">
|
|
||||||
<XDrawerMenu/>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<Transition
|
|
||||||
:enterActiveClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_enterActive : ''"
|
|
||||||
:leaveActiveClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_leaveActive : ''"
|
|
||||||
:enterFromClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_enterFrom : ''"
|
|
||||||
:leaveToClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_leaveTo : ''"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="widgetsShowing"
|
|
||||||
:class="$style.widgetsDrawerBg"
|
|
||||||
class="_modalBg"
|
|
||||||
@click="widgetsShowing = false"
|
|
||||||
@touchstart.passive="widgetsShowing = false"
|
|
||||||
></div>
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<Transition
|
|
||||||
:enterActiveClass="prefer.s.animation ? $style.transition_widgetsDrawer_enterActive : ''"
|
|
||||||
:leaveActiveClass="prefer.s.animation ? $style.transition_widgetsDrawer_leaveActive : ''"
|
|
||||||
:enterFromClass="prefer.s.animation ? $style.transition_widgetsDrawer_enterFrom : ''"
|
|
||||||
:leaveToClass="prefer.s.animation ? $style.transition_widgetsDrawer_leaveTo : ''"
|
|
||||||
>
|
|
||||||
<div v-if="widgetsShowing" :class="$style.widgetsDrawer">
|
|
||||||
<button class="_button" :class="$style.widgetsCloseButton" @click="widgetsShowing = false"><i class="ti ti-x"></i></button>
|
|
||||||
<XWidgets/>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<XCommon/>
|
<XCommon/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, provide, onMounted, computed, ref, watch, useTemplateRef } from 'vue';
|
import { provide, onMounted, computed, ref } from 'vue';
|
||||||
import { instanceName } from '@@/js/config.js';
|
import { instanceName } from '@@/js/config.js';
|
||||||
import { isLink } from '@@/js/is-link.js';
|
import { isLink } from '@@/js/is-link.js';
|
||||||
import XCommon from './_common_/common.vue';
|
import XCommon from './_common_/common.vue';
|
||||||
import type { Ref } from 'vue';
|
|
||||||
import type { PageMetadata } from '@/page.js';
|
import type { PageMetadata } from '@/page.js';
|
||||||
import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
|
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { navbarItemDef } from '@/navbar.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { $i } from '@/i.js';
|
|
||||||
import { provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
|
import { provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
|
||||||
import { deviceKind } from '@/utility/device-kind.js';
|
import { deviceKind } from '@/utility/device-kind.js';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { mainRouter } from '@/router.js';
|
import { mainRouter } from '@/router.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
import { shouldSuggestRestoreBackup } from '@/preferences/utility.js';
|
|
||||||
import { DI } from '@/di.js';
|
import { DI } from '@/di.js';
|
||||||
|
|
||||||
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
|
|
||||||
const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue'));
|
|
||||||
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
|
|
||||||
const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
|
|
||||||
const XPreferenceRestore = defineAsyncComponent(() => import('@/ui/_common_/PreferenceRestore.vue'));
|
|
||||||
|
|
||||||
const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
|
const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
|
||||||
|
|
||||||
const DESKTOP_THRESHOLD = 1100;
|
const DESKTOP_THRESHOLD = 1100;
|
||||||
|
@ -131,8 +42,6 @@ window.addEventListener('resize', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const pageMetadata = ref<null | PageMetadata>(null);
|
const pageMetadata = ref<null | PageMetadata>(null);
|
||||||
const widgetsShowing = ref(false);
|
|
||||||
const navFooter = useTemplateRef('navFooter');
|
|
||||||
|
|
||||||
provide(DI.router, mainRouter);
|
provide(DI.router, mainRouter);
|
||||||
provideMetadataReceiver((metadataGetter) => {
|
provideMetadataReceiver((metadataGetter) => {
|
||||||
|
@ -148,14 +57,6 @@ provideMetadataReceiver((metadataGetter) => {
|
||||||
});
|
});
|
||||||
provideReactiveMetadata(pageMetadata);
|
provideReactiveMetadata(pageMetadata);
|
||||||
|
|
||||||
const menuIndicated = computed(() => {
|
|
||||||
for (const def in navbarItemDef) {
|
|
||||||
if (def === 'notifications') continue; // 通知は下にボタンとして表示されてるから
|
|
||||||
if (navbarItemDef[def].indicated) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
const drawerMenuShowing = ref(false);
|
const drawerMenuShowing = ref(false);
|
||||||
|
|
||||||
mainRouter.on('change', () => {
|
mainRouter.on('change', () => {
|
||||||
|
@ -195,20 +96,6 @@ const onContextmenu = (ev) => {
|
||||||
},
|
},
|
||||||
}], ev);
|
}], ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
const navFooterHeight = ref(0);
|
|
||||||
|
|
||||||
watch(navFooter, () => {
|
|
||||||
if (navFooter.value) {
|
|
||||||
navFooterHeight.value = navFooter.value.offsetHeight;
|
|
||||||
window.document.body.style.setProperty('--MI-minBottomSpacing', 'var(--MI-minBottomSpacingMobile)');
|
|
||||||
} else {
|
|
||||||
navFooterHeight.value = 0;
|
|
||||||
window.document.body.style.setProperty('--MI-minBottomSpacing', '0px');
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
immediate: true,
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -216,13 +103,14 @@ html,
|
||||||
body {
|
body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: clip;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
overscroll-behavior: none;
|
overscroll-behavior: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
/* NOTE: htmlにも overflow: clip を設定したいところだが、設定すると何故か少なくともChromeで html が main thread scrolling になりパフォーマンスが(多分)落ちる */
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
#misskey_app {
|
#misskey_app {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -497,7 +497,7 @@ export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) {
|
||||||
if (claimedAchievements.includes(type)) return;
|
if (claimedAchievements.includes(type)) return;
|
||||||
claimingQueue.add(type);
|
claimingQueue.add(type);
|
||||||
claimedAchievements.push(type);
|
claimedAchievements.push(type);
|
||||||
await new Promise(resolve => setTimeout(resolve, (claimingQueue.size - 1) * 500));
|
await new Promise(resolve => window.setTimeout(resolve, (claimingQueue.size - 1) * 500));
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
claimingQueue.delete(type);
|
claimingQueue.delete(type);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
|
@ -290,51 +290,41 @@ export const searchIndexes: SearchIndexItem[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'lfI3yMX9g',
|
id: 'lfI3yMX9g',
|
||||||
label: i18n.ts.useBlurEffect,
|
|
||||||
keywords: ['blur'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '31Y4IcGEf',
|
|
||||||
label: i18n.ts.useBlurEffectForModal,
|
|
||||||
keywords: ['blur', 'modal'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '78q2asrLS',
|
|
||||||
label: i18n.ts.showAvatarDecorations,
|
label: i18n.ts.showAvatarDecorations,
|
||||||
keywords: ['avatar', 'icon', 'decoration', 'show'],
|
keywords: ['avatar', 'icon', 'decoration', 'show'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'zydOfGYip',
|
id: '31Y4IcGEf',
|
||||||
label: i18n.ts.alwaysConfirmFollow,
|
label: i18n.ts.alwaysConfirmFollow,
|
||||||
keywords: ['follow', 'confirm', 'always'],
|
keywords: ['follow', 'confirm', 'always'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'wqpOC22Zm',
|
id: '78q2asrLS',
|
||||||
label: i18n.ts.highlightSensitiveMedia,
|
label: i18n.ts.highlightSensitiveMedia,
|
||||||
keywords: ['highlight', 'sensitive', 'nsfw', 'image', 'photo', 'picture', 'media', 'thumbnail'],
|
keywords: ['highlight', 'sensitive', 'nsfw', 'image', 'photo', 'picture', 'media', 'thumbnail'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'c98gbF9c6',
|
id: 'zydOfGYip',
|
||||||
label: i18n.ts.confirmWhenRevealingSensitiveMedia,
|
label: i18n.ts.confirmWhenRevealingSensitiveMedia,
|
||||||
keywords: ['sensitive', 'nsfw', 'media', 'image', 'photo', 'picture', 'attachment', 'confirm'],
|
keywords: ['sensitive', 'nsfw', 'media', 'image', 'photo', 'picture', 'attachment', 'confirm'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4LxdiOMNh',
|
id: 'wqpOC22Zm',
|
||||||
label: i18n.ts.enableAdvancedMfm,
|
label: i18n.ts.enableAdvancedMfm,
|
||||||
keywords: ['mfm', 'enable', 'show', 'advanced'],
|
keywords: ['mfm', 'enable', 'show', 'advanced'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '9gTCaLkIf',
|
id: 'c98gbF9c6',
|
||||||
label: i18n.ts.enableInfiniteScroll,
|
label: i18n.ts.enableInfiniteScroll,
|
||||||
keywords: ['auto', 'load', 'auto', 'more', 'scroll'],
|
keywords: ['auto', 'load', 'auto', 'more', 'scroll'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'jmJT0twuJ',
|
id: '6ANRSOaNg',
|
||||||
label: i18n.ts.emojiStyle,
|
label: i18n.ts.emojiStyle,
|
||||||
keywords: ['emoji', 'style', 'native', 'system', 'fluent', 'twemoji'],
|
keywords: ['emoji', 'style', 'native', 'system', 'fluent', 'twemoji'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'igFN7RIUa',
|
id: 'wo0s0CaI1',
|
||||||
label: i18n.ts.pinnedList,
|
label: i18n.ts.pinnedList,
|
||||||
keywords: ['pinned', 'list'],
|
keywords: ['pinned', 'list'],
|
||||||
},
|
},
|
||||||
|
@ -343,85 +333,85 @@ export const searchIndexes: SearchIndexItem[] = [
|
||||||
keywords: ['general'],
|
keywords: ['general'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ufc2X9voy',
|
id: 'l78F2W9Ok',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 'd2H4E5ys6',
|
id: 'iOJ3Crlky',
|
||||||
label: i18n.ts.showFixedPostForm,
|
label: i18n.ts.showFixedPostForm,
|
||||||
keywords: ['post', 'form', 'timeline'],
|
keywords: ['post', 'form', 'timeline'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '1LHOhDKGW',
|
id: 'CQldliCSi',
|
||||||
label: i18n.ts.showFixedPostFormInChannel,
|
label: i18n.ts.showFixedPostFormInChannel,
|
||||||
keywords: ['post', 'form', 'timeline', 'channel'],
|
keywords: ['post', 'form', 'timeline', 'channel'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'DSzwvTp7i',
|
id: 'd2H4E5ys6',
|
||||||
label: i18n.ts.collapseRenotes,
|
label: i18n.ts.collapseRenotes,
|
||||||
keywords: ['renote', i18n.ts.collapseRenotesDescription],
|
keywords: ['renote', i18n.ts.collapseRenotesDescription],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'jb3HUeyrx',
|
id: 'yb11lSY1G',
|
||||||
label: i18n.ts.showGapBetweenNotesInTimeline,
|
label: i18n.ts.showGapBetweenNotesInTimeline,
|
||||||
keywords: ['note', 'timeline', 'gap'],
|
keywords: ['note', 'timeline', 'gap'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2LNjwv1cr',
|
id: 'fL49Zxe9i',
|
||||||
label: i18n.ts.disableStreamingTimeline,
|
label: i18n.ts.disableStreamingTimeline,
|
||||||
keywords: ['disable', 'streaming', 'timeline'],
|
keywords: ['disable', 'streaming', 'timeline'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '7W6g8Dcqz',
|
id: 'ykifk3NHS',
|
||||||
label: i18n.ts.showNoteActionsOnlyHover,
|
label: i18n.ts.showNoteActionsOnlyHover,
|
||||||
keywords: ['hover', 'show', 'footer', 'action'],
|
keywords: ['hover', 'show', 'footer', 'action'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'uAOoH3LFF',
|
id: 'tLGyaQagB',
|
||||||
label: i18n.ts.showClipButtonInNoteFooter,
|
label: i18n.ts.showClipButtonInNoteFooter,
|
||||||
keywords: ['footer', 'action', 'clip', 'show'],
|
keywords: ['footer', 'action', 'clip', 'show'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'eCiyZLC8n',
|
id: '7W6g8Dcqz',
|
||||||
label: i18n.ts.showReactionsCount,
|
label: i18n.ts.showReactionsCount,
|
||||||
keywords: ['reaction', 'count', 'show'],
|
keywords: ['reaction', 'count', 'show'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '68u9uRmFP',
|
id: 'uAOoH3LFF',
|
||||||
label: i18n.ts.confirmOnReact,
|
label: i18n.ts.confirmOnReact,
|
||||||
keywords: ['reaction', 'confirm'],
|
keywords: ['reaction', 'confirm'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'rHWm4sXIe',
|
id: 'eCiyZLC8n',
|
||||||
label: i18n.ts.loadRawImages,
|
label: i18n.ts.loadRawImages,
|
||||||
keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'quality', 'raw', 'attachment'],
|
keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'quality', 'raw', 'attachment'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '9L2XGJw7e',
|
id: '68u9uRmFP',
|
||||||
label: i18n.ts.useReactionPickerForContextMenu,
|
label: i18n.ts.useReactionPickerForContextMenu,
|
||||||
keywords: ['reaction', 'picker', 'contextmenu', 'open'],
|
keywords: ['reaction', 'picker', 'contextmenu', 'open'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'uIMCIK7kG',
|
id: 'yxehrHZ6x',
|
||||||
label: i18n.ts.reactionsDisplaySize,
|
label: i18n.ts.reactionsDisplaySize,
|
||||||
keywords: ['reaction', 'size', 'scale', 'display'],
|
keywords: ['reaction', 'size', 'scale', 'display'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'uMckjO9bz',
|
id: 'gi8ILaE2Z',
|
||||||
label: i18n.ts.limitWidthOfReaction,
|
label: i18n.ts.limitWidthOfReaction,
|
||||||
keywords: ['reaction', 'size', 'scale', 'display', 'width', 'limit'],
|
keywords: ['reaction', 'size', 'scale', 'display', 'width', 'limit'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'yeghU4qiH',
|
id: 'cEQJZ7DQG',
|
||||||
label: i18n.ts.mediaListWithOneImageAppearance,
|
label: i18n.ts.mediaListWithOneImageAppearance,
|
||||||
keywords: ['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'list', 'size', 'height'],
|
keywords: ['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'list', 'size', 'height'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'yYSOPoAKE',
|
id: 'haX4QVulD',
|
||||||
label: i18n.ts.instanceTicker,
|
label: i18n.ts.instanceTicker,
|
||||||
keywords: ['ticker', 'information', 'label', 'instance', 'server', 'host', 'federation'],
|
keywords: ['ticker', 'information', 'label', 'instance', 'server', 'host', 'federation'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'iOHiIu32L',
|
id: 'pneYnQekL',
|
||||||
label: i18n.ts.displayOfSensitiveMedia,
|
label: i18n.ts.displayOfSensitiveMedia,
|
||||||
keywords: ['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'nsfw', 'sensitive', 'display', 'show', 'hide', 'visibility'],
|
keywords: ['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'nsfw', 'sensitive', 'display', 'show', 'hide', 'visibility'],
|
||||||
},
|
},
|
||||||
|
@ -430,25 +420,25 @@ export const searchIndexes: SearchIndexItem[] = [
|
||||||
keywords: ['timeline', 'note'],
|
keywords: ['timeline', 'note'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'eROFRMtXv',
|
id: 'eJ2jme16W',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 'BaQfrVO82',
|
id: 'ErMQr6LQk',
|
||||||
label: i18n.ts.keepCw,
|
label: i18n.ts.keepCw,
|
||||||
keywords: ['remember', 'keep', 'note', 'cw'],
|
keywords: ['remember', 'keep', 'note', 'cw'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'vFerPo2he',
|
id: 'zrJicawH9',
|
||||||
label: i18n.ts.rememberNoteVisibility,
|
label: i18n.ts.rememberNoteVisibility,
|
||||||
keywords: ['remember', 'keep', 'note', 'visibility'],
|
keywords: ['remember', 'keep', 'note', 'visibility'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'dcAC0yJcH',
|
id: 'BaQfrVO82',
|
||||||
label: i18n.ts.enableQuickAddMfmFunction,
|
label: i18n.ts.enableQuickAddMfmFunction,
|
||||||
keywords: ['mfm', 'enable', 'show', 'advanced', 'picker', 'form', 'function', 'fn'],
|
keywords: ['mfm', 'enable', 'show', 'advanced', 'picker', 'form', 'function', 'fn'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'bECeWZVMb',
|
id: 'C2WYcVM1d',
|
||||||
label: i18n.ts.defaultNoteVisibility,
|
label: i18n.ts.defaultNoteVisibility,
|
||||||
keywords: ['default', 'note', 'visibility'],
|
keywords: ['default', 'note', 'visibility'],
|
||||||
},
|
},
|
||||||
|
@ -457,20 +447,20 @@ export const searchIndexes: SearchIndexItem[] = [
|
||||||
keywords: ['post', 'form'],
|
keywords: ['post', 'form'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'tsSP93Cc6',
|
id: 'sQXSA6gik',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 'dtw8FepYL',
|
id: 'rICn8stqk',
|
||||||
label: i18n.ts.useGroupedNotifications,
|
label: i18n.ts.useGroupedNotifications,
|
||||||
keywords: ['group'],
|
keywords: ['group'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'eb0yCYJTn',
|
id: 'xFmAg2tDe',
|
||||||
label: i18n.ts.position,
|
label: i18n.ts.position,
|
||||||
keywords: ['position'],
|
keywords: ['position'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '1Spt4Gpr5',
|
id: 'Ek4Cw3VPq',
|
||||||
label: i18n.ts.stackAxis,
|
label: i18n.ts.stackAxis,
|
||||||
keywords: ['stack', 'axis', 'direction'],
|
keywords: ['stack', 'axis', 'direction'],
|
||||||
},
|
},
|
||||||
|
@ -479,20 +469,15 @@ export const searchIndexes: SearchIndexItem[] = [
|
||||||
keywords: ['notification'],
|
keywords: ['notification'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'SYmWxGOF',
|
id: 'gDVCqZfxm',
|
||||||
label: i18n.ts.dataSaver,
|
|
||||||
keywords: ['datasaver'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'vPQPvmntL',
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 'zZxyXHk3A',
|
id: 'ei8Ix3s4S',
|
||||||
label: i18n.ts._settings._chat.showSenderName,
|
label: i18n.ts._settings._chat.showSenderName,
|
||||||
keywords: ['show', 'sender', 'name'],
|
keywords: ['show', 'sender', 'name'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'omEy5Q3Ev',
|
id: '2E7vdIUQd',
|
||||||
label: i18n.ts._settings._chat.sendOnEnter,
|
label: i18n.ts._settings._chat.sendOnEnter,
|
||||||
keywords: ['send', 'enter', 'newline'],
|
keywords: ['send', 'enter', 'newline'],
|
||||||
},
|
},
|
||||||
|
@ -501,50 +486,77 @@ export const searchIndexes: SearchIndexItem[] = [
|
||||||
keywords: ['chat', 'messaging'],
|
keywords: ['chat', 'messaging'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5fy7VEy6i',
|
id: '96LnS1sxB',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 'EosiWZvak',
|
id: '5h8vhCX1S',
|
||||||
|
label: i18n.ts.turnOffToImprovePerformance,
|
||||||
|
keywords: ['blur'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Cbjosj3TG',
|
||||||
|
label: i18n.ts.turnOffToImprovePerformance,
|
||||||
|
keywords: ['blur', 'modal'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'BKndoHcCj',
|
||||||
|
label: i18n.ts.turnOffToImprovePerformance,
|
||||||
|
keywords: ['sticky'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: i18n.ts.performance,
|
||||||
|
keywords: ['performance'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4yCgcFElF',
|
||||||
|
label: i18n.ts.dataSaver,
|
||||||
|
keywords: ['datasaver'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'DILm2LlCn',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'Fd0rFTSry',
|
||||||
label: i18n.ts.squareAvatars,
|
label: i18n.ts.squareAvatars,
|
||||||
keywords: ['avatar', 'icon', 'square'],
|
keywords: ['avatar', 'icon', 'square'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'qY5xTzl35',
|
id: 'xNsLokqeA',
|
||||||
label: i18n.ts.seasonalScreenEffect,
|
label: i18n.ts.seasonalScreenEffect,
|
||||||
keywords: ['effect', 'show'],
|
keywords: ['effect', 'show'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2VSnj81vC',
|
id: 'sZcalFBE8',
|
||||||
label: i18n.ts.openImageInNewTab,
|
label: i18n.ts.openImageInNewTab,
|
||||||
keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'],
|
keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'hdQa7W2H1',
|
id: 'Eh7vTluDO',
|
||||||
label: i18n.ts.withRepliesByDefaultForNewlyFollowed,
|
label: i18n.ts.withRepliesByDefaultForNewlyFollowed,
|
||||||
keywords: ['follow', 'replies'],
|
keywords: ['follow', 'replies'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'nnj4DkjhP',
|
id: 'vTRSKf1JA',
|
||||||
label: i18n.ts.whenServerDisconnected,
|
label: i18n.ts.whenServerDisconnected,
|
||||||
keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'],
|
keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'Eh7vTluDO',
|
id: 'zlO5cBZFH',
|
||||||
label: i18n.ts.numberOfPageCache,
|
label: i18n.ts.numberOfPageCache,
|
||||||
keywords: ['cache', 'page'],
|
keywords: ['cache', 'page'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'vTRSKf1JA',
|
id: 'huQ8nc4iD',
|
||||||
label: i18n.ts.forceShowAds,
|
label: i18n.ts.forceShowAds,
|
||||||
keywords: ['ad', 'show'],
|
keywords: ['ad', 'show'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'dwhQfcLGt',
|
id: 'nJWfqwQ4R',
|
||||||
label: i18n.ts.hemisphere,
|
label: i18n.ts.hemisphere,
|
||||||
keywords: [],
|
keywords: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'Ar1lj7f7U',
|
id: 'kwEEgTlwR',
|
||||||
label: i18n.ts.additionalEmojiDictionary,
|
label: i18n.ts.additionalEmojiDictionary,
|
||||||
keywords: ['emoji', 'dictionary', 'additional', 'extra'],
|
keywords: ['emoji', 'dictionary', 'additional', 'extra'],
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,11 +15,11 @@ export function confetti(options: { duration?: number; } = {}) {
|
||||||
return Math.random() * (max - min) + min;
|
return Math.random() * (max - min) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = window.setInterval(() => {
|
||||||
const timeLeft = animationEnd - Date.now();
|
const timeLeft = animationEnd - Date.now();
|
||||||
|
|
||||||
if (timeLeft <= 0) {
|
if (timeLeft <= 0) {
|
||||||
return clearInterval(interval);
|
return window.clearInterval(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
const particleCount = 50 * (timeLeft / duration);
|
const particleCount = 50 * (timeLeft / duration);
|
||||||
|
|
|
@ -362,12 +362,18 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
|
||||||
const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${user.host}`;
|
const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${user.host}`;
|
||||||
os.post({ specified: user, initialText: `${canonical} ` });
|
os.post({ specified: user, initialText: `${canonical} ` });
|
||||||
},
|
},
|
||||||
}, {
|
});
|
||||||
|
|
||||||
|
if ($i.policies.canChat && user.canChat && user.host == null) {
|
||||||
|
menuItems.push({
|
||||||
type: 'link',
|
type: 'link',
|
||||||
icon: 'ti ti-messages',
|
icon: 'ti ti-messages',
|
||||||
text: i18n.ts._chat.chatWithThisUser,
|
text: i18n.ts._chat.chatWithThisUser,
|
||||||
to: `/chat/user/${user.id}`,
|
to: `/chat/user/${user.id}`,
|
||||||
}, { type: 'divider' }, {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
menuItems.push({ type: 'divider' }, {
|
||||||
icon: user.isMuted ? 'ti ti-eye' : 'ti ti-eye-off',
|
icon: user.isMuted ? 'ti ti-eye' : 'ti ti-eye-off',
|
||||||
text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute,
|
text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute,
|
||||||
action: toggleMute,
|
action: toggleMute,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { getHTMLElementOrNull } from "@/utility/get-dom-node-or-null.js";
|
import { getHTMLElementOrNull } from '@/utility/get-dom-node-or-null.js';
|
||||||
|
|
||||||
//#region types
|
//#region types
|
||||||
export type Keymap = Record<string, CallbackFunction | CallbackObject>;
|
export type Keymap = Record<string, CallbackFunction | CallbackObject>;
|
||||||
|
@ -136,7 +136,7 @@ let lastHotKeyStoreTimer: number | null = null;
|
||||||
|
|
||||||
const storePattern = (ev: KeyboardEvent, callback: CallbackFunction) => {
|
const storePattern = (ev: KeyboardEvent, callback: CallbackFunction) => {
|
||||||
if (lastHotKeyStoreTimer != null) {
|
if (lastHotKeyStoreTimer != null) {
|
||||||
clearTimeout(lastHotKeyStoreTimer);
|
window.clearTimeout(lastHotKeyStoreTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
latestHotkey = {
|
latestHotkey = {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
const requestIdleCallback: typeof globalThis.requestIdleCallback = globalThis.requestIdleCallback ?? ((callback) => {
|
const requestIdleCallback: typeof globalThis.requestIdleCallback = globalThis.requestIdleCallback ?? ((callback) => {
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = window.setTimeout(() => {
|
||||||
callback({
|
callback({
|
||||||
didTimeout: false, // polyfill でタイムアウト発火することはない
|
didTimeout: false, // polyfill でタイムアウト発火することはない
|
||||||
timeRemaining() {
|
timeRemaining() {
|
||||||
|
@ -17,7 +17,7 @@ const requestIdleCallback: typeof globalThis.requestIdleCallback = globalThis.re
|
||||||
return timeoutId;
|
return timeoutId;
|
||||||
});
|
});
|
||||||
const cancelIdleCallback: typeof globalThis.cancelIdleCallback = globalThis.cancelIdleCallback ?? ((timeoutId) => {
|
const cancelIdleCallback: typeof globalThis.cancelIdleCallback = globalThis.cancelIdleCallback ?? ((timeoutId) => {
|
||||||
clearTimeout(timeoutId);
|
window.clearTimeout(timeoutId);
|
||||||
});
|
});
|
||||||
|
|
||||||
class IdlingRenderScheduler {
|
class IdlingRenderScheduler {
|
||||||
|
|
|
@ -158,7 +158,7 @@ export async function playMisskeySfxFile(soundStore: SoundStore): Promise<boolea
|
||||||
canPlay = false;
|
canPlay = false;
|
||||||
return await playMisskeySfxFileInternal(soundStore).finally(() => {
|
return await playMisskeySfxFileInternal(soundStore).finally(() => {
|
||||||
// ごく短時間に音が重複しないように
|
// ごく短時間に音が重複しないように
|
||||||
setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
canPlay = true;
|
canPlay = true;
|
||||||
}, 25);
|
}, 25);
|
||||||
});
|
});
|
||||||
|
@ -230,10 +230,10 @@ export async function getSoundDuration(file: string): Promise<number> {
|
||||||
const audioEl = window.document.createElement('audio');
|
const audioEl = window.document.createElement('audio');
|
||||||
audioEl.src = file;
|
audioEl.src = file;
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const si = setInterval(() => {
|
const si = window.setInterval(() => {
|
||||||
if (audioEl.readyState > 0) {
|
if (audioEl.readyState > 0) {
|
||||||
resolve(audioEl.duration * 1000);
|
resolve(audioEl.duration * 1000);
|
||||||
clearInterval(si);
|
window.clearInterval(si);
|
||||||
audioEl.remove();
|
audioEl.remove();
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
|
@ -5,5 +5,5 @@
|
||||||
|
|
||||||
export async function tick(): Promise<void> {
|
export async function tick(): Promise<void> {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
await new Promise((globalThis.requestIdleCallback ?? setTimeout) as never);
|
await new Promise((globalThis.requestIdleCallback ?? window.setTimeout) as never);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "misskey-js",
|
"name": "misskey-js",
|
||||||
"version": "2025.3.2-beta.14",
|
"version": "2025.3.2-beta.16",
|
||||||
"description": "Misskey SDK for JavaScript",
|
"description": "Misskey SDK for JavaScript",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
|
|
|
@ -4057,6 +4057,7 @@ export type components = {
|
||||||
followersVisibility: 'public' | 'followers' | 'private';
|
followersVisibility: 'public' | 'followers' | 'private';
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
chatScope: 'everyone' | 'following' | 'followers' | 'mutual' | 'none';
|
chatScope: 'everyone' | 'following' | 'followers' | 'mutual' | 'none';
|
||||||
|
canChat: boolean;
|
||||||
roles: components['schemas']['RoleLite'][];
|
roles: components['schemas']['RoleLite'][];
|
||||||
followedMessage?: string | null;
|
followedMessage?: string | null;
|
||||||
memo: string | null;
|
memo: string | null;
|
||||||
|
|
Loading…
Reference in New Issue