Firefish v1.0.5-dev19
This commit is contained in:
parent
bd7273fd5e
commit
be83fa27ac
40 changed files with 750 additions and 654 deletions
|
@ -526,12 +526,12 @@ objectStorageBaseUrl: "Basis-URL"
|
|||
objectStorageBaseUrlDesc: "Die als Referenz verwendete URL. Verwendest du einen CDN
|
||||
oder Proxy, gib dessen URL an. \nFür S3 verwende 'https://<bucket>.s3.amazonaws.com'.
|
||||
Für GCS o.ä. verwende 'https://storage.googleapis.com/<bucket>'."
|
||||
objectStorageBucket: "Eimer"
|
||||
objectStorageBucket: "Bucket"
|
||||
objectStorageBucketDesc: "Bitte gib den Namen des Buckets an, der bei deinem Anbieter
|
||||
verwendet wird."
|
||||
objectStoragePrefix: "Prefix"
|
||||
objectStoragePrefixDesc: "Dateien werden in Ordnern unter diesem Prefix gespeichert."
|
||||
objectStorageEndpoint: "Limit"
|
||||
objectStorageEndpoint: "Endpunkt"
|
||||
objectStorageEndpointDesc: "Im Falle von S3 leerlassen, für andere Anbieter den relevanten
|
||||
Endpoint im Format „<host>“ oder „<host>:<port>“ angeben."
|
||||
objectStorageRegion: "Region"
|
||||
|
@ -1034,6 +1034,7 @@ _accountDelete:
|
|||
_ad:
|
||||
back: "Zurück"
|
||||
reduceFrequencyOfThisAd: "Diese Werbeanzeige weniger anzeigen"
|
||||
adsBy: Community-Banner von {by}
|
||||
_forgotPassword:
|
||||
enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese
|
||||
wird ein Link gesendet, mit dem du dein Passwort zurücksetzen kannst."
|
||||
|
@ -1240,6 +1241,7 @@ _wordMute:
|
|||
soft: "Leicht"
|
||||
hard: "Schwer"
|
||||
mutedNotes: "Stummgeschaltete Beiträge"
|
||||
muteLangs: Stummgeschaltete Sprachen
|
||||
_instanceMute:
|
||||
instanceMuteDescription: "Schaltet alle Beiträge/Boosts stumm, die von den gelisteten
|
||||
Servern stammen, inklusive Antworten von Nutzern an einen Nutzer eines stummgeschalteten
|
||||
|
@ -2212,3 +2214,5 @@ indexable: Indexierbar
|
|||
languageForTranslation: Übersetzungssprache veröffentlichen
|
||||
openServerInfo: Anzeigen von Serverinformationen durch Anklicken des Server-Tickers
|
||||
in einem Beitrag
|
||||
vibrate: Vibrationen abspielen
|
||||
clickToShowPatterns: Klicken um Modul-Muster anzuzeigen
|
||||
|
|
|
@ -616,7 +616,7 @@ emptyToDisableSmtpAuth: "ユーザー名とパスワードを空欄にするこ
|
|||
smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
|
||||
smtpSecureInfo: "STARTTLS使用時はオフにします。"
|
||||
testEmail: "配信テスト"
|
||||
wordMute: "ワードミュート"
|
||||
wordMute: "単語または言語のミュート"
|
||||
regexpError: "正規表現エラー"
|
||||
regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが発生しました:"
|
||||
instanceMute: "サーバーミュート"
|
||||
|
@ -694,7 +694,7 @@ no: "いいえ"
|
|||
driveFilesCount: "ドライブのファイル数"
|
||||
driveUsage: "ドライブ使用量"
|
||||
noCrawle: "クローラーによるインデックスを拒否"
|
||||
noCrawleDescription: "検索エンジンにあなたのプロフィールや投稿、ページなどのコンテンツを登録(インデックス)しないよう要請します。"
|
||||
noCrawleDescription: "Web検索にあなたのプロフィールや投稿、ページなどのコンテンツを登録(インデックス)しないよう要請します。"
|
||||
lockedAccountInfo: "フォローを承認制にしても、投稿の公開範囲を「フォロワー」にしない限り、誰でもあなたの投稿を見られます。"
|
||||
alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にする"
|
||||
loadRawImages: "添付画像のサムネイルをオリジナル画質にする"
|
||||
|
@ -811,7 +811,7 @@ instanceSecurity: "サーバーのセキュリティー"
|
|||
secureModeInfo: "認証情報の無いリモートサーバーからのリクエストに応えません。"
|
||||
privateMode: "非公開モード"
|
||||
privateModeInfo: "有効にすると、許可したサーバーのみからリクエストを受け付けます。"
|
||||
allowedInstances: "許可されたサーバー"
|
||||
allowedInstances: "許可するサーバー"
|
||||
allowedInstancesDescription: "許可したいサーバーのホストを改行で区切って設定します。非公開モードだけで有効です。"
|
||||
previewNoteText: "本文をプレビュー"
|
||||
customCss: "カスタムCSS"
|
||||
|
@ -1518,7 +1518,7 @@ _profile:
|
|||
youCanIncludeHashtags: "ハッシュタグを含められます。"
|
||||
metadata: "追加情報"
|
||||
metadataEdit: "追加情報を編集"
|
||||
metadataDescription: "プロフィールに追加情報を表示できます。{a}タグまたは{l}タグを{rel}とともに追加すると、プロフィールのリンクを本人認証できます。"
|
||||
metadataDescription: "プロフィールに追加情報を表示できます。{rel}属性をつけた{a}タグや{l}タグを含むページをリンクすることで、リンクの本人確認もできます!"
|
||||
metadataLabel: "ラベル"
|
||||
metadataContent: "内容"
|
||||
changeAvatar: "アバター画像を変更"
|
||||
|
@ -2019,6 +2019,9 @@ postSearch: "このサーバーの投稿検索"
|
|||
indexableDescription: MastodonやFirefishなどの検索機能に、あなたの投稿が表示されるのを許可します。
|
||||
makePrivate: "秘密にする"
|
||||
makePrivateConfirm: "リモートサーバーに削除リクエストを送信し、投稿の公開範囲を「秘密」にして他の人から見られないようにします。実行しますか?"
|
||||
clickToShowPatterns: クリックしてトラックを表示
|
||||
vibrate: 振動を有効にする
|
||||
indexable: 投稿検索に登録
|
||||
_iconSets:
|
||||
bold: "太め"
|
||||
light: "細め"
|
||||
|
|
|
@ -937,7 +937,8 @@ _accountDelete:
|
|||
inProgress: "正在删除"
|
||||
_ad:
|
||||
back: "返回"
|
||||
reduceFrequencyOfThisAd: "减少此广告的频率"
|
||||
reduceFrequencyOfThisAd: "减少此横幅的频率"
|
||||
adsBy: 社区横幅(作者:{by})
|
||||
_forgotPassword:
|
||||
enterEmail: "请输入您注册账号时用的电子邮箱地址,密码重置链接将发送至该邮箱上。"
|
||||
ifNoEmail: "如果您在注册时没有输入电子邮件地址,请联系服务器管理员。"
|
||||
|
@ -1996,3 +1997,4 @@ indexable: 可索引的
|
|||
languageForTranslation: 帖子翻译语言
|
||||
vibrate: 播放振动
|
||||
openServerInfo: 点击帖子上的服务器滚动条时显示服务器信息
|
||||
clickToShowPatterns: 点击显示模块模式
|
||||
|
|
|
@ -21,7 +21,7 @@ basicSettings: "基本設定"
|
|||
otherSettings: "其他設定"
|
||||
openInWindow: "在新視窗開啟"
|
||||
profile: "個人檔案"
|
||||
timeline: "時間線"
|
||||
timeline: "時間軸"
|
||||
noAccountDescription: "此用戶還沒有自我介紹。"
|
||||
login: "登入"
|
||||
loggingIn: "登入中"
|
||||
|
@ -149,12 +149,12 @@ flagAsBot: "標記此帳號是機器人"
|
|||
flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整Firefish內部系統將本帳戶識別為機器人。"
|
||||
flagAsCat: "你是喵咪嗎?w😺"
|
||||
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示!"
|
||||
flagShowTimelineReplies: "在時間線上顯示貼文的回覆"
|
||||
flagShowTimelineRepliesDescription: "啟用時,時間線除了顯示用戶的貼文以外,還會顯示用戶對其他貼文的回覆。"
|
||||
flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
|
||||
flagShowTimelineRepliesDescription: "啟用時,時間軸除了顯示用戶的貼文以外,還會顯示用戶對其他貼文的回覆。"
|
||||
autoAcceptFollowed: "自動准予追隨中使用者的追隨請求"
|
||||
addAccount: "添加帳戶"
|
||||
loginFailed: "登入失敗"
|
||||
showOnRemote: "轉到所在伺服器顯示"
|
||||
showOnRemote: "開啟來源頁面"
|
||||
general: "一般"
|
||||
wallpaper: "桌布"
|
||||
setWallpaper: "設定桌布"
|
||||
|
@ -324,9 +324,9 @@ dayX: "{day}日"
|
|||
monthX: "{month}月"
|
||||
yearX: "{year}年"
|
||||
pages: "頁面"
|
||||
enableLocalTimeline: "開啟本地時間線"
|
||||
enableGlobalTimeline: "啟用公開時間線"
|
||||
disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和板主仍可訪問所有的時間線。"
|
||||
enableLocalTimeline: "開啟本地時間軸"
|
||||
enableGlobalTimeline: "啟用公開時間軸"
|
||||
disablingTimelinesInfo: "即使您關閉了時間軸功能,管理員和板主仍可訪問所有的時間軸。"
|
||||
registration: "註冊"
|
||||
enableRegistration: "開啟新使用者註冊"
|
||||
invite: "邀請"
|
||||
|
@ -386,7 +386,7 @@ administrator: "管理員"
|
|||
token: "權杖"
|
||||
twoStepAuthentication: "兩階段驗證"
|
||||
moderator: "板主"
|
||||
moderation: "言論調節"
|
||||
moderation: "管理"
|
||||
nUsersMentioned: "提到了{n}"
|
||||
securityKey: "安全金鑰"
|
||||
securityKeyName: "金鑰名稱"
|
||||
|
@ -483,7 +483,7 @@ promotion: "推廣"
|
|||
promote: "推廣"
|
||||
numberOfDays: "有效天數"
|
||||
hideThisNote: "隱藏此貼文"
|
||||
showFeaturedNotesInTimeline: "在時間線上顯示熱門推薦"
|
||||
showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦"
|
||||
objectStorage: "Object Storage (物件儲存)"
|
||||
useObjectStorage: "使用Object Storage"
|
||||
objectStorageBaseUrl: "根URL"
|
||||
|
@ -503,7 +503,7 @@ objectStorageUseProxyDesc: "如果不使用代理進行API連接,請關閉"
|
|||
objectStorageSetPublicRead: "上傳時設定為\"public-read\""
|
||||
serverLogs: "伺服器日誌"
|
||||
deleteAll: "刪除所有記錄"
|
||||
showFixedPostForm: "於時間線頁頂顯示「發送貼文」方框"
|
||||
showFixedPostForm: "於時間軸頁頂顯示「發送貼文」方框"
|
||||
newNoteRecived: "發現新的貼文"
|
||||
sounds: "音效"
|
||||
listen: "聆聽"
|
||||
|
@ -671,7 +671,7 @@ no: "取消"
|
|||
driveFilesCount: "雲端硬碟檔案數量"
|
||||
driveUsage: "雲端硬碟使用量"
|
||||
noCrawle: "拒絕搜尋引擎索引"
|
||||
noCrawleDescription: "要求網路搜尋引擎不要索引你的個人資料頁、貼文及頁面等。"
|
||||
noCrawleDescription: "要求外部搜尋引擎不要收錄(索引)你的內容(個人檔案、貼文、頁面等)。"
|
||||
lockedAccountInfo: "即使你通過了追隨者請求,除非你將貼文的可見性設定為 「追隨者」,否則任何人都能看見你的貼文。"
|
||||
alwaysMarkSensitive: "默認將圖像/影像標記為敏感內容"
|
||||
loadRawImages: "以原始圖檔顯示附件圖檔的縮圖"
|
||||
|
@ -689,7 +689,7 @@ experimentalFeatures: "實驗中的功能"
|
|||
developer: "開發者"
|
||||
makeExplorable: "使自己的帳戶能夠在“探索”頁面中顯示"
|
||||
makeExplorableDescription: "如果關閉,帳戶將不會被顯示在\"探索\"頁面中。"
|
||||
showGapBetweenNotesInTimeline: "分開顯示時間線上的貼文"
|
||||
showGapBetweenNotesInTimeline: "分開顯示時間軸上的貼文"
|
||||
duplicate: "複製"
|
||||
left: "左"
|
||||
center: "置中"
|
||||
|
@ -735,7 +735,7 @@ inChannelSearch: "頻道内搜尋"
|
|||
useReactionPickerForContextMenu: "點擊右鍵開啟反應工具欄"
|
||||
typingUsers: "{users}輸入中"
|
||||
jumpToSpecifiedDate: "跳轉到特定日期"
|
||||
showingPastTimeline: "顯示過往的時間線"
|
||||
showingPastTimeline: "顯示過往的時間軸"
|
||||
clear: "清除"
|
||||
markAllAsRead: "全部標示為已讀"
|
||||
goBack: "返回"
|
||||
|
@ -823,7 +823,7 @@ unmuteThread: "將貼文串的靜音解除"
|
|||
ffVisibility: "連接的公開範圍"
|
||||
ffVisibilityDescription: "您可以設定您的關注/關注者資訊的公開範圍。"
|
||||
continueThread: "查看更多貼文"
|
||||
deleteAccountConfirm: "將要刪除帳戶。是否確定?"
|
||||
deleteAccountConfirm: "此帳戶將被刪除,是否繼續?"
|
||||
incorrectPassword: "密碼錯誤。"
|
||||
voteConfirm: "確定投給「{choice}」?"
|
||||
hide: "隱藏"
|
||||
|
@ -934,6 +934,7 @@ _accountDelete:
|
|||
_ad:
|
||||
back: "返回"
|
||||
reduceFrequencyOfThisAd: "降低此橫幅的頻率"
|
||||
adsBy: 社群橫幅(作者:{by})
|
||||
_forgotPassword:
|
||||
enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。"
|
||||
ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。"
|
||||
|
@ -1111,14 +1112,14 @@ _wordMute:
|
|||
muteWords: "加入靜音文字"
|
||||
muteWordsDescription: "用空格分隔指定AND,用換行分隔指定OR。"
|
||||
muteWordsDescription2: "將關鍵字用斜線括起來表示正規表達式。"
|
||||
softDescription: "隱藏時間線中指定條件的貼文。"
|
||||
hardDescription: "具有指定條件的貼文將不添加到時間線。 即使您更改條件,未被添加的貼文也會被排除在外。"
|
||||
softDescription: "隱藏時間軸中指定條件的貼文。"
|
||||
hardDescription: "符合指定條件的貼文將不添加到時間軸。 即使您更改條件,未被添加的貼文也會被排除在外。"
|
||||
soft: "軟性靜音"
|
||||
hard: "硬性靜音"
|
||||
mutedNotes: "已靜音的貼文"
|
||||
muteLangsDescription2: '使用語言代碼。例: en, fr, ja, zh.'
|
||||
lang: 語言
|
||||
langDescription: 將指定語言的貼文從時間線中隱藏。
|
||||
langDescription: 將指定語言的貼文從時間軸中隱藏。
|
||||
muteLangs: 被靜音的語言
|
||||
muteLangsDescription: OR條件以空格或換行進行分隔。
|
||||
_instanceMute:
|
||||
|
@ -1230,16 +1231,16 @@ _tutorial:
|
|||
step2_1: "首先,請完成你的個人資料。"
|
||||
step2_2: "通過提供一些關於你自己的資料,其他人會更容易了解他們是否想看到你的貼文或關注你。"
|
||||
step3_1: "現在是時候追隨一些人了!"
|
||||
step3_2: "你的主頁和社交時間線是基於你所追蹤的人,所以試著先追蹤幾個帳戶。\n點擊個人資料右上角的加號圈就可以關注它。"
|
||||
step3_2: "你的主頁和社交時間軸是基於你所追蹤的人,所以試著先追蹤幾個帳戶。\n點擊個人資料右上角的加號按鈕就可以關注它。"
|
||||
step4_1: "讓我們出去找你。"
|
||||
step4_2: "作為第一則貼文,有些人喜歡發 {introduction} 或單純發一個 \"hello world!\""
|
||||
step5_1: "時間線,到處都是時間線!"
|
||||
step5_2: "您的伺服器已啟用了{timelines}個時間線。"
|
||||
step5_3: "首頁 {icon} 時間線是顯示你追蹤的帳號的貼文。"
|
||||
step5_4: "本地 {icon} 時間線是你可以看到伺服器中所有其他用戶的貼文的時間線。"
|
||||
step5_5: "社交 {icon} 時間線是你的 首頁時間線 和 本地時間線 的結合體。"
|
||||
step5_6: "推薦 {icon} 時間線是顯示你的伺服器管理員推薦的貼文。"
|
||||
step5_7: "全球 {icon} 時間線是顯示來自所有其他連接的伺服器的貼文。"
|
||||
step5_1: "時間軸,到處都是時間軸!"
|
||||
step5_2: "您的伺服器已啟用了{timelines}個時間軸。"
|
||||
step5_3: "首頁 {icon} 時間軸是顯示你追蹤的帳號的貼文。"
|
||||
step5_4: "本地 {icon} 時間軸是你可以看到伺服器中所有其他用戶的貼文的時間軸。"
|
||||
step5_5: "社交 {icon} 時間軸是你的 首頁時間軸 和 本地時間軸 的結合體。"
|
||||
step5_6: "推薦 {icon} 時間軸是顯示你的伺服器管理員推薦的貼文。"
|
||||
step5_7: "全球 {icon} 時間軸是顯示來自所有其他連接的伺服器的貼文。"
|
||||
step6_1: "那麼,這裡是什麼地方?"
|
||||
step6_2: "你不只是加入Firefish。你已經加入了Fediverse的一個門戶,這是一個由成千上萬台服務器組成的互聯網絡。"
|
||||
step6_3: "每個服務器也有不同,而並不是所有的服務器都運行Firefish。但這個服務器確實是運行Firefish的! 你可能會覺得有點複雜,但你很快就會明白的。"
|
||||
|
@ -1328,7 +1329,7 @@ _weekday:
|
|||
_widgets:
|
||||
memo: "備忘錄"
|
||||
notifications: "通知"
|
||||
timeline: "時間線"
|
||||
timeline: "時間軸"
|
||||
calendar: "行事曆"
|
||||
trends: "發燒貼文"
|
||||
clock: "時鐘"
|
||||
|
@ -1384,9 +1385,9 @@ _poll:
|
|||
remainingSeconds: "{s}秒後截止"
|
||||
_visibility:
|
||||
public: "公開"
|
||||
publicDescription: "發佈至公開時間線"
|
||||
publicDescription: "發佈至公開時間軸"
|
||||
home: "不在主頁顯示"
|
||||
homeDescription: "僅發送至首頁的時間線"
|
||||
homeDescription: "僅發送至首頁的時間軸"
|
||||
followers: "追隨者"
|
||||
followersDescription: "僅發佈至關注者"
|
||||
specified: "指定使用者"
|
||||
|
@ -1787,6 +1788,7 @@ _notification:
|
|||
renote: "轉發"
|
||||
reacted: 對您的貼文做出了反應
|
||||
renoted: 轉發了您的貼文
|
||||
voted: 投了票
|
||||
_deck:
|
||||
alwaysShowMainColumn: "總是顯示主欄"
|
||||
columnAlign: "對齊欄位"
|
||||
|
@ -1810,7 +1812,7 @@ _deck:
|
|||
main: "主列"
|
||||
widgets: "小工具"
|
||||
notifications: "通知"
|
||||
tl: "時間線"
|
||||
tl: "時間軸"
|
||||
antenna: "天線"
|
||||
list: "清單"
|
||||
mentions: "提及"
|
||||
|
@ -1842,7 +1844,7 @@ accountMoved: '該使用者已遷移至新帳戶:'
|
|||
showAds: 顯示社群橫幅
|
||||
noThankYou: 不用了,謝謝
|
||||
selectInstance: 選擇伺服器
|
||||
enableRecommendedTimeline: 啟用推薦時間線
|
||||
enableRecommendedTimeline: 啟用推薦時間軸
|
||||
antennaInstancesDescription: 分行列出一個伺服器
|
||||
moveTo: 遷移此帳戶到新帳戶
|
||||
moveToLabel: '請輸入你將會遷移到的帳戶:'
|
||||
|
@ -1874,6 +1876,7 @@ silenced: 已靜音
|
|||
_experiments:
|
||||
title: 試驗功能
|
||||
enablePostImports: 啟用匯入貼文的功能
|
||||
postImportsCaption: 允許用戶從舊有的Firefish・Misskey・Mastodon・Akkoma・Pleroma帳號匯入貼文。在伺服器佇列堵塞時匯入貼文可能會導致載入速度變慢。
|
||||
findOtherInstance: 找找另一個伺服器
|
||||
noGraze: 瀏覽器擴充元件 "Graze for Mastodon" 會與Firefish發生衝突,請停用該擴充元件。
|
||||
userSaysSomethingReasonRenote: '{name} 轉發了包含 {reason} 的貼文'
|
||||
|
@ -1895,7 +1898,7 @@ pushNotification: 推送通知
|
|||
subscribePushNotification: 啟用推送通知
|
||||
unsubscribePushNotification: 停用推送通知
|
||||
pushNotificationAlreadySubscribed: 推送通知已經啟用
|
||||
recommendedInstancesDescription: 以每行分隔的推薦伺服器出現在推薦的時間線中。
|
||||
recommendedInstancesDescription: 推薦的伺服器(將顯示在推薦時間軸中),一行一個。
|
||||
searchPlaceholder: 在 Firefish 上搜尋
|
||||
cw: 內容警告
|
||||
selectChannel: 選擇一個頻道
|
||||
|
@ -1903,9 +1906,9 @@ newer: 較新
|
|||
older: 較舊
|
||||
jumpToPrevious: 跳到上一個
|
||||
removeReaction: 移除你的反應
|
||||
listsDesc: 清單可以創建一個只有您指定用戶的時間線。 可以從時間線頁面訪問它們。
|
||||
listsDesc: 清單可以創建一個只有您指定用戶的時間軸。 可以從時間軸頁面訪問它們。
|
||||
flagSpeakAsCatDescription: 在喵咪模式下你的貼文會被喵化ヾ(•ω•`)o
|
||||
antennasDesc: "天線會顯示符合您設置條件的新貼文!\n 可以從時間線訪問它們。"
|
||||
antennasDesc: "天線會顯示符合您設置條件的新貼文!\n 可以從時間軸訪問它們。"
|
||||
expandOnNoteClick: 點擊以打開貼文
|
||||
expandOnNoteClickDesc: 即使停用,您仍然可以從右鍵選單或單擊發文時間來打開貼文。
|
||||
hiddenTagsDescription: '列出您希望隱藏趨勢和探索的主題標籤(不帶 #)。 隱藏的主題標籤仍然可以通過其他方式發現。'
|
||||
|
@ -1924,7 +1927,7 @@ seperateRenoteQuote: 分開轉發及引用的按鈕
|
|||
clipsDesc: 摘錄就像一個可以分享的書籤。 你可以從每個貼文的菜單創建新摘錄或將貼文加入已有的摘錄。
|
||||
noteId: 貼文 ID
|
||||
sendModMail: 發送審核通知
|
||||
enableIdenticonGeneration: 啟用碎片生成
|
||||
enableIdenticonGeneration: 啟用Identicon生成
|
||||
enableServerMachineStats: 啟用伺服器硬體統計資訊
|
||||
reactionPickerSkinTone: 首選表情符號膚色
|
||||
indexFromDescription: 留空以索引每個貼文
|
||||
|
@ -1965,10 +1968,17 @@ _dialog:
|
|||
charactersExceeded: 超過字數限制! 當前 {current} / 限制 {max}
|
||||
_skinTones:
|
||||
yellow: 黃色
|
||||
medium: 中等
|
||||
dark: 深色
|
||||
mediumDark: 中等偏深
|
||||
light: 淺色
|
||||
mediumLight: 中等偏淺
|
||||
exportZip: 匯出ZIP
|
||||
_feeds:
|
||||
atom: Atom
|
||||
rss: RSS
|
||||
copyFeed: 複製訂閱URL
|
||||
jsonFeed: JSON Feed
|
||||
emojiPackCreator: 表情包的作者
|
||||
importZip: 匯入ZIP
|
||||
delete2fa: 停用二階段認證(2FA)
|
||||
|
@ -2012,3 +2022,9 @@ _iconSets:
|
|||
regular: 標準
|
||||
fill: 填滿
|
||||
duotone: 雙色
|
||||
objectStorageS3ForcePathStyleDesc: 以 "s3.amazonaws.com/<bucket>/" 而非 "<bucket>.s3.amazonaws.com"
|
||||
的格式建構端點(Endpoint)URL。
|
||||
indexable: 登錄至貼文搜尋引擎
|
||||
origin: 來源
|
||||
objectStorageS3ForcePathStyle: 使用基於路徑的端點(Endpoint)URL
|
||||
clickToShowPatterns: 點擊顯示模組模式(Module Pattern)
|
||||
|
|
|
@ -1 +1 @@
|
|||
696d3c6255b3608a8de59fcac5aea3d08b2eeebe
|
||||
0a44d0652244240aa50fbae3ea88c616c8e1c136
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "firefish",
|
||||
"version": "1.0.5-dev18",
|
||||
"version": "1.0.5-dev19",
|
||||
"codename": "aqua",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://code.naskya.net/naskya/firefish"
|
||||
},
|
||||
"packageManager": "pnpm@8.8.0",
|
||||
"packageManager": "pnpm@8.9.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"rebuild": "pnpm run clean && pnpm run build",
|
||||
|
@ -57,7 +57,7 @@
|
|||
"gulp-replace": "1.1.4",
|
||||
"gulp-terser": "2.1.0",
|
||||
"install-peers": "^1.0.4",
|
||||
"pnpm": "8.8.0",
|
||||
"pnpm": "8.9.2",
|
||||
"typescript": "5.2.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { Cache } from "./cache.js";
|
|||
import { getWordHardMute } from "./check-word-mute.js";
|
||||
|
||||
const blockingCache = new Cache<User["id"][]>("blocking", 60 * 5);
|
||||
const mutedWordsCache = new Cache<string[][] | undefined>("mutedWords", 60 * 5);
|
||||
|
||||
export async function checkHitAntenna(
|
||||
antenna: Antenna,
|
||||
|
@ -17,33 +18,16 @@ export async function checkHitAntenna(
|
|||
antennaUserFollowing: User["id"][],
|
||||
): Promise<boolean> {
|
||||
if (note.visibility === "specified") return false;
|
||||
|
||||
// アンテナ作成者がノート作成者にブロックされていたらスキップ
|
||||
const blockings = await blockingCache.fetch(noteUser.id, () =>
|
||||
Blockings.findBy({ blockerId: noteUser.id }).then((res) =>
|
||||
res.map((x) => x.blockeeId),
|
||||
),
|
||||
);
|
||||
if (blockings.some((blocking) => blocking === antenna.userId)) return false;
|
||||
|
||||
if (
|
||||
await getWordHardMute(
|
||||
note,
|
||||
antenna.userId,
|
||||
(
|
||||
await UserProfiles.findOneBy({ userId: antenna.userId })
|
||||
)?.mutedWords,
|
||||
)
|
||||
)
|
||||
return false;
|
||||
if (antenna.withFile) {
|
||||
if (note.fileIds && note.fileIds.length === 0) return false;
|
||||
}
|
||||
if (!antenna.withReplies && note.replyId != null) return false;
|
||||
|
||||
if (note.visibility === "followers" || note.visibility === "home") {
|
||||
if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!antenna.withReplies && note.replyId != null) return false;
|
||||
|
||||
if (antenna.src === "users") {
|
||||
const accts = antenna.users.map((x) => {
|
||||
const { username, host } = Acct.parse(x);
|
||||
|
@ -107,9 +91,20 @@ export async function checkHitAntenna(
|
|||
if (matched) return false;
|
||||
}
|
||||
|
||||
if (antenna.withFile) {
|
||||
if (note.fileIds && note.fileIds.length === 0) return false;
|
||||
}
|
||||
// アンテナ作成者がノート作成者にブロックされていたらスキップ
|
||||
const blockings = await blockingCache.fetch(noteUser.id, () =>
|
||||
Blockings.findBy({ blockerId: noteUser.id }).then((res) =>
|
||||
res.map((x) => x.blockeeId),
|
||||
),
|
||||
);
|
||||
if (blockings.includes(antenna.userId)) return false;
|
||||
|
||||
const mutedWords = await mutedWordsCache.fetch(antenna.userId, () =>
|
||||
UserProfiles.findOneBy({ userId: antenna.userId }).then(
|
||||
(profile) => profile?.mutedWords,
|
||||
),
|
||||
);
|
||||
if (await getWordHardMute(note, antenna.userId, mutedWords)) return false;
|
||||
|
||||
// TODO: eval expression
|
||||
|
||||
|
|
|
@ -59,10 +59,7 @@ export async function getWordHardMute(
|
|||
meId: string | null | undefined,
|
||||
mutedWords?: Array<string | string[]>,
|
||||
): Promise<boolean> {
|
||||
// 自分自身
|
||||
if (note.userId === meId || mutedWords == null) {
|
||||
return false;
|
||||
}
|
||||
if (note.userId === meId || mutedWords == null) return false;
|
||||
|
||||
if (mutedWords.length > 0) {
|
||||
return (
|
||||
|
|
|
@ -90,11 +90,25 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
|
|||
}
|
||||
|
||||
// HTTP-Signatureの検証
|
||||
const httpSignatureValidated = httpSignature.verifySignature(
|
||||
let httpSignatureValidated = httpSignature.verifySignature(
|
||||
signature,
|
||||
authUser.key.keyPem,
|
||||
);
|
||||
|
||||
// If signature validation failed, try refetching the actor
|
||||
if (!httpSignatureValidated) {
|
||||
authUser.key = await dbResolver.refetchPublicKeyForApId(authUser.user);
|
||||
|
||||
if (authUser.key == null) {
|
||||
return "skip: failed to re-resolve user publicKey";
|
||||
}
|
||||
|
||||
httpSignatureValidated = httpSignature.verifySignature(
|
||||
signature,
|
||||
authUser.key.keyPem,
|
||||
);
|
||||
}
|
||||
|
||||
// また、signatureのsignerは、activity.actorと一致する必要がある
|
||||
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
|
||||
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
|
||||
|
|
|
@ -86,11 +86,25 @@ export async function checkFetch(req: IncomingMessage): Promise<number> {
|
|||
}
|
||||
|
||||
// HTTP-Signatureの検証
|
||||
const httpSignatureValidated = httpSignature.verifySignature(
|
||||
let httpSignatureValidated = httpSignature.verifySignature(
|
||||
signature,
|
||||
authUser.key.keyPem,
|
||||
);
|
||||
|
||||
// If signature validation failed, try refetching the actor
|
||||
if (!httpSignatureValidated) {
|
||||
authUser.key = await dbResolver.refetchPublicKeyForApId(authUser.user);
|
||||
|
||||
if (authUser.key == null) {
|
||||
return 403;
|
||||
}
|
||||
|
||||
httpSignatureValidated = httpSignature.verifySignature(
|
||||
signature,
|
||||
authUser.key.keyPem,
|
||||
);
|
||||
}
|
||||
|
||||
if (!httpSignatureValidated) {
|
||||
return 403;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import { Cache } from "@/misc/cache.js";
|
|||
import { uriPersonCache, userByIdCache } from "@/services/user-cache.js";
|
||||
import type { IObject } from "./type.js";
|
||||
import { getApId } from "./type.js";
|
||||
import { resolvePerson } from "./models/person.js";
|
||||
import { resolvePerson, updatePerson } from "./models/person.js";
|
||||
|
||||
const publicKeyCache = new Cache<UserPublickey | null>("publicKey", 60 * 30);
|
||||
const publicKeyByUserIdCache = new Cache<UserPublickey | null>(
|
||||
|
@ -151,7 +151,7 @@ export default class DbResolver {
|
|||
*/
|
||||
public async getAuthUserFromKeyId(keyId: string): Promise<{
|
||||
user: CacheableRemoteUser;
|
||||
key: UserPublickey;
|
||||
key: UserPublickey | null;
|
||||
} | null> {
|
||||
const key = await publicKeyCache.fetch(
|
||||
keyId,
|
||||
|
@ -203,4 +203,15 @@ export default class DbResolver {
|
|||
key,
|
||||
};
|
||||
}
|
||||
|
||||
public async refetchPublicKeyForApId(
|
||||
user: CacheableRemoteUser,
|
||||
): Promise<UserPublickey | null> {
|
||||
await updatePerson(user.uri!, undefined, undefined, user);
|
||||
const key = await UserPublickeys.findOneBy({ userId: user.id });
|
||||
if (key != null) {
|
||||
await publicKeyByUserIdCache.set(user.id, key);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ export const meta = {
|
|||
id: "4362f8dc-731f-4ad8-a694-be2a88922a24",
|
||||
},
|
||||
uriNull: {
|
||||
message: "User ActivityPup URI is null.",
|
||||
message: "User ActivityPub URI is null.",
|
||||
code: "URI_NULL",
|
||||
id: "bf326f31-d430-4f97-9933-5d61e4d48a23",
|
||||
},
|
||||
|
|
|
@ -48,12 +48,12 @@ export const meta = {
|
|||
id: "fcd2eef9-a9b2-4c4f-8624-038099e90aa5",
|
||||
},
|
||||
uriNull: {
|
||||
message: "User ActivityPup URI is null.",
|
||||
message: "User ActivityPub URI is null.",
|
||||
code: "URI_NULL",
|
||||
id: "bf326f31-d430-4f97-9933-5d61e4d48a23",
|
||||
},
|
||||
localUriNull: {
|
||||
message: "Local User ActivityPup URI is null.",
|
||||
message: "Local User ActivityPub URI is null.",
|
||||
code: "URI_NULL",
|
||||
id: "95ba11b9-90e8-43a5-ba16-7acc1ab32e71",
|
||||
},
|
||||
|
|
|
@ -67,9 +67,8 @@ export default class extends Channel {
|
|||
// レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
|
||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
||||
if (
|
||||
this.user &&
|
||||
this.userProfile &&
|
||||
(await getWordHardMute(note, this.user.id, this.userProfile.mutedWords))
|
||||
(await getWordHardMute(note, this.user?.id, this.userProfile.mutedWords))
|
||||
)
|
||||
return;
|
||||
|
||||
|
|
|
@ -66,9 +66,8 @@ export default class extends Channel {
|
|||
// レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
|
||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
||||
if (
|
||||
this.user &&
|
||||
this.userProfile &&
|
||||
(await getWordHardMute(note, this.user.id, this.userProfile.mutedWords))
|
||||
(await getWordHardMute(note, this.user?.id, this.userProfile.mutedWords))
|
||||
)
|
||||
return;
|
||||
|
||||
|
|
|
@ -83,9 +83,8 @@ export default class extends Channel {
|
|||
// レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
|
||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
||||
if (
|
||||
this.user &&
|
||||
this.userProfile &&
|
||||
(await getWordHardMute(note, this.user.id, this.userProfile.mutedWords))
|
||||
(await getWordHardMute(note, this.user?.id, this.userProfile.mutedWords))
|
||||
)
|
||||
return;
|
||||
|
||||
|
|
|
@ -59,9 +59,8 @@ export default class extends Channel {
|
|||
// レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
|
||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
||||
if (
|
||||
this.user &&
|
||||
this.userProfile &&
|
||||
(await getWordHardMute(note, this.user.id, this.userProfile.mutedWords))
|
||||
(await getWordHardMute(note, this.user?.id, this.userProfile.mutedWords))
|
||||
)
|
||||
return;
|
||||
|
||||
|
|
|
@ -81,9 +81,8 @@ export default class extends Channel {
|
|||
// レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
|
||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる
|
||||
if (
|
||||
this.user &&
|
||||
this.userProfile &&
|
||||
(await getWordHardMute(note, this.user.id, this.userProfile.mutedWords))
|
||||
(await getWordHardMute(note, this.user?.id, this.userProfile.mutedWords))
|
||||
)
|
||||
return;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as mfm from "mfm-js";
|
||||
import es from "../../db/elasticsearch.js";
|
||||
import sonic from "../../db/sonic.js";
|
||||
import es from "@/db/elasticsearch.js";
|
||||
import sonic from "@/db/sonic.js";
|
||||
import {
|
||||
publishMainStream,
|
||||
publishNotesStream,
|
||||
|
|
|
@ -10,15 +10,15 @@
|
|||
:aria-controls="bodyId"
|
||||
>
|
||||
<template v-if="showBody"
|
||||
><i class="ph-caret-up ph ph-lg"></i
|
||||
><i :class="icon('ph-caret-up')"></i
|
||||
></template>
|
||||
<template v-else
|
||||
><i class="ph-caret-down ph ph-lg"></i
|
||||
><i :class="icon('ph-caret-down')"></i
|
||||
></template>
|
||||
</button>
|
||||
</header>
|
||||
<transition
|
||||
name=""
|
||||
:name="defaultStore.state.animation ? 'folder-toggle' : ''"
|
||||
@enter="enter"
|
||||
@after-enter="afterEnter"
|
||||
@leave="leave"
|
||||
|
@ -34,8 +34,8 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { getUniqueId } from "@/os";
|
||||
// import { defaultStore } from "@/store";
|
||||
// import icon from "@/scripts/icon";
|
||||
import { defaultStore } from "@/store";
|
||||
import icon from "@/scripts/icon";
|
||||
|
||||
const localStoragePrefix = "ui:folder:";
|
||||
|
||||
|
@ -62,6 +62,8 @@ export default defineComponent({
|
|||
localStoragePrefix + this.persistKey,
|
||||
) === "t"
|
||||
: this.expanded,
|
||||
defaultStore,
|
||||
icon,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
|
|
@ -359,7 +359,7 @@ const isDeleted = ref(false);
|
|||
const muted = ref(
|
||||
getWordSoftMute(
|
||||
note.value,
|
||||
$i,
|
||||
$i.id,
|
||||
defaultStore.state.mutedWords,
|
||||
defaultStore.state.mutedLangs,
|
||||
),
|
||||
|
|
|
@ -234,7 +234,7 @@ const isDeleted = ref(false);
|
|||
const muted = ref(
|
||||
getWordSoftMute(
|
||||
note.value,
|
||||
$i,
|
||||
$i.id,
|
||||
defaultStore.state.mutedWords,
|
||||
defaultStore.state.mutedLangs,
|
||||
),
|
||||
|
|
|
@ -268,7 +268,7 @@ const isDeleted = ref(false);
|
|||
const muted = ref(
|
||||
getWordSoftMute(
|
||||
note.value,
|
||||
$i,
|
||||
$i.id,
|
||||
defaultStore.state.mutedWords,
|
||||
defaultStore.state.mutedLangs,
|
||||
),
|
||||
|
|
|
@ -509,9 +509,6 @@ useTooltip(reactionRef, (showing) => {
|
|||
|
||||
// > span:first-child {
|
||||
// opacity: 0.7;
|
||||
// &::after {
|
||||
// content: ": ";
|
||||
// }
|
||||
// }
|
||||
|
||||
> i {
|
||||
|
|
|
@ -42,11 +42,11 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const note = ref<firefish.entities.Note>();
|
||||
const tab = ref<string>();
|
||||
const tab = ref<string | null>(null);
|
||||
const reactions = ref<string[]>();
|
||||
const users = ref();
|
||||
|
||||
watch(tab, async () => {
|
||||
async function updateUsers(): void {
|
||||
const res = await os.api("notes/reactions", {
|
||||
noteId: props.noteId,
|
||||
type: tab.value,
|
||||
|
@ -54,15 +54,17 @@ watch(tab, async () => {
|
|||
});
|
||||
|
||||
users.value = res.map((x) => x.user);
|
||||
});
|
||||
}
|
||||
|
||||
watch(tab, updateUsers);
|
||||
|
||||
onMounted(() => {
|
||||
os.api("notes/show", {
|
||||
noteId: props.noteId,
|
||||
}).then((res) => {
|
||||
}).then(async (res) => {
|
||||
reactions.value = Object.keys(res.reactions);
|
||||
tab.value = reactions.value[0];
|
||||
note.value = res;
|
||||
await updateUsers();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -18,11 +18,14 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import icon from "@/scripts/icon";
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
defaultOpen: boolean;
|
||||
}>();
|
||||
|
||||
const opened = ref(props.defaultOpen);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<template>
|
||||
<transition name="" mode="out-in">
|
||||
<transition
|
||||
:name="defaultStore.state.animation ? 'fade' : ''"
|
||||
mode="out-in"
|
||||
>
|
||||
<div v-if="pending">
|
||||
<MkLoading />
|
||||
</div>
|
||||
|
@ -9,11 +12,11 @@
|
|||
<div v-else>
|
||||
<div class="wszdbhzo">
|
||||
<div>
|
||||
<i class="ph-warning ph ph-lg"></i>
|
||||
<i :class="iconClass('ph-warning')"></i>
|
||||
{{ i18n.ts.somethingHappened }}
|
||||
</div>
|
||||
<MkButton inline class="retry" @click="retry"
|
||||
><i class="ph-arrow-clockwise ph ph-lg"></i>
|
||||
<MkButton inline class="retry" @click="retry">
|
||||
<i :class="iconClass('ph-arrow-clockwise')"></i>
|
||||
{{ i18n.ts.retry }}</MkButton
|
||||
>
|
||||
</div>
|
||||
|
@ -26,8 +29,8 @@ import type { PropType } from "vue";
|
|||
import { defineComponent, ref, watch } from "vue";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
import { i18n } from "@/i18n";
|
||||
// import { defaultStore } from "@/store";
|
||||
// import icon from "@/scripts/icon";
|
||||
import { defaultStore } from "@/store";
|
||||
import iconClass from "@/scripts/icon";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -87,6 +90,8 @@ export default defineComponent({
|
|||
result,
|
||||
retry,
|
||||
i18n,
|
||||
defaultStore,
|
||||
iconClass,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
primary
|
||||
class="save"
|
||||
@click="updated"
|
||||
><i class="ph-floppy-disk-back ph ph-lg"></i>
|
||||
>
|
||||
<i :class="icon('ph-floppy-disk-back')"></i>
|
||||
{{ i18n.ts.save }}</MkButton
|
||||
>
|
||||
</div>
|
||||
|
@ -50,6 +51,7 @@ import {
|
|||
import { debounce } from "throttle-debounce";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
import { i18n } from "@/i18n";
|
||||
import icon from "@/scripts/icon";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -188,6 +190,7 @@ export default defineComponent({
|
|||
onKeydown,
|
||||
updated,
|
||||
i18n,
|
||||
icon,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
@swiper="setSwiperRef"
|
||||
@slide-change="onSlideChange"
|
||||
>
|
||||
<swiper-slide>
|
||||
<swiper-slide v-if="true">
|
||||
<MkFolder class="_gap">
|
||||
<template #header
|
||||
><i :class="icon('ph-clock')"></i>
|
||||
|
@ -66,7 +66,7 @@
|
|||
</MkPagination>
|
||||
</MkFolder>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<swiper-slide v-if="true">
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
:pagination="likedPostsPagination"
|
||||
|
@ -81,7 +81,7 @@
|
|||
</div>
|
||||
</MkPagination>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<swiper-slide v-if="true">
|
||||
<MkA to="/gallery/new" class="_link" style="margin: 16px"
|
||||
><i :class="icon('ph-plus')"></i>
|
||||
{{ i18n.ts.postToGallery }}</MkA
|
||||
|
|
|
@ -75,6 +75,7 @@ export default defineComponent({
|
|||
return {
|
||||
showBody: this.expanded,
|
||||
i18n,
|
||||
icon,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
>
|
||||
<template #func>
|
||||
<button class="_button" @click="changeType()">
|
||||
<i :class="icon('ph-pencil')"></i>
|
||||
<i :class="iconClass('ph-pencil')"></i>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
|
@ -158,7 +158,7 @@ import * as os from "@/os";
|
|||
import { isLiteralValue } from "@/scripts/hpml/expr";
|
||||
import { funcDefs } from "@/scripts/hpml/lib";
|
||||
import { i18n } from "@/i18n";
|
||||
import icon from "@/scripts/icon";
|
||||
import iconClass from "@/scripts/icon";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -207,6 +207,7 @@ export default defineComponent({
|
|||
warn: null,
|
||||
slots: "",
|
||||
i18n,
|
||||
iconClass,
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
@swiper="setSwiperRef"
|
||||
@slide-change="onSlideChange"
|
||||
>
|
||||
<swiper-slide>
|
||||
<swiper-slide v-if="true">
|
||||
<div class="rknalgpo">
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
|
@ -38,7 +38,7 @@
|
|||
</MkPagination>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<swiper-slide v-if="true">
|
||||
<div class="rknalgpo liked">
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
|
@ -53,7 +53,7 @@
|
|||
</MkPagination>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<swiper-slide v-if="true">
|
||||
<div class="rknalgpo my">
|
||||
<div class="buttoncontainer">
|
||||
<MkButton class="new primary" @click="create()"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import type * as firefish from "firefish-js";
|
||||
|
||||
export interface Muted {
|
||||
muted: boolean;
|
||||
matched: string[];
|
||||
|
@ -7,7 +9,7 @@ export interface Muted {
|
|||
const NotMuted = { muted: false, matched: [] };
|
||||
|
||||
function checkLangMute(
|
||||
note: NoteLike,
|
||||
note: firefish.entities.Note,
|
||||
mutedLangs: Array<string | string[]>,
|
||||
): Muted {
|
||||
const mutedLangList = new Set(
|
||||
|
@ -20,7 +22,7 @@ function checkLangMute(
|
|||
}
|
||||
|
||||
function checkWordMute(
|
||||
note: NoteLike,
|
||||
note: firefish.entities.Note,
|
||||
mutedWords: Array<string | string[]>,
|
||||
): Muted {
|
||||
let text = `${note.cw ?? ""} ${note.text ?? ""}`;
|
||||
|
@ -72,15 +74,12 @@ function checkWordMute(
|
|||
}
|
||||
|
||||
export function getWordSoftMute(
|
||||
note: Record<string, any>,
|
||||
me: Record<string, any> | null | undefined,
|
||||
note: firefish.entities.Note,
|
||||
meId: string | null | undefined,
|
||||
mutedWords: Array<string | string[]>,
|
||||
mutedLangs: Array<string | string[]>,
|
||||
): Muted {
|
||||
// 自分自身
|
||||
if (me && note.userId === me.id) {
|
||||
return NotMuted;
|
||||
}
|
||||
if (note.userId === meId) return NotMuted;
|
||||
|
||||
if (mutedWords.length > 0) {
|
||||
const noteMuted = checkWordMute(note, mutedWords);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defaultStore } from "@/store";
|
||||
|
||||
export default function icon(name: string, large = true): string {
|
||||
export default function (name: string, large = true): string {
|
||||
return `${name} ${large ? "ph-lg" : ""} ${defaultStore.state.iconSet}`;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as mfm from "mfm-js";
|
|||
import { defaultStore } from "@/store";
|
||||
import { expandKaTeXMacro } from "@/scripts/katex-macro";
|
||||
|
||||
export default function preprocess(text: string): string {
|
||||
export default function (text: string): string {
|
||||
if (defaultStore.state.enableCustomKaTeXMacro) {
|
||||
const parsedKaTeXMacro =
|
||||
localStorage.getItem("customKaTeXMacroParsed") ?? "{}";
|
||||
|
|
|
@ -41,23 +41,23 @@ export const defaultStore = markRaw(
|
|||
default: 0,
|
||||
},
|
||||
tlHomeHintClosed: {
|
||||
where: "device",
|
||||
where: "account",
|
||||
default: false,
|
||||
},
|
||||
tlLocalHintClosed: {
|
||||
where: "device",
|
||||
where: "account",
|
||||
default: false,
|
||||
},
|
||||
tlRecommendedHintClosed: {
|
||||
where: "device",
|
||||
where: "account",
|
||||
default: false,
|
||||
},
|
||||
tlSocialHintClosed: {
|
||||
where: "device",
|
||||
where: "account",
|
||||
default: false,
|
||||
},
|
||||
tlGlobalHintClosed: {
|
||||
where: "device",
|
||||
where: "account",
|
||||
default: false,
|
||||
},
|
||||
keepCw: {
|
||||
|
|
|
@ -127,7 +127,9 @@
|
|||
class="item _button help"
|
||||
@click="openHelpMenu"
|
||||
>
|
||||
<i :class="icon('ph-info ph-xl ph-fw help', false)"></i>
|
||||
<i
|
||||
:class="icon('ph-info help icon ph-xl ph-fw', false)"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"api": "pnpm api-extractor run --local --verbose",
|
||||
"api-prod": "pnpm api-extractor run --verbose",
|
||||
"api-doc": "pnpm api-documenter markdown -i ./etc/",
|
||||
"lint": "pnpm biome check --apply **/*.ts",
|
||||
"lint": "pnpm biome check --apply *.ts",
|
||||
"format": "pnpm biome format --write **/*.ts"
|
||||
},
|
||||
"repository": {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"build": "pnpm vite build --emptyOutDir",
|
||||
"build:debug": "pnpm run build",
|
||||
"watch": "pnpm swc src -d built -D -w",
|
||||
"lint": "pnpm biome check **/*.ts --apply",
|
||||
"lint": "pnpm biome check *.ts --apply",
|
||||
"format": "pnpm biome format * --write"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
1048
pnpm-lock.yaml
1048
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue