Compare commits
30 Commits
8dd8f636dc
...
cce88c904b
Author | SHA1 | Date |
---|---|---|
|
cce88c904b | |
|
d866ab12e9 | |
|
df75715d29 | |
|
02da241ec9 | |
|
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 |
|
@ -38,6 +38,7 @@
|
||||||
- Enhance: プラグインの管理が強化されました
|
- Enhance: プラグインの管理が強化されました
|
||||||
- インストール/アンインストール/設定の変更時にリロード不要になりました
|
- インストール/アンインストール/設定の変更時にリロード不要になりました
|
||||||
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
|
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
|
||||||
|
- Enhance: アイコンのスクロール追従を無効化してパフォーマンス向上できるように
|
||||||
- Enhance: CWの注釈テキストが入力されていない場合, Postボタンを非アクティブに
|
- Enhance: CWの注釈テキストが入力されていない場合, Postボタンを非アクティブに
|
||||||
- Enhance: CWを無効にした場合, 注釈テキストが最大入力文字数を超えていても投稿できるように
|
- Enhance: CWを無効にした場合, 注釈テキストが最大入力文字数を超えていても投稿できるように
|
||||||
- Enhance: テーマ設定画面のデザインを改善
|
- Enhance: テーマ設定画面のデザインを改善
|
||||||
|
@ -46,6 +47,8 @@
|
||||||
- 文字数カウントを復活
|
- 文字数カウントを復活
|
||||||
- Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように
|
- Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように
|
||||||
- Enhance: 全体的なブラッシュアップ
|
- Enhance: 全体的なブラッシュアップ
|
||||||
|
- Enhance 全体的なパフォーマンス向上
|
||||||
|
- Fix: 読み込み直後にスクロールしようとすると途中で止まる場合があるのを修正
|
||||||
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
|
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
|
|
@ -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: "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"
|
||||||
|
|
|
@ -5634,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"
|
||||||
|
|
|
@ -1409,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.15",
|
"version": "2025.3.2-beta.17",
|
||||||
"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,
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,18 @@
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 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 = {
|
||||||
|
|
|
@ -2,18 +2,16 @@
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 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,9 +850,12 @@ function emitUpdReaction(emoji: string, delta: number) {
|
||||||
margin: 0 14px 0 0;
|
margin: 0 14px 0 0;
|
||||||
width: 58px;
|
width: 58px;
|
||||||
height: 58px;
|
height: 58px;
|
||||||
position: sticky !important;
|
|
||||||
top: calc(22px + var(--MI-stickyTop, 0px));
|
&.useSticky {
|
||||||
left: 0;
|
position: sticky !important;
|
||||||
|
top: calc(22px + var(--MI-stickyTop, 0px));
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
|
@ -1041,7 +1042,10 @@ function emitUpdReaction(emoji: string, delta: number) {
|
||||||
margin: 0 10px 0 0;
|
margin: 0 10px 0 0;
|
||||||
width: 46px;
|
width: 46px;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
top: calc(14px + var(--MI-stickyTop, 0px));
|
|
||||||
|
&.useSticky {
|
||||||
|
top: calc(14px + var(--MI-stickyTop, 0px));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,41 +4,49 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkLoading v-if="fetching"/>
|
<Transition
|
||||||
|
:enterActiveClass="prefer.s.animation ? $style.transition_fade_enterActive : ''"
|
||||||
|
:leaveActiveClass="prefer.s.animation ? $style.transition_fade_leaveActive : ''"
|
||||||
|
:enterFromClass="prefer.s.animation ? $style.transition_fade_enterFrom : ''"
|
||||||
|
:leaveToClass="prefer.s.animation ? $style.transition_fade_leaveTo : ''"
|
||||||
|
mode="out-in"
|
||||||
|
>
|
||||||
|
<MkLoading v-if="fetching"/>
|
||||||
|
|
||||||
<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"/>
|
||||||
<div>{{ i18n.ts.nothing }}</div>
|
<div>{{ i18n.ts.nothing }}</div>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else ref="rootEl" class="_gaps">
|
||||||
|
<div v-show="pagination.reversed && more" key="_more_">
|
||||||
|
<MkButton v-if="!moreFetching" v-appear="(enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMoreAhead : null" :class="$style.more" :wait="moreFetching" primary rounded @click="fetchMoreAhead">
|
||||||
|
{{ i18n.ts.loadMore }}
|
||||||
|
</MkButton>
|
||||||
|
<MkLoading v-else/>
|
||||||
|
</div>
|
||||||
|
<slot :items="Array.from(items.values())" :fetching="fetching || moreFetching"></slot>
|
||||||
|
<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">
|
||||||
|
{{ i18n.ts.loadMore }}
|
||||||
|
</MkButton>
|
||||||
|
<MkLoading v-else/>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else ref="rootEl" class="_gaps">
|
|
||||||
<div v-show="pagination.reversed && more" key="_more_">
|
|
||||||
<MkButton v-if="!moreFetching" v-appear="(enableInfiniteScroll && !props.disableAutoLoad) ? appearFetchMoreAhead : null" :class="$style.more" :wait="moreFetching" primary rounded @click="fetchMoreAhead">
|
|
||||||
{{ i18n.ts.loadMore }}
|
|
||||||
</MkButton>
|
|
||||||
<MkLoading v-else class="loading"/>
|
|
||||||
</div>
|
</div>
|
||||||
<slot :items="Array.from(items.values())" :fetching="fetching || moreFetching"></slot>
|
</Transition>
|
||||||
<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">
|
|
||||||
{{ i18n.ts.loadMore }}
|
|
||||||
</MkButton>
|
|
||||||
<MkLoading v-else class="loading"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
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';
|
||||||
|
@ -250,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' });
|
||||||
}
|
}
|
||||||
|
@ -349,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;
|
||||||
|
@ -445,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);
|
||||||
});
|
});
|
||||||
|
@ -459,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();
|
||||||
|
@ -483,6 +491,15 @@ defineExpose({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
.transition_fade_enterActive,
|
||||||
|
.transition_fade_leaveActive {
|
||||||
|
transition: opacity 0.125s ease;
|
||||||
|
}
|
||||||
|
.transition_fade_enterFrom,
|
||||||
|
.transition_fade_leaveTo {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.more {
|
.more {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
|
|
@ -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,9 +16,8 @@ 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>
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,10 @@
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 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(() => {
|
||||||
rootEl.value.style.viewTransitionName = viewId; // view-transition-nameにcss varが使えないっぽいため直接代入
|
if (prefer.s.animation) {
|
||||||
|
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;
|
||||||
|
|
|
@ -6,17 +6,15 @@ 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>
|
<div v-else-if="tab === 'mentions'">
|
||||||
<div v-else-if="tab === 'mentions'">
|
<MkNotes :pagination="mentionsPagination"/>
|
||||||
<MkNotes :pagination="mentionsPagination"/>
|
</div>
|
||||||
</div>
|
<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';
|
||||||
|
@ -301,7 +301,7 @@ if (!props.game.isEnded) {
|
||||||
|
|
||||||
if (iAmPlayer.value) {
|
if (iAmPlayer.value) {
|
||||||
if ((isMyTurn.value && myTurnTimerRmain.value === 0) || (!isMyTurn.value && opTurnTimerRmain.value === 0)) {
|
if ((isMyTurn.value && myTurnTimerRmain.value === 0) || (!isMyTurn.value && opTurnTimerRmain.value === 0)) {
|
||||||
props.connection!.send('claimTimeIsUp', {});
|
props.connection!.send('claimTimeIsUp', {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, TIMER_INTERVAL_SEC * 1000, { immediate: false, afterMounted: true });
|
}, TIMER_INTERVAL_SEC * 1000, { immediate: false, afterMounted: true });
|
||||||
|
@ -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,47 +4,43 @@ 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">
|
||||||
<MkSpacer :contentMax="800">
|
<MkStickyContainer>
|
||||||
<MkHorizontalSwipe v-model:tab="src" :tabs="$i ? headerTabs : headerTabsWhenNotLogin">
|
<template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin"/></template>
|
||||||
<div ref="rootEl">
|
<MkSpacer :contentMax="800">
|
||||||
<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"
|
||||||
:withReplies="withReplies"
|
:withReplies="withReplies"
|
||||||
:withSensitive="withSensitive"
|
:withSensitive="withSensitive"
|
||||||
:onlyFiles="onlyFiles"
|
:onlyFiles="onlyFiles"
|
||||||
:sound="true"
|
:sound="true"
|
||||||
@queue="queueUpdated"
|
@queue="queueUpdated"
|
||||||
/>
|
/>
|
||||||
</div>
|
</MkSpacer>
|
||||||
</div>
|
</MkStickyContainer>
|
||||||
</MkHorizontalSwipe>
|
</div>
|
||||||
</MkSpacer>
|
|
||||||
</PageWithHeader>
|
|
||||||
</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,
|
||||||
},
|
},
|
||||||
|
|
|
@ -226,9 +226,6 @@ html,
|
||||||
body {
|
body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
overscroll-behavior: none;
|
overscroll-behavior: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,9 +238,6 @@ body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<XAnnouncements v-if="$i"/>
|
<XAnnouncements v-if="$i"/>
|
||||||
<XStatusBars :class="$style.statusbars"/>
|
<XStatusBars :class="$style.statusbars"/>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.content">
|
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :class="$style.content"/>
|
||||||
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']"/>
|
<RouterView v-else :class="$style.content"/>
|
||||||
<RouterView v-else/>
|
|
||||||
</div>
|
|
||||||
<div v-if="isMobile" ref="navFooter" :class="$style.nav">
|
<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="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('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button>
|
||||||
|
@ -216,9 +214,6 @@ html,
|
||||||
body {
|
body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
overscroll-behavior: none;
|
overscroll-behavior: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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.15",
|
"version": "2025.3.2-beta.17",
|
||||||
"description": "Misskey SDK for JavaScript",
|
"description": "Misskey SDK for JavaScript",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
|
|
Loading…
Reference in New Issue