Compare commits
19 Commits
feb2bcdd9e
...
2b8bd591a9
Author | SHA1 | Date |
---|---|---|
|
2b8bd591a9 | |
|
fe38115883 | |
|
6fba73ca13 | |
|
0d33e1f839 | |
|
74f33157a3 | |
|
ae10cad9a7 | |
|
ba9924abdb | |
|
99686801a0 | |
|
f3e0713501 | |
|
7fcbf57a9d | |
|
48232ca57b | |
|
3564bf5c66 | |
|
685fc2bd9d | |
|
6cc0138d1e | |
|
d3228d5570 | |
|
4a8ffe20a7 | |
|
5615675991 | |
|
0c65b8058a | |
|
82bec76cd4 |
|
@ -20,7 +20,9 @@
|
|||
- Enhance: ウォーターマークにアカウントのQRコードを追加できるように
|
||||
- Enhance: テーマをドラッグ&ドロップできるように
|
||||
- Enhance: 絵文字ピッカーのサイズをより大きくできるように
|
||||
- Enhance: カスタム絵文字が多い場合にサーバーの絵文字一覧ページがフリーズしないように
|
||||
- Enhance: 時刻計算のための基準値を一か所で管理するようにし、パフォーマンスを向上
|
||||
- Enhance: 「お問い合わせ」ページから、バグの調査等に役立つ情報(OSやブラウザのバージョン等)を取得・コピーできるように
|
||||
- Fix: iOSで、デバイスがダークモードだと初回読み込み時にエラーになる問題を修正
|
||||
- Fix: アクティビティウィジェットのグラフモードが動作しない問題を修正
|
||||
|
||||
|
@ -1073,7 +1075,7 @@
|
|||
- Fix: カスタム絵文字の画像読み込みに失敗した際はテキストではなくダミー画像を表示 #13487
|
||||
|
||||
### Server
|
||||
-
|
||||
- Fix: FTT有効かつDBフォールバック有効時、STLのようにタイムラインのソースが複数だとFTTとDBのフォールバック間で取得されないノートがある問題
|
||||
|
||||
## 2024.3.0
|
||||
|
||||
|
|
|
@ -1010,6 +1010,7 @@ postForm: "أنشئ ملاحظة"
|
|||
information: "عن"
|
||||
inMinutes: "د"
|
||||
inDays: "ي"
|
||||
widgets: "التطبيقات المُصغّرة"
|
||||
_chat:
|
||||
invitations: "دعوة"
|
||||
noHistory: "السجل فارغ"
|
||||
|
|
|
@ -850,6 +850,7 @@ postForm: "নোট লিখুন"
|
|||
information: "আপনার সম্পর্কে"
|
||||
inMinutes: "মিনিট"
|
||||
inDays: "দিন"
|
||||
widgets: "উইজেটগুলি"
|
||||
_chat:
|
||||
invitations: "আমন্ত্রণ"
|
||||
noHistory: "কোনো ইতিহাস নেই"
|
||||
|
|
|
@ -334,6 +334,7 @@ fileName: "Nom del Fitxer"
|
|||
selectFile: "Selecciona un fitxer"
|
||||
selectFiles: "Selecciona fitxers"
|
||||
selectFolder: "Selecció de carpeta"
|
||||
unselectFolder: "Deixa de seleccionar la carpeta"
|
||||
selectFolders: "Selecció de carpetes"
|
||||
fileNotSelected: "Cap fitxer seleccionat"
|
||||
renameFile: "Canvia el nom del fitxer"
|
||||
|
@ -346,6 +347,7 @@ addFile: "Afegeix un fitxer"
|
|||
showFile: "Mostrar fitxer"
|
||||
emptyDrive: "El teu Disc és buit"
|
||||
emptyFolder: "La carpeta està buida"
|
||||
dropHereToUpload: "Arrossega els arxius fins aquí per pujar-los al servidor"
|
||||
unableToDelete: "No es pot eliminar"
|
||||
inputNewFileName: "Introduïu el nom de fitxer nou"
|
||||
inputNewDescription: "Escriu el peu de foto."
|
||||
|
@ -1389,6 +1391,9 @@ scheduleToPostOnX: "Programar una nota per {x}"
|
|||
scheduledToPostOnX: "S'ha programat la nota per {x}"
|
||||
schedule: "Programa"
|
||||
scheduled: "Programat"
|
||||
widgets: "Ginys"
|
||||
deviceInfo: "Informació del dispositiu"
|
||||
deviceInfoDescription: "En fer consultes tècniques influir la següent informació pot ajudar a resoldre'l més ràpidament."
|
||||
_compression:
|
||||
_quality:
|
||||
high: "Qualitat alta"
|
||||
|
@ -2431,6 +2436,7 @@ _auth:
|
|||
scopeUser: "Opera com si fossis aquest usuari"
|
||||
pleaseLogin: "Si us plau, identificat per autoritzar l'aplicació."
|
||||
byClickingYouWillBeRedirectedToThisUrl: "Si es garanteix l'accés, seràs redirigit automàticament a la següent adreça URL"
|
||||
alreadyAuthorized: "Aquesta aplicació ja té accés."
|
||||
_antennaSources:
|
||||
all: "Totes les publicacions"
|
||||
homeTimeline: "Publicacions dels usuaris seguits"
|
||||
|
@ -2697,6 +2703,8 @@ _notification:
|
|||
quote: "Citar"
|
||||
reaction: "Reaccions"
|
||||
pollEnded: "Enquesta terminada"
|
||||
scheduledNotePosted: "Nota programada amb èxit "
|
||||
scheduledNotePostFailed: "Ha fallat la programació de la nota"
|
||||
receiveFollowRequest: "Rebuda una petició de seguiment"
|
||||
followRequestAccepted: "Petició de seguiment acceptada"
|
||||
roleAssigned: "Rol donat"
|
||||
|
|
|
@ -1109,6 +1109,7 @@ postForm: "Formulář pro odeslání"
|
|||
information: "Informace"
|
||||
inMinutes: "Minut"
|
||||
inDays: "Dnů"
|
||||
widgets: "Widgety"
|
||||
_chat:
|
||||
invitations: "Pozvat"
|
||||
noHistory: "Žádná historie"
|
||||
|
|
|
@ -1370,6 +1370,7 @@ defaultImageCompressionLevel: "Standard-Bildkomprimierungsstufe"
|
|||
defaultImageCompressionLevel_description: "Ein niedrigerer Wert erhält die Bildqualität, erhöht aber die Dateigröße. <br>Höhere Werte reduzieren die Dateigröße, verringern aber die Bildqualität."
|
||||
inMinutes: "Minute(n)"
|
||||
inDays: "Tag(en)"
|
||||
widgets: "Widgets"
|
||||
_order:
|
||||
newest: "Neueste zuerst"
|
||||
oldest: "Älteste zuerst"
|
||||
|
|
|
@ -288,6 +288,7 @@ replies: "Απάντηση"
|
|||
renotes: "Κοινοποίηση σημειώματος"
|
||||
postForm: "Φόρμα δημοσίευσης"
|
||||
information: "Πληροφορίες"
|
||||
widgets: "Μαραφέτια"
|
||||
_chat:
|
||||
members: "Μέλη"
|
||||
home: "Κεντρικό"
|
||||
|
|
|
@ -1389,6 +1389,7 @@ scheduleToPostOnX: "Scheduled to note on {x}"
|
|||
scheduledToPostOnX: "Note is scheduled for {x}"
|
||||
schedule: "Schedule"
|
||||
scheduled: "Scheduled"
|
||||
widgets: "Widgets"
|
||||
_compression:
|
||||
_quality:
|
||||
high: "High quality"
|
||||
|
|
|
@ -1389,6 +1389,7 @@ scheduleToPostOnX: "Programar una nota para {x}"
|
|||
scheduledToPostOnX: "La nota está programada para {x}."
|
||||
schedule: "Programado"
|
||||
scheduled: "Programado"
|
||||
widgets: "Widgets"
|
||||
_compression:
|
||||
_quality:
|
||||
high: "Calidad alta"
|
||||
|
|
|
@ -1273,6 +1273,7 @@ postForm: "Formulaire de publication"
|
|||
information: "Informations"
|
||||
inMinutes: "min"
|
||||
inDays: "j"
|
||||
widgets: "Widgets"
|
||||
_chat:
|
||||
invitations: "Inviter"
|
||||
noHistory: "Pas d'historique"
|
||||
|
|
|
@ -1264,6 +1264,7 @@ postForm: "Buat catatan"
|
|||
information: "Informasi"
|
||||
inMinutes: "menit"
|
||||
inDays: "hari"
|
||||
widgets: "Widget"
|
||||
_chat:
|
||||
invitations: "Undang"
|
||||
noHistory: "Tidak ada riwayat"
|
||||
|
|
|
@ -1354,6 +1354,10 @@ export interface Locale extends ILocale {
|
|||
* フォルダーを選択
|
||||
*/
|
||||
"selectFolder": string;
|
||||
/**
|
||||
* フォルダーの選択を解除
|
||||
*/
|
||||
"unselectFolder": string;
|
||||
/**
|
||||
* フォルダーを選択
|
||||
*/
|
||||
|
@ -1402,6 +1406,10 @@ export interface Locale extends ILocale {
|
|||
* フォルダーは空です
|
||||
*/
|
||||
"emptyFolder": string;
|
||||
/**
|
||||
* ここにファイルをドロップしてアップロード
|
||||
*/
|
||||
"dropHereToUpload": string;
|
||||
/**
|
||||
* 削除できません
|
||||
*/
|
||||
|
@ -5581,6 +5589,14 @@ export interface Locale extends ILocale {
|
|||
* ウィジェット
|
||||
*/
|
||||
"widgets": string;
|
||||
/**
|
||||
* デバイス情報
|
||||
*/
|
||||
"deviceInfo": string;
|
||||
/**
|
||||
* 技術的なお問い合わせの際に、以下の情報を併記すると問題の解決に役立つことがあります。
|
||||
*/
|
||||
"deviceInfoDescription": string;
|
||||
"_compression": {
|
||||
"_quality": {
|
||||
/**
|
||||
|
@ -9460,6 +9476,10 @@ export interface Locale extends ILocale {
|
|||
* アクセスを許可すると、自動で以下のURLに遷移します
|
||||
*/
|
||||
"byClickingYouWillBeRedirectedToThisUrl": string;
|
||||
/**
|
||||
* このアプリケーションは既にアクセスが許可されています。
|
||||
*/
|
||||
"alreadyAuthorized": string;
|
||||
};
|
||||
"_antennaSources": {
|
||||
/**
|
||||
|
|
|
@ -1389,6 +1389,7 @@ scheduleToPostOnX: "Pianificare la pubblicazione {x}"
|
|||
scheduledToPostOnX: "Pubblicazione pianificata {x}"
|
||||
schedule: "Pianificare"
|
||||
scheduled: "Pianificata"
|
||||
widgets: "Riquadri"
|
||||
_compression:
|
||||
_quality:
|
||||
high: "Alta qualità"
|
||||
|
|
|
@ -334,6 +334,7 @@ fileName: "ファイル名"
|
|||
selectFile: "ファイルを選択"
|
||||
selectFiles: "ファイルを選択"
|
||||
selectFolder: "フォルダーを選択"
|
||||
unselectFolder: "フォルダーの選択を解除"
|
||||
selectFolders: "フォルダーを選択"
|
||||
fileNotSelected: "ファイルが選択されていません"
|
||||
renameFile: "ファイル名を変更"
|
||||
|
@ -346,6 +347,7 @@ addFile: "ファイルを追加"
|
|||
showFile: "ファイルを表示"
|
||||
emptyDrive: "ドライブは空です"
|
||||
emptyFolder: "フォルダーは空です"
|
||||
dropHereToUpload: "ここにファイルをドロップしてアップロード"
|
||||
unableToDelete: "削除できません"
|
||||
inputNewFileName: "新しいファイル名を入力してください"
|
||||
inputNewDescription: "新しいキャプションを入力してください"
|
||||
|
@ -1390,6 +1392,8 @@ scheduledToPostOnX: "{x}に投稿が予約されています"
|
|||
schedule: "予約"
|
||||
scheduled: "予約"
|
||||
widgets: "ウィジェット"
|
||||
deviceInfo: "デバイス情報"
|
||||
deviceInfoDescription: "技術的なお問い合わせの際に、以下の情報を併記すると問題の解決に役立つことがあります。"
|
||||
|
||||
_compression:
|
||||
_quality:
|
||||
|
@ -2484,6 +2488,7 @@ _auth:
|
|||
scopeUser: "以下のユーザーとして操作しています"
|
||||
pleaseLogin: "アプリケーションにアクセス許可を与えるには、ログインが必要です。"
|
||||
byClickingYouWillBeRedirectedToThisUrl: "アクセスを許可すると、自動で以下のURLに遷移します"
|
||||
alreadyAuthorized: "このアプリケーションは既にアクセスが許可されています。"
|
||||
|
||||
_antennaSources:
|
||||
all: "全てのノート"
|
||||
|
|
|
@ -1337,6 +1337,7 @@ safeModeEnabled: "セーフモードがオンになってるで"
|
|||
pluginsAreDisabledBecauseSafeMode: "セーフモードがオンやから、プラグインは全部無効化されてるで。"
|
||||
customCssIsDisabledBecauseSafeMode: "セーフモードがオンやから、カスタムCSSは適用されてへんで。"
|
||||
themeIsDefaultBecauseSafeMode: "セーフモードがオンの間はデフォルトのテーマを使うで。セーフモードをオフにれば元に戻るで。"
|
||||
widgets: "ウィジェット"
|
||||
_chat:
|
||||
noMessagesYet: "まだメッセージはあらへんで"
|
||||
individualChat_description: "特定のユーザーと一対一でチャットができるで。"
|
||||
|
|
|
@ -1389,6 +1389,7 @@ scheduleToPostOnX: "{x}에 게시를 예약합니다."
|
|||
scheduledToPostOnX: "{x}에 게시가 예약돼있습니다."
|
||||
schedule: "예약"
|
||||
scheduled: "예약"
|
||||
widgets: "위젯"
|
||||
_compression:
|
||||
_quality:
|
||||
high: "고품질"
|
||||
|
|
|
@ -1042,6 +1042,7 @@ postForm: "Formularz tworzenia wpisu"
|
|||
information: "Informacje"
|
||||
inMinutes: "minuta"
|
||||
inDays: "dzień"
|
||||
widgets: "Widżety"
|
||||
_chat:
|
||||
invitations: "Zaproś"
|
||||
noHistory: "Brak historii"
|
||||
|
|
|
@ -1389,6 +1389,7 @@ scheduleToPostOnX: "Agendar nota para {x}"
|
|||
scheduledToPostOnX: "A nota está agendada para {x}"
|
||||
schedule: "Agendar"
|
||||
scheduled: "Agendado"
|
||||
widgets: "Widgets"
|
||||
_compression:
|
||||
_quality:
|
||||
high: "Qualidade alta"
|
||||
|
|
|
@ -1277,6 +1277,7 @@ textCount: "Количество символов"
|
|||
information: "Описание"
|
||||
inMinutes: "мин"
|
||||
inDays: "сут"
|
||||
widgets: "Виджеты"
|
||||
_chat:
|
||||
invitations: "Пригласить"
|
||||
noHistory: "История пока пуста"
|
||||
|
|
|
@ -915,6 +915,7 @@ postForm: "Napísať poznámku"
|
|||
information: "Informácie"
|
||||
inMinutes: "min"
|
||||
inDays: "dní"
|
||||
widgets: "Widgety"
|
||||
_chat:
|
||||
invitations: "Pozvať"
|
||||
noHistory: "Žiadna história"
|
||||
|
|
|
@ -1389,6 +1389,7 @@ scheduleToPostOnX: "กำหนดเวลาให้โพสต์ไว้
|
|||
scheduledToPostOnX: "มีการกำหนดเวลาให้โพสต์ไว้ที่ {x}"
|
||||
schedule: "กำหนดเวลา"
|
||||
scheduled: "กำหนดเวลา"
|
||||
widgets: "วิดเจ็ต"
|
||||
_compression:
|
||||
_quality:
|
||||
high: "คุณภาพสูง"
|
||||
|
|
|
@ -1378,6 +1378,7 @@ pluginsAreDisabledBecauseSafeMode: "Güvenli mod etkinleştirildiği için tüm
|
|||
customCssIsDisabledBecauseSafeMode: "Güvenli mod etkin olduğu için özel CSS uygulanmıyor."
|
||||
themeIsDefaultBecauseSafeMode: "Güvenli mod etkinken, varsayılan tema kullanılır. Güvenli modu devre dışı bırakmak bu değişiklikleri geri alır."
|
||||
thankYouForTestingBeta: "Beta sürümünü test ettiğin için teşekkür ederiz!"
|
||||
widgets: "Widget'lar"
|
||||
_order:
|
||||
newest: "Önce yeni"
|
||||
oldest: "Önce eski"
|
||||
|
|
|
@ -921,6 +921,7 @@ postForm: "Створення нотатки"
|
|||
information: "Інформація"
|
||||
inMinutes: "х"
|
||||
inDays: "д"
|
||||
widgets: "Віджети"
|
||||
_chat:
|
||||
invitations: "Запросити"
|
||||
noHistory: "Історія порожня"
|
||||
|
|
|
@ -1222,6 +1222,7 @@ migrateOldSettings: "Di chuyển cài đặt cũ"
|
|||
migrateOldSettings_description: "Thông thường, quá trình này diễn ra tự động, nhưng nếu vì lý do nào đó mà quá trình di chuyển không thành công, bạn có thể kích hoạt thủ công quy trình di chuyển, quá trình này sẽ ghi đè lên thông tin cấu hình hiện tại của bạn."
|
||||
inMinutes: "phút"
|
||||
inDays: "ngày"
|
||||
widgets: "Tiện ích"
|
||||
_chat:
|
||||
invitations: "Mời"
|
||||
noHistory: "Không có dữ liệu"
|
||||
|
|
|
@ -56,7 +56,7 @@ deleteAndEdit: "删除并编辑"
|
|||
deleteAndEditConfirm: "要删除此帖并再次编辑吗?对此帖的所有回应、转发和回复也将被删除。"
|
||||
addToList: "添加至列表"
|
||||
addToAntenna: "添加到天线"
|
||||
sendMessage: "发送"
|
||||
sendMessage: "发送消息"
|
||||
copyRSS: "复制RSS"
|
||||
copyUsername: "复制用户名"
|
||||
copyUserId: "复制用户 ID"
|
||||
|
@ -334,6 +334,7 @@ fileName: "文件名称"
|
|||
selectFile: "选择文件"
|
||||
selectFiles: "选择文件"
|
||||
selectFolder: "选择文件夹"
|
||||
unselectFolder: "取消全选文件夹"
|
||||
selectFolders: "选择多个文件夹"
|
||||
fileNotSelected: "未选择文件"
|
||||
renameFile: "重命名文件"
|
||||
|
@ -346,6 +347,7 @@ addFile: "添加文件"
|
|||
showFile: "显示文件"
|
||||
emptyDrive: "网盘中无文件"
|
||||
emptyFolder: "此文件夹中无文件"
|
||||
dropHereToUpload: "将文件拖动到这里来上传"
|
||||
unableToDelete: "无法删除"
|
||||
inputNewFileName: "请输入新文件名"
|
||||
inputNewDescription: "请输入新标题"
|
||||
|
@ -1389,6 +1391,9 @@ scheduleToPostOnX: "预定在 {x} 发出"
|
|||
scheduledToPostOnX: "已预定在 {x} 发出"
|
||||
schedule: "定时"
|
||||
scheduled: "定时"
|
||||
widgets: "小工具"
|
||||
deviceInfo: "设备信息"
|
||||
deviceInfoDescription: "咨询技术问题时,将以下信息一并发送有助于解决问题。"
|
||||
_compression:
|
||||
_quality:
|
||||
high: "高质量"
|
||||
|
@ -2431,6 +2436,7 @@ _auth:
|
|||
scopeUser: "以下面的用户进行操作"
|
||||
pleaseLogin: "在对应用进行授权许可之前,请先登录"
|
||||
byClickingYouWillBeRedirectedToThisUrl: "允许访问后将会自动重定向到以下 URL"
|
||||
alreadyAuthorized: "此应用已有访问许可。"
|
||||
_antennaSources:
|
||||
all: "所有帖子"
|
||||
homeTimeline: "已关注用户的帖子"
|
||||
|
@ -2697,6 +2703,8 @@ _notification:
|
|||
quote: "引用"
|
||||
reaction: "回应"
|
||||
pollEnded: "问卷调查结束"
|
||||
scheduledNotePosted: "定时发送成功"
|
||||
scheduledNotePostFailed: "定时发送失败"
|
||||
receiveFollowRequest: "收到关注请求"
|
||||
followRequestAccepted: "关注请求已通过"
|
||||
roleAssigned: "授予的角色"
|
||||
|
|
|
@ -1389,6 +1389,7 @@ scheduleToPostOnX: "排定在 {x} 發布"
|
|||
scheduledToPostOnX: "已排定在 {x} 發布貼文"
|
||||
schedule: "排定"
|
||||
scheduled: "排定"
|
||||
widgets: "小工具"
|
||||
_compression:
|
||||
_quality:
|
||||
high: "高品質"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "misskey",
|
||||
"version": "2025.10.0-beta.1",
|
||||
"version": "2025.10.0-beta.2",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -40,6 +40,7 @@ type TimelineOptions = {
|
|||
excludePureRenotes: boolean;
|
||||
ignoreAuthorFromUserSuspension?: boolean;
|
||||
dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>,
|
||||
preventEmptyTimelineDbFallback?: boolean;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
|
@ -73,12 +74,20 @@ export class FanoutTimelineEndpointService {
|
|||
|
||||
const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId);
|
||||
|
||||
// TODO: いい感じにgetMulti内でソート済だからuniqするときにredisResultが全てソート済なのを利用して再ソートを避けたい
|
||||
const redisResultIds = Array.from(new Set(redisResult.flat(1))).sort(idCompare);
|
||||
// オプション無効時、取得したredisResultのうち、2つ以上ソースがあり、1つでも空であればDBにフォールバックする
|
||||
let shouldFallbackToDb = ps.useDbFallback &&
|
||||
(ps.preventEmptyTimelineDbFallback !== true && redisResult.length > 1 && redisResult.some(ids => ids.length === 0));
|
||||
|
||||
// 取得したresultの中で最古のIDのうち、最も新しいものを取得
|
||||
const fttThresholdId = redisResult.map(ids => ids[0]).sort()[0];
|
||||
|
||||
// TODO: いい感じにgetMulti内でソート済だからuniqするときにredisResultが全てソート済なのを利用して再ソートを避けたい
|
||||
const redisResultIds = shouldFallbackToDb ? [] : Array.from(new Set(redisResult.flat(1))).sort(idCompare);
|
||||
|
||||
let noteIds = redisResultIds.filter(id => id >= fttThresholdId).slice(0, ps.limit);
|
||||
|
||||
let noteIds = redisResultIds.slice(0, ps.limit);
|
||||
const oldestNoteId = ascending ? redisResultIds[0] : redisResultIds[redisResultIds.length - 1];
|
||||
const shouldFallbackToDb = noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId;
|
||||
shouldFallbackToDb ||= ps.useDbFallback && (noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId);
|
||||
|
||||
if (!shouldFallbackToDb) {
|
||||
let filter = ps.noteFilter ?? (_note => true) as NoteFilter;
|
||||
|
|
|
@ -192,7 +192,7 @@ export const paramDef = {
|
|||
scheduledAt: { type: 'integer', nullable: true },
|
||||
isActuallyScheduled: { type: 'boolean', default: false },
|
||||
},
|
||||
required: ['visibility', 'visibleUserIds', 'cw', 'hashtag', 'localOnly', 'reactionAcceptance', 'replyId', 'renoteId', 'channelId', 'text', 'fileIds', 'poll', 'scheduledAt', 'isActuallyScheduled'],
|
||||
required: [],
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
|
@ -203,22 +203,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const draft = await this.noteDraftService.create(me, {
|
||||
fileIds: ps.fileIds,
|
||||
fileIds: ps.fileIds ?? [],
|
||||
pollChoices: ps.poll?.choices ?? [],
|
||||
pollMultiple: ps.poll?.multiple ?? false,
|
||||
pollExpiresAt: ps.poll?.expiresAt ? new Date(ps.poll.expiresAt) : null,
|
||||
pollExpiredAfter: ps.poll?.expiredAfter ?? null,
|
||||
hasPoll: ps.poll != null,
|
||||
text: ps.text,
|
||||
replyId: ps.replyId,
|
||||
renoteId: ps.renoteId,
|
||||
cw: ps.cw,
|
||||
hashtag: ps.hashtag,
|
||||
text: ps.text ?? null,
|
||||
replyId: ps.replyId ?? null,
|
||||
renoteId: ps.renoteId ?? null,
|
||||
cw: ps.cw ?? null,
|
||||
hashtag: ps.hashtag ?? null,
|
||||
localOnly: ps.localOnly,
|
||||
reactionAcceptance: ps.reactionAcceptance,
|
||||
visibility: ps.visibility,
|
||||
visibleUserIds: ps.visibleUserIds,
|
||||
channelId: ps.channelId,
|
||||
visibleUserIds: ps.visibleUserIds ?? [],
|
||||
channelId: ps.channelId ?? null,
|
||||
scheduledAt: ps.scheduledAt ? new Date(ps.scheduledAt) : null,
|
||||
isActuallyScheduled: ps.isActuallyScheduled,
|
||||
}).catch((err) => {
|
||||
|
|
|
@ -150,6 +150,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
withFiles: ps.withFiles,
|
||||
withRenotes: ps.withRenotes,
|
||||
}, me),
|
||||
preventEmptyTimelineDbFallback: true,
|
||||
});
|
||||
|
||||
return timeline;
|
||||
|
|
|
@ -5,7 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<div :class="[$style.codeBlockRoot, { [$style.codeEditor]: codeEditor }, (darkMode ? $style.dark : $style.light)]" v-html="html"></div>
|
||||
<div
|
||||
:class="[$style.codeBlockRoot, {
|
||||
[$style.codeEditor]: codeEditor,
|
||||
[$style.outerStyle]: !codeEditor && withOuterStyle,
|
||||
[$style.dark]: darkMode,
|
||||
[$style.light]: !darkMode,
|
||||
}]" v-html="html"></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
@ -15,11 +21,15 @@ import type { BundledLanguage } from 'shiki/langs';
|
|||
import { getHighlighter, getTheme } from '@/utility/code-highlighter.js';
|
||||
import { store } from '@/store.js';
|
||||
|
||||
const props = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
code: string;
|
||||
lang?: string;
|
||||
codeEditor?: boolean;
|
||||
}>();
|
||||
withOuterStyle?: boolean;
|
||||
}>(), {
|
||||
codeEditor: false,
|
||||
withOuterStyle: true,
|
||||
});
|
||||
|
||||
const highlighter = await getHighlighter();
|
||||
const darkMode = store.r.darkMode;
|
||||
|
@ -73,17 +83,13 @@ watch(() => props.lang, (to) => {
|
|||
|
||||
<style module lang="scss">
|
||||
.codeBlockRoot :global(.shiki) {
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
|
||||
|
||||
color: var(--shiki-fallback);
|
||||
background-color: var(--shiki-fallback-bg);
|
||||
|
||||
& span {
|
||||
color: var(--shiki-fallback);
|
||||
background-color: var(--shiki-fallback-bg);
|
||||
}
|
||||
|
||||
& pre,
|
||||
|
@ -92,26 +98,40 @@ watch(() => props.lang, (to) => {
|
|||
}
|
||||
}
|
||||
|
||||
.outerStyle.codeBlockRoot :global(.shiki) {
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--MI_THEME-divider);
|
||||
background-color: var(--shiki-fallback-bg);
|
||||
}
|
||||
|
||||
.light.codeBlockRoot :global(.shiki) {
|
||||
color: var(--shiki-light);
|
||||
background-color: var(--shiki-light-bg);
|
||||
|
||||
& span {
|
||||
color: var(--shiki-light);
|
||||
background-color: var(--shiki-light-bg);
|
||||
}
|
||||
}
|
||||
|
||||
.light.outerStyle.codeBlockRoot :global(.shiki),
|
||||
.light.codeEditor.codeBlockRoot :global(.shiki) {
|
||||
background-color: var(--shiki-light-bg);
|
||||
}
|
||||
|
||||
.dark.codeBlockRoot :global(.shiki) {
|
||||
color: var(--shiki-dark);
|
||||
background-color: var(--shiki-dark-bg);
|
||||
|
||||
& span {
|
||||
color: var(--shiki-dark);
|
||||
background-color: var(--shiki-dark-bg);
|
||||
}
|
||||
}
|
||||
|
||||
.dark.outerStyle.codeBlockRoot :global(.shiki),
|
||||
.dark.codeEditor.codeBlockRoot :global(.shiki) {
|
||||
background-color: var(--shiki-dark-bg);
|
||||
}
|
||||
|
||||
.codeBlockRoot.codeEditor {
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
@ -5,15 +5,32 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<template>
|
||||
<div :class="$style.codeBlockRoot">
|
||||
<button v-if="copyButton" :class="$style.codeBlockCopyButton" class="_button" @click="copy">
|
||||
<button v-if="copyButton" :class="[$style.codeBlockCopyButton, { [$style.withOuterStyle]: withOuterStyle }]" class="_button" @click="copy">
|
||||
<i class="ti ti-copy"></i>
|
||||
</button>
|
||||
<Suspense>
|
||||
<template #fallback>
|
||||
<MkLoading/>
|
||||
<pre
|
||||
class="_selectable"
|
||||
:class="[$style.codeBlockFallbackRoot, {
|
||||
[$style.outerStyle]: withOuterStyle,
|
||||
}]"
|
||||
><code :class="$style.codeBlockFallbackCode">Loading...</code></pre>
|
||||
</template>
|
||||
<XCode v-if="show && lang" class="_selectable" :code="code" :lang="lang"/>
|
||||
<pre v-else-if="show" class="_selectable" :class="$style.codeBlockFallbackRoot"><code :class="$style.codeBlockFallbackCode">{{ code }}</code></pre>
|
||||
<XCode
|
||||
v-if="show && lang"
|
||||
class="_selectable"
|
||||
:code="code"
|
||||
:lang="lang"
|
||||
:withOuterStyle="withOuterStyle"
|
||||
/>
|
||||
<pre
|
||||
v-else-if="show"
|
||||
class="_selectable"
|
||||
:class="[$style.codeBlockFallbackRoot, {
|
||||
[$style.outerStyle]: withOuterStyle,
|
||||
}]"
|
||||
><code :class="$style.codeBlockFallbackCode">{{ code }}</code></pre>
|
||||
<button v-else :class="$style.codePlaceholderRoot" @click="show = true">
|
||||
<div :class="$style.codePlaceholderContainer">
|
||||
<div><i class="ti ti-code"></i> {{ i18n.ts.code }}</div>
|
||||
|
@ -26,8 +43,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent, ref } from 'vue';
|
||||
import * as os from '@/os.js';
|
||||
import MkLoading from '@/components/global/MkLoading.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
|
@ -36,10 +51,12 @@ const props = withDefaults(defineProps<{
|
|||
code: string;
|
||||
forceShow?: boolean;
|
||||
copyButton?: boolean;
|
||||
withOuterStyle?: boolean;
|
||||
lang?: string;
|
||||
}>(), {
|
||||
copyButton: true,
|
||||
forceShow: false,
|
||||
withOuterStyle: true,
|
||||
});
|
||||
|
||||
const show = ref(props.forceShow === true ? true : !prefer.s.dataSaver.code);
|
||||
|
@ -58,9 +75,15 @@ function copy() {
|
|||
|
||||
.codeBlockCopyButton {
|
||||
position: absolute;
|
||||
opacity: 0.5;
|
||||
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
&.withOuterStyle {
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
|
@ -70,11 +93,17 @@ function copy() {
|
|||
.codeBlockFallbackRoot {
|
||||
display: block;
|
||||
overflow-wrap: anywhere;
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.outerStyle.codeBlockFallbackRoot {
|
||||
background: var(--MI_THEME-bg);
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--MI_THEME-divider);
|
||||
}
|
||||
|
||||
.codeBlockFallbackCode {
|
||||
font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
|
||||
}
|
||||
|
|
|
@ -35,18 +35,18 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div v-if="select === 'folder'">
|
||||
<template v-if="folder == null">
|
||||
<MkButton v-if="!isRootSelected" @click="isRootSelected = true">
|
||||
<i class="ti ti-square"></i> {{ i18n.ts.selectThisFolder }}
|
||||
<i class="ti ti-square"></i> {{ i18n.ts.selectFolder }}
|
||||
</MkButton>
|
||||
<MkButton v-else @click="isRootSelected = false">
|
||||
<i class="ti ti-checkbox"></i> {{ i18n.ts.unselectThisFolder }}
|
||||
<i class="ti ti-checkbox"></i> {{ i18n.ts.unselectFolder }}
|
||||
</MkButton>
|
||||
</template>
|
||||
<template v-else>
|
||||
<MkButton v-if="!selectedFolders.some(f => f.id === folder!.id)" @click="selectedFolders.push(folder)">
|
||||
<i class="ti ti-square"></i> {{ i18n.ts.selectThisFolder }}
|
||||
<i class="ti ti-square"></i> {{ i18n.ts.selectFolder }}
|
||||
</MkButton>
|
||||
<MkButton v-else @click="selectedFolders = selectedFolders.filter(f => f.id !== folder!.id)">
|
||||
<i class="ti ti-checkbox"></i> {{ i18n.ts.unselectThisFolder }}
|
||||
<i class="ti ti-checkbox"></i> {{ i18n.ts.unselectFolder }}
|
||||
</MkButton>
|
||||
</template>
|
||||
</div>
|
||||
|
@ -112,7 +112,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkButton v-show="filesPaginator.canFetchOlder.value" :class="$style.loadMore" primary rounded @click="filesPaginator.fetchOlder()">{{ i18n.ts.loadMore }}</MkButton>
|
||||
|
||||
<div v-if="filesPaginator.items.value.length == 0 && foldersPaginator.items.value.length == 0 && !fetching" :class="$style.empty">
|
||||
<div v-if="draghover">{{ i18n.ts['empty-draghover'] }}</div>
|
||||
<div v-if="draghover">{{ i18n.ts.dropHereToUpload }}</div>
|
||||
<div v-if="!draghover && folder == null"><strong>{{ i18n.ts.emptyDrive }}</strong></div>
|
||||
<div v-if="!draghover && folder != null">{{ i18n.ts.emptyFolder }}</div>
|
||||
</div>
|
||||
|
|
|
@ -96,7 +96,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, ref, useTemplateRef } from 'vue';
|
||||
import { nextTick, onMounted, ref, useTemplateRef, watch } from 'vue';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { getBgColor } from '@/utility/get-bg-color.js';
|
||||
import { pageFolderTeleportCount, popup } from '@/os.js';
|
||||
|
@ -119,6 +119,11 @@ const props = withDefaults(defineProps<{
|
|||
canPage: true,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'opened'): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const rootEl = useTemplateRef('rootEl');
|
||||
const asPage = props.canPage && deviceKind === 'smartphone' && prefer.s['experimental.enableFolderPageView'];
|
||||
const bgSame = ref(false);
|
||||
|
@ -164,7 +169,7 @@ function afterLeave(el: Element) {
|
|||
let pageId = pageFolderTeleportCount.value;
|
||||
pageFolderTeleportCount.value += 1000;
|
||||
|
||||
async function toggle() {
|
||||
async function toggle(ev: MouseEvent) {
|
||||
if (asPage && !opened.value) {
|
||||
pageId++;
|
||||
const { dispose } = await popup(MkFolderPage, {
|
||||
|
@ -192,6 +197,14 @@ onMounted(() => {
|
|||
const myBg = computedStyle.getPropertyValue('--MI_THEME-panel');
|
||||
bgSame.value = parentBg === myBg;
|
||||
});
|
||||
|
||||
watch(opened, (isOpened) => {
|
||||
if (isOpened) {
|
||||
emit('opened');
|
||||
} else {
|
||||
emit('closed');
|
||||
}
|
||||
}, { flush: 'post' });
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
|
|
@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.id }}</template>
|
||||
<template #key>{{ i18n.ts.name }}</template>
|
||||
<template #value>{{ name }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
|
|
|
@ -68,7 +68,7 @@ export type GetMkSelectValueTypesFromDef<T extends MkSelectItem[]> = T[number] e
|
|||
</script>
|
||||
|
||||
<script lang="ts" setup generic="const ITEMS extends MkSelectItem[], MODELT extends OptionValue">
|
||||
import { onMounted, nextTick, ref, watch, computed, toRefs } from 'vue';
|
||||
import { onMounted, nextTick, ref, watch, computed, toRefs, useTemplateRef } from 'vue';
|
||||
import { useInterval } from '@@/js/use-interval.js';
|
||||
import type { MenuItem } from '@/types/menu.js';
|
||||
import * as os from '@/os.js';
|
||||
|
@ -97,10 +97,10 @@ const { autofocus } = toRefs(props);
|
|||
const focused = ref(false);
|
||||
const opening = ref(false);
|
||||
const currentValueText = ref<string | null>(null);
|
||||
const inputEl = ref<HTMLObjectElement | null>(null);
|
||||
const prefixEl = ref<HTMLElement | null>(null);
|
||||
const suffixEl = ref<HTMLElement | null>(null);
|
||||
const container = ref<HTMLElement | null>(null);
|
||||
const inputEl = useTemplateRef('inputEl');
|
||||
const prefixEl = useTemplateRef('prefixEl');
|
||||
const suffixEl = useTemplateRef('suffixEl');
|
||||
const container = useTemplateRef('container');
|
||||
const height =
|
||||
props.small ? 33 :
|
||||
props.large ? 39 :
|
||||
|
|
|
@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
</MkFoldableSection>
|
||||
|
||||
<MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category ?? '___root___'">
|
||||
<MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category ?? '___root___'" :expanded="false">
|
||||
<template #header>{{ category || i18n.ts.other }}</template>
|
||||
<div :class="$style.emojis">
|
||||
<XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji"/>
|
||||
|
|
|
@ -151,7 +151,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
|
||||
<div v-else-if="tab === 'announcements'" class="_gaps">
|
||||
<MkButton primary rounded @click="createAnnouncement"><i class="ti ti-plus"></i> {{ i18n.ts.new }}</MkButton>
|
||||
<MkButton primary rounded @click="createAnnouncement"><i class="ti ti-plus"></i> {{ i18n.ts.createNew }}</MkButton>
|
||||
|
||||
<MkSelect v-model="announcementsStatus" :items="announcementsStatusDef">
|
||||
<template #label>{{ i18n.ts.filter }}</template>
|
||||
|
|
|
@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<h1>{{ i18n.ts._auth.denied }}</h1>
|
||||
</div>
|
||||
<div v-if="state == 'accepted' && session">
|
||||
<h1>{{ session.app.isAuthorized ? i18n.ts['already-authorized'] : i18n.ts.allowed }}</h1>
|
||||
<h1>{{ session.app.isAuthorized ? i18n.ts._auth.alreadyAuthorized : i18n.ts._auth.accepted }}</h1>
|
||||
<p v-if="session.app.callbackUrl">
|
||||
{{ i18n.ts._auth.callback }}
|
||||
<MkEllipsis/>
|
||||
|
|
|
@ -28,17 +28,37 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
<MkFolder @opened="onOpened">
|
||||
<template #icon><i class="ti ti-report-search"></i></template>
|
||||
<template #label>{{ i18n.ts.deviceInfo }}</template>
|
||||
<template #caption>{{ i18n.ts.deviceInfoDescription }}</template>
|
||||
<MkLoading v-if="userEnv == null" />
|
||||
<MkCode v-else lang="json" :code="JSON.stringify(userEnv, null, 2)" style="max-height: 300px; overflow: auto;"/>
|
||||
</MkFolder>
|
||||
</div>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { getUserEnvironment } from '@/utility/get-user-environment.js';
|
||||
import type { UserEnvironment } from '@/utility/get-user-environment.js';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkLink from '@/components/MkLink.vue';
|
||||
import MkCode from '@/components/MkCode.vue';
|
||||
|
||||
const userEnv = ref<UserEnvironment | null>(null);
|
||||
|
||||
async function onOpened() {
|
||||
if (userEnv.value == null) {
|
||||
userEnv.value = await getUserEnvironment();
|
||||
}
|
||||
}
|
||||
|
||||
definePage(() => ({
|
||||
title: i18n.ts.inquiry,
|
||||
|
|
|
@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkButton primary @click="createKey">{{ i18n.ts._registry.createKey }}</MkButton>
|
||||
|
||||
<FormSection v-if="keys">
|
||||
<template #label>{{ i18n.ts.keys }}</template>
|
||||
<template #label>{{ i18n.ts._registry.keys }}</template>
|
||||
<div class="_gaps_s">
|
||||
<FormLink v-for="key in keys" :to="`/registry/value/${props.domain}/${scope.join('/')}/${key[0]}`" class="_monospace">{{ key[0] }}<template #suffix>{{ key[1].toUpperCase() }}</template></FormLink>
|
||||
</div>
|
||||
|
|
|
@ -159,7 +159,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent, computed, onMounted, onUnmounted, onActivated, onDeactivated, nextTick, watch, ref } from 'vue';
|
||||
import { defineAsyncComponent, computed, onMounted, onUnmounted, onActivated, onDeactivated, nextTick, watch, ref, useTemplateRef } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { getScrollContainer } from '@@/js/scroll.js';
|
||||
import MkNote from '@/components/MkNote.vue';
|
||||
|
@ -222,9 +222,9 @@ const router = useRouter();
|
|||
|
||||
const user = ref(props.user);
|
||||
const narrow = ref<null | boolean>(null);
|
||||
const rootEl = ref<null | HTMLElement>(null);
|
||||
const bannerEl = ref<null | HTMLElement>(null);
|
||||
const memoTextareaEl = ref<null | HTMLElement>(null);
|
||||
const rootEl = useTemplateRef('rootEl');
|
||||
const bannerEl = useTemplateRef('bannerEl');
|
||||
const memoTextareaEl = useTemplateRef('memoTextareaEl');
|
||||
const memoDraft = ref(props.user.memo);
|
||||
const isEditingMemo = ref(false);
|
||||
const moderationNote = ref(props.user.moderationNote ?? '');
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export type UserEnvironment = {
|
||||
os: string;
|
||||
browser: string;
|
||||
userAgent: string;
|
||||
screenWidth: number;
|
||||
screenHeight: number;
|
||||
viaGetHighEntropyValues: true;
|
||||
} | {
|
||||
userAgent: string;
|
||||
screenWidth: number;
|
||||
screenHeight: number;
|
||||
viaGetHighEntropyValues: false;
|
||||
};
|
||||
|
||||
export async function getUserEnvironment(): Promise<UserEnvironment> {
|
||||
if ('userAgentData' in navigator && navigator.userAgentData != null) {
|
||||
try {
|
||||
const uaData: any = await navigator.userAgentData.getHighEntropyValues([
|
||||
'fullVersionList',
|
||||
'platformVersion',
|
||||
]);
|
||||
|
||||
let osVersion = 'v' + uaData.platformVersion;
|
||||
|
||||
if (uaData.platform === 'Windows' && uaData.platformVersion != null) {
|
||||
// https://learn.microsoft.com/ja-jp/microsoft-edge/web-platform/how-to-detect-win11
|
||||
const majorPlatformVersion = parseInt(uaData.platformVersion.split('.')[0]);
|
||||
if (majorPlatformVersion >= 13) {
|
||||
osVersion = '11 or later';
|
||||
} else if (majorPlatformVersion > 0) {
|
||||
osVersion = '10';
|
||||
} else {
|
||||
osVersion = '8.1 or earlier';
|
||||
}
|
||||
}
|
||||
|
||||
const browserData = uaData.fullVersionList.find((item) => !/^\s*not.+a.+brand\s*$/i.test(item.brand));
|
||||
return {
|
||||
os: `${uaData.platform} ${osVersion}`,
|
||||
browser: browserData ? `${browserData.brand} v${browserData.version}` : 'Unknown',
|
||||
userAgent: navigator.userAgent,
|
||||
screenWidth: window.innerWidth,
|
||||
screenHeight: window.innerHeight,
|
||||
viaGetHighEntropyValues: true,
|
||||
};
|
||||
} catch {
|
||||
return getViaUa();
|
||||
}
|
||||
} else {
|
||||
return getViaUa();
|
||||
}
|
||||
}
|
||||
|
||||
function getViaUa(): UserEnvironment {
|
||||
return {
|
||||
userAgent: navigator.userAgent,
|
||||
screenWidth: window.innerWidth,
|
||||
screenHeight: window.innerHeight,
|
||||
viaGetHighEntropyValues: false,
|
||||
};
|
||||
}
|
|
@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<p v-if="widgetProps.folderId == null">
|
||||
{{ i18n.ts.folder }}
|
||||
</p>
|
||||
<p v-if="widgetProps.folderId != null && images.length === 0 && !fetching">{{ i18n.ts['no-image'] }}</p>
|
||||
<p v-if="widgetProps.folderId != null && images.length === 0 && !fetching">{{ i18n.ts.nothing }}</p>
|
||||
<div ref="slideA" class="slide a"></div>
|
||||
<div ref="slideB" class="slide b"></div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"type": "module",
|
||||
"name": "misskey-js",
|
||||
"version": "2025.10.0-beta.1",
|
||||
"version": "2025.10.0-beta.2",
|
||||
"description": "Misskey SDK for JavaScript",
|
||||
"license": "MIT",
|
||||
"main": "./built/index.js",
|
||||
|
|
|
@ -29205,34 +29205,34 @@ export interface operations {
|
|||
* @default public
|
||||
* @enum {string}
|
||||
*/
|
||||
visibility: 'public' | 'home' | 'followers' | 'specified';
|
||||
visibleUserIds: string[];
|
||||
cw: string | null;
|
||||
hashtag: string | null;
|
||||
visibility?: 'public' | 'home' | 'followers' | 'specified';
|
||||
visibleUserIds?: string[];
|
||||
cw?: string | null;
|
||||
hashtag?: string | null;
|
||||
/** @default false */
|
||||
localOnly: boolean;
|
||||
localOnly?: boolean;
|
||||
/**
|
||||
* @default null
|
||||
* @enum {string|null}
|
||||
*/
|
||||
reactionAcceptance: null | 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote';
|
||||
reactionAcceptance?: null | 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote';
|
||||
/** Format: misskey:id */
|
||||
replyId: string | null;
|
||||
replyId?: string | null;
|
||||
/** Format: misskey:id */
|
||||
renoteId: string | null;
|
||||
renoteId?: string | null;
|
||||
/** Format: misskey:id */
|
||||
channelId: string | null;
|
||||
text: string | null;
|
||||
fileIds: string[];
|
||||
poll: {
|
||||
channelId?: string | null;
|
||||
text?: string | null;
|
||||
fileIds?: string[];
|
||||
poll?: {
|
||||
choices: string[];
|
||||
multiple?: boolean;
|
||||
expiresAt?: number | null;
|
||||
expiredAfter?: number | null;
|
||||
} | null;
|
||||
scheduledAt: number | null;
|
||||
scheduledAt?: number | null;
|
||||
/** @default false */
|
||||
isActuallyScheduled: boolean;
|
||||
isActuallyScheduled?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
869
pnpm-lock.yaml
869
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue