diff --git a/CHANGELOG.md b/CHANGELOG.md index 91d4edf120..d8129bf7e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ You should also include the user name that made the change. --> +## 12.114.0 (2022/07/15) + +### Improvements +- RSSティッカーで表示順序をシャッフルできるように @syuilo + +### Bugfixes +- クライアントが起動しなくなることがある問題を修正 @syuilo + ## 12.113.0 (2022/07/13) ### Improvements diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 1289fba44d..992eeb0f50 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -808,6 +808,7 @@ reverse: "اقلب" colored: "ملوّن" label: "التسمية" localOnly: "المحلي فقط" +account: "الحسابات" _emailUnavailable: used: "هذا البريد الإلكتروني مستخدم" format: "صيغة البريد الإلكتروني غير صالحة" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index 6017566bb0..f6547646cf 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -848,6 +848,7 @@ reverse: "উল্টান" colored: "রঙ্গিন" label: "লেবেল" localOnly: "শুধুমাত্র লোকাল" +account: "অ্যাকাউন্টগুলি" _emailUnavailable: used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে" format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index dfa65198be..b7941cbaf1 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -887,6 +887,9 @@ beta: "Beta" enableAutoSensitive: "NSFW-Automarkierung" enableAutoSensitiveDescription: "Setzt soweit möglich durch Verwendung von Machine Learning automatisch NSFW-Markierungen für Medien, die NSFW-Anteile beinhalten. Auch wenn du diese Option deaktiviert hast, ist sie möglicherweise auf Instanzebene aktiviert." activeEmailValidationDescription: "Aktivert strengere Überprüfung von E-Mail-Adressen, d.h. Testen auf Wegwerfadressen und darauf, ob mit der Adresse tatsächlich kommuniziert werden kann. Ist dies deaktiviert, so wird nur das Format der E-Mail überprüft." +navbar: "Navigationsleiste" +shuffle: "Mischen" +account: "Benutzerkonten" _sensitiveMediaDetection: description: "Ermöglicht eine Erleichterung der Servermoderation durch die automatische Erkennungen von NSFW-Medien unter Verwendung von Machine Learning. Hierdurch wird die Serverlast etwas erhöht." sensitivity: "Erkennungssensitivität" diff --git a/locales/en-US.yml b/locales/en-US.yml index f0a9924cd7..520b541292 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -887,6 +887,9 @@ beta: "Beta" enableAutoSensitive: "Automatic NSFW-Marking" enableAutoSensitiveDescription: "Allows automatic detection and marking of NSFW media through Machine Learning where possible. Even if this option is disabled, it may be enabled instance-wide." activeEmailValidationDescription: "Enables stricter validation of email addresses, which includes checking for disposable addresses and by whether it can actually be communicated with. When unchecked, only the format of the email is validated." +navbar: "Navigation bar" +shuffle: "Shuffle" +account: "Accounts" _sensitiveMediaDetection: description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server." sensitivity: "Detection sensitivity" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index a8f6aaa79d..8383864ede 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -865,6 +865,7 @@ reverse: "Echar de un capirotazo" colored: "Color" label: "Etiqueta" localOnly: "Solo local" +account: "Cuentas" _emailUnavailable: used: "Ya fue usado" format: "Formato no válido." diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index c5f803bb88..b14d3d14b6 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -843,6 +843,7 @@ reverse: "Inverser" colored: "Coloré" label: "Étiquette" localOnly: "Local seulement" +account: "Comptes" _emailUnavailable: used: "Non disponible" format: "Le format de cette adresse de courriel est invalide" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index d543fae862..315b690b0f 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -852,6 +852,7 @@ reverse: "Balik" colored: "Diwarnai" label: "Label" localOnly: "Hanya lokal" +account: "Akun" _emailUnavailable: used: "Alamat surel ini telah digunakan" format: "Format tidak valid." diff --git a/locales/it-IT.yml b/locales/it-IT.yml index eb7478dd51..ac14bed133 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -814,6 +814,7 @@ reverse: "Inverti" colored: "Colorato" label: "Etichetta" localOnly: "Soltanto locale" +account: "Account" _emailUnavailable: used: "Email già in uso" format: "Formato email non valido" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0e278bead6..c797c0d55e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -887,6 +887,9 @@ beta: "ベータ" enableAutoSensitive: "自動NSFW判定" enableAutoSensitiveDescription: "利用可能な場合は、機械学習を利用して自動でメディアにNSFWフラグを設定します。この機能をオフにしても、インスタンスによっては自動で設定されることがあります。" activeEmailValidationDescription: "ユーザーのメールアドレスのバリデーションを、捨てアドかどうかや実際に通信可能かどうかなどを判定しより積極的に行います。オフにすると単に文字列として正しいかどうかのみチェックされます。" +navbar: "ナビゲーションバー" +shuffle: "シャッフル" +account: "アカウント" _sensitiveMediaDetection: description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" diff --git a/locales/kab-KAB.yml b/locales/kab-KAB.yml index f0297c66a0..29eca64c7a 100644 --- a/locales/kab-KAB.yml +++ b/locales/kab-KAB.yml @@ -57,6 +57,7 @@ selectAccount: "Fren amiḍan" accounts: "Imiḍan" searchByGoogle: "Nadi" file: "Ifuyla" +account: "Imiḍan" _email: _follow: title: "Yeṭṭafaṛ-ik·em-id" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 781de61062..2ba05505c5 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -886,6 +886,7 @@ beta: "베타" enableAutoSensitive: "자동 NSFW 탐지" enableAutoSensitiveDescription: "이용 가능할 경우 기계학습을 통해 자동으로 미디어 NSFW를 설정합니다. 이 기능을 해제하더라도, 인스턴스 정책에 따라 자동으로 설정될 수 있습니다." activeEmailValidationDescription: "유저가 입력한 메일 주소가 일회용 메일인지, 실제로 통신할 수 있는 지 엄격하게 검사합니다. 해제할 경우 이메일 형식에 대해서만 검사합니다." +account: "계정" _sensitiveMediaDetection: description: "기계학습을 통해 자동으로 민감한 미디어를 탐지하여, 모더레이션에 참고할 수 있도록 합니다. 서버의 부하를 약간 증가시킵니다." sensitivity: "탐지 민감도" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index af7d48ea75..41ba8fdd35 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -764,6 +764,7 @@ file: "Pliki" reverse: "Odwróć" colored: "Kolorowe" label: "Etykieta" +account: "Konta" _ffVisibility: public: "Publikuj" _ad: diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index c49cbe1388..4386e1c87c 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -842,6 +842,7 @@ reverse: "Переворот" colored: "Выделена цветом" label: "Метка" localOnly: "Локально" +account: "Учётные записи" _sensitiveMediaDetection: description: "Машинное обучение может быть использовано для автоматического обнаружения чувствительных медиа для модерации. Нагрузка на сервер увеличивается незначительно." setSensitiveFlagAutomatically: "Установить флаг NSFW" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index a5641e68f4..c5a5d6d0b1 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -883,6 +883,8 @@ beta: "Beta" enableAutoSensitive: "Automatická detekcia NSFW" enableAutoSensitiveDescription: "Ak je zapnuté, príznak NSFW sa na médiách automaticky nastaví pomocou strojového učenia. Aj keď je táto funkcia vypnutá, v niektorých prípadoch sa môže nastaviť automaticky." activeEmailValidationDescription: "Dôkladnejšie overí e-mailovú adresu používateľa tým, že zistí, či ide o vyradenú e-mailovú adresu a či sa s ňou dá skutočne komunikovať. Ak nie je začiarknuté, e-mailová adresa sa kontroluje len ako text." +navbar: "Navigačný panel" +account: "Účty" _sensitiveMediaDetection: description: "Strojové učenie sa použije na automatickú detekciu citlivých médií na účely ich moderovania. Mierne sa zvýši zaťaženie servera." sensitivity: "Citlivosť detekcie" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index 1170185db4..0c695ff5b4 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -822,6 +822,7 @@ deleteAccountConfirm: "การดำเนินการนี้จะลบ incorrectPassword: "รหัสผ่านไม่ถูกต้อง" searchByGoogle: "ค้นหา" file: "ไฟล์" +account: "บัญชีผู้ใช้" _ffVisibility: public: "เผยแพร่" _ad: diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index 0680654879..2384915505 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -887,6 +887,9 @@ beta: "Beta" enableAutoSensitive: "Tự động đánh dấu NSFW" enableAutoSensitiveDescription: "Cho phép tự động phát hiện và đánh dấu media NSFW thông qua học máy, nếu có thể. Ngay cả khi tùy chọn này bị tắt, nó vẫn có thể được bật trên toàn máy chủ." activeEmailValidationDescription: "Cho phép xác minh địa chỉ email chặt chẽ hơn, bao gồm việc kiểm tra các địa chỉ dùng một lần và xem nó có thực sự được giao tiếp hay không. Khi bỏ chọn, chỉ định dạng của email được xác minh." +navbar: "Thanh điều hướng" +shuffle: "Xáo trộn" +account: "Tài khoản của bạn" _sensitiveMediaDetection: description: "Giảm nỗ lực kiểm duyệt máy chủ thông qua việc tự động nhận dạng media NSFW thông qua học máy. Điều này sẽ làm tăng một chút áp lực trên máy chủ." sensitivity: "Phát hiện nhạy cảm" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 26f745e65e..cb97a1b685 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -878,6 +878,7 @@ cannotUploadBecauseInappropriate: "因为可能含有不适宜的内容,无法 cannotUploadBecauseNoFreeSpace: "因为已无可用空间,无法上传。" beta: "测试" enableAutoSensitive: "自动 NSFW 识别" +account: "账户" _sensitiveMediaDetection: description: "可以使用机器学习技术自动检测敏感媒体,以便进行审核。服务器负载将略微增加。" sensitivity: "检测敏感度" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 945080ee44..0359b03b23 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -887,6 +887,8 @@ beta: "Beta" enableAutoSensitive: "自動NSFW判定" enableAutoSensitiveDescription: "如果可用,請利用機器學習在媒體上自動設置 NSFW 旗標。 即使關閉此功能,依實例而定也可能會自動設置。" activeEmailValidationDescription: "積極地驗證用戶的電子郵件地址,判斷它是否為免洗地址,或者它是否可以通信。 若關閉,則只會檢查字元是否正確。" +navbar: "導覽列" +account: "帳戶" _sensitiveMediaDetection: description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。" sensitivity: "檢測敏感度" diff --git a/package.json b/package.json index 93df550f84..24cf9a6182 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "12.113.0", + "version": "12.114.0", "codename": "indigo", "repository": { "type": "git", diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index 0a5cc0e0dc..9570115423 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -14,9 +14,11 @@ // ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので (async () => { window.onerror = (e) => { + console.error(e); renderError('SOMETHING_HAPPENED', e); }; window.onunhandledrejection = (e) => { + console.error(e); renderError('SOMETHING_HAPPENED_IN_PROMISE', e); }; @@ -47,18 +49,30 @@ localStorage.setItem('localeVersion', v); } else { await checkUpdate(); - renderError('LOCALE_FETCH_FAILED'); + renderError('LOCALE_FETCH'); return; } } //#endregion //#region Script - import(`/assets/${CLIENT_ENTRY}`) - .catch(async e => { - await checkUpdate(); - renderError('APP_FETCH_FAILED', e); - }) + function importAppScript() { + import(`/assets/${CLIENT_ENTRY}`) + .catch(async e => { + await checkUpdate(); + console.error(e); + renderError('APP_IMPORT', e); + }); + } + + // タイミングによっては、この時点でDOMの構築が済んでいる場合とそうでない場合とがある + if (document.readyState !== 'loading') { + importAppScript(); + } else { + window.addEventListener('DOMContentLoaded', () => { + importAppScript(); + }); + } //#endregion //#region Theme @@ -112,35 +126,35 @@ let errorsElement = document.getElementById('errors'); if (!errorsElement) { - document.documentElement.innerHTML = ` + document.body.innerHTML = ` - - - + + +

An error has occurred!

-

Don't worry, it's (probably) not your fault.

+

Don't worry, it's (probably) not your fault.

If the problem persists after refreshing, please contact your instance's administrator.
You may also try the following options:

- - - + + +
- - - + + +
- - - + + +
`; @@ -269,17 +283,22 @@ // eslint-disable-next-line no-inner-declarations async function checkUpdate() { - // TODO: サーバーが落ちている場合などのエラーハンドリング - const res = await fetch('/api/meta', { - method: 'POST', - cache: 'no-cache' - }); + try { + const res = await fetch('/api/meta', { + method: 'POST', + cache: 'no-cache' + }); - const meta = await res.json(); + const meta = await res.json(); - if (meta.version != v) { - localStorage.setItem('v', meta.version); - refresh(); + if (meta.version != v) { + localStorage.setItem('v', meta.version); + refresh(); + } + } catch (e) { + console.error(e); + renderError('UPDATE_CHECK', e); + throw e; } } diff --git a/packages/client/package.json b/packages/client/package.json index 8d2efd0b92..94f6688dd2 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -56,7 +56,6 @@ "random-seed": "0.3.0", "reflect-metadata": "0.1.13", "rndstr": "1.0.0", - "rollup": "2.76.0", "s-age": "1.1.2", "sass": "1.53.0", "seedrandom": "3.0.5", @@ -102,6 +101,7 @@ "@types/ws": "8.5.3", "@typescript-eslint/eslint-plugin": "5.30.6", "@typescript-eslint/parser": "5.30.6", + "rollup": "2.76.0", "cross-env": "7.0.3", "cypress": "10.3.0", "eslint": "8.19.0", diff --git a/packages/client/src/components/drive.vue b/packages/client/src/components/drive.vue index 6c2c8acad0..9e2ef1b930 100644 --- a/packages/client/src/components/drive.vue +++ b/packages/client/src/components/drive.vue @@ -26,7 +26,8 @@ -
entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles() + (entries) => entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles(), ); watch(folder, () => emit('cd', folder.value)); @@ -232,7 +233,7 @@ function onDrop(ev: DragEvent): any { removeFile(file.id); os.api('drive/files/update', { fileId: file.id, - folderId: folder.value ? folder.value.id : null + folderId: folder.value ? folder.value.id : null, }); } //#endregion @@ -248,7 +249,7 @@ function onDrop(ev: DragEvent): any { removeFolder(droppedFolder.id); os.api('drive/folders/update', { folderId: droppedFolder.id, - parentId: folder.value ? folder.value.id : null + parentId: folder.value ? folder.value.id : null, }).then(() => { // noop }).catch(err => { @@ -256,13 +257,13 @@ function onDrop(ev: DragEvent): any { case 'detected-circular-definition': os.alert({ title: i18n.ts.unableToProcess, - text: i18n.ts.circularReferenceFolder + text: i18n.ts.circularReferenceFolder, }); break; default: os.alert({ type: 'error', - text: i18n.ts.somethingHappened + text: i18n.ts.somethingHappened, }); } }); @@ -278,17 +279,17 @@ function urlUpload() { os.inputText({ title: i18n.ts.uploadFromUrl, type: 'url', - placeholder: i18n.ts.uploadFromUrlDescription + placeholder: i18n.ts.uploadFromUrlDescription, }).then(({ canceled, result: url }) => { if (canceled || !url) return; os.api('drive/files/upload-from-url', { url: url, - folderId: folder.value ? folder.value.id : undefined + folderId: folder.value ? folder.value.id : undefined, }); os.alert({ title: i18n.ts.uploadFromUrlRequested, - text: i18n.ts.uploadFromUrlMayTakeTime + text: i18n.ts.uploadFromUrlMayTakeTime, }); }); } @@ -296,12 +297,12 @@ function urlUpload() { function createFolder() { os.inputText({ title: i18n.ts.createFolder, - placeholder: i18n.ts.folderName + placeholder: i18n.ts.folderName, }).then(({ canceled, result: name }) => { if (canceled) return; os.api('drive/folders/create', { name: name, - parentId: folder.value ? folder.value.id : undefined + parentId: folder.value ? folder.value.id : undefined, }).then(createdFolder => { addFolder(createdFolder, true); }); @@ -312,12 +313,12 @@ function renameFolder(folderToRename: Misskey.entities.DriveFolder) { os.inputText({ title: i18n.ts.renameFolder, placeholder: i18n.ts.inputNewFolderName, - default: folderToRename.name + default: folderToRename.name, }).then(({ canceled, result: name }) => { if (canceled) return; os.api('drive/folders/update', { folderId: folderToRename.id, - name: name + name: name, }).then(updatedFolder => { // FIXME: 画面を更新するために自分自身に移動 move(updatedFolder); @@ -327,7 +328,7 @@ function renameFolder(folderToRename: Misskey.entities.DriveFolder) { function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) { os.api('drive/folders/delete', { - folderId: folderToDelete.id + folderId: folderToDelete.id, }).then(() => { // 削除時に親フォルダに移動 move(folderToDelete.parentId); @@ -337,15 +338,15 @@ function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) { os.alert({ type: 'error', title: i18n.ts.unableToDelete, - text: i18n.ts.hasChildFilesOrFolders + text: i18n.ts.hasChildFilesOrFolders, }); break; default: os.alert({ type: 'error', - text: i18n.ts.unableToDelete + text: i18n.ts.unableToDelete, }); - } + } }); } @@ -411,7 +412,7 @@ function move(target?: Misskey.entities.DriveFolder) { fetching.value = true; os.api('drive/folders/show', { - folderId: target + folderId: target, }).then(folderToMove => { folder.value = folderToMove; hierarchyFolders.value = []; @@ -510,7 +511,7 @@ async function fetch() { const foldersPromise = os.api('drive/folders', { folderId: folder.value ? folder.value.id : null, - limit: foldersMax + 1 + limit: foldersMax + 1, }).then(fetchedFolders => { if (fetchedFolders.length === foldersMax + 1) { moreFolders.value = true; @@ -522,7 +523,7 @@ async function fetch() { const filesPromise = os.api('drive/files', { folderId: folder.value ? folder.value.id : null, type: props.type, - limit: filesMax + 1 + limit: filesMax + 1, }).then(fetchedFiles => { if (fetchedFiles.length === filesMax + 1) { moreFiles.value = true; @@ -549,7 +550,7 @@ function fetchMoreFiles() { folderId: folder.value ? folder.value.id : null, type: props.type, untilId: files.value[files.value.length - 1].id, - limit: max + 1 + limit: max + 1, }).then(files => { if (files.length === max + 1) { moreFiles.value = true; @@ -569,30 +570,30 @@ function getMenu() { ref: keepOriginal, }, null, { text: i18n.ts.addFile, - type: 'label' + type: 'label', }, { text: i18n.ts.upload, icon: 'fas fa-upload', - action: () => { selectLocalFile(); } + action: () => { selectLocalFile(); }, }, { text: i18n.ts.fromUrl, icon: 'fas fa-link', - action: () => { urlUpload(); } + action: () => { urlUpload(); }, }, null, { text: folder.value ? folder.value.name : i18n.ts.drive, - type: 'label' + type: 'label', }, folder.value ? { text: i18n.ts.renameFolder, icon: 'fas fa-i-cursor', - action: () => { renameFolder(folder.value); } + action: () => { renameFolder(folder.value); }, } : undefined, folder.value ? { text: i18n.ts.deleteFolder, icon: 'fas fa-trash-alt', - action: () => { deleteFolder(folder.value as Misskey.entities.DriveFolder); } + action: () => { deleteFolder(folder.value as Misskey.entities.DriveFolder); }, } : undefined, { text: i18n.ts.createFolder, icon: 'fas fa-folder-plus', - action: () => { createFolder(); } + action: () => { createFolder(); }, }]; } @@ -662,14 +663,14 @@ onBeforeUnmount(() => { > .path { display: inline-block; vertical-align: bottom; - line-height: 50px; + line-height: 42px; white-space: nowrap; > * { display: inline-block; margin: 0; padding: 0 8px; - line-height: 50px; + line-height: 42px; cursor: pointer; * { diff --git a/packages/client/src/components/global/loading.vue b/packages/client/src/components/global/loading.vue index 5a7e362fcf..bcc6dfac01 100644 --- a/packages/client/src/components/global/loading.vue +++ b/packages/client/src/components/global/loading.vue @@ -16,9 +16,7 @@ diff --git a/packages/client/src/scripts/shuffle.ts b/packages/client/src/scripts/shuffle.ts new file mode 100644 index 0000000000..05e6cdfbcf --- /dev/null +++ b/packages/client/src/scripts/shuffle.ts @@ -0,0 +1,19 @@ +/** + * 配列をシャッフル (破壊的) + */ +export function shuffle(array: T): T { + let currentIndex = array.length, randomIndex; + + // While there remain elements to shuffle. + while (currentIndex !== 0) { + // Pick a remaining element. + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + + // And swap it with the current element. + [array[currentIndex], array[randomIndex]] = [ + array[randomIndex], array[currentIndex]]; + } + + return array; +} diff --git a/packages/client/src/style.scss b/packages/client/src/style.scss index 0f892d2e19..27e33702ad 100644 --- a/packages/client/src/style.scss +++ b/packages/client/src/style.scss @@ -30,7 +30,7 @@ html { overflow: auto; overflow-wrap: break-word; font-family: "BIZ UDGothic", Roboto, HelveticaNeue, Arial, sans-serif; - font-size: 15px; + font-size: 14px; line-height: 1.35; text-size-adjust: 100%; tab-size: 2; @@ -61,16 +61,16 @@ html { } } - &.f-small { - font-size: 0.9em; + &.f-1 { + font-size: 15px; } - &.f-large { - font-size: 1.1em; + &.f-2 { + font-size: 16px; } - &.f-veryLarge { - font-size: 1.2em; + &.f-3 { + font-size: 17px; } &.useSystemFont { diff --git a/packages/client/src/ui/_common_/navbar-for-mobile.vue b/packages/client/src/ui/_common_/navbar-for-mobile.vue new file mode 100644 index 0000000000..cae1d25304 --- /dev/null +++ b/packages/client/src/ui/_common_/navbar-for-mobile.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/packages/client/src/ui/_common_/navbar.vue b/packages/client/src/ui/_common_/navbar.vue new file mode 100644 index 0000000000..fbac8425d7 --- /dev/null +++ b/packages/client/src/ui/_common_/navbar.vue @@ -0,0 +1,492 @@ + + + + + diff --git a/packages/client/src/ui/_common_/sidebar-for-mobile.vue b/packages/client/src/ui/_common_/sidebar-for-mobile.vue deleted file mode 100644 index e789ae5e06..0000000000 --- a/packages/client/src/ui/_common_/sidebar-for-mobile.vue +++ /dev/null @@ -1,209 +0,0 @@ - - - - - diff --git a/packages/client/src/ui/_common_/sidebar.vue b/packages/client/src/ui/_common_/sidebar.vue deleted file mode 100644 index a72bf786ad..0000000000 --- a/packages/client/src/ui/_common_/sidebar.vue +++ /dev/null @@ -1,303 +0,0 @@ - - - - - diff --git a/packages/client/src/ui/_common_/statusbar-rss.vue b/packages/client/src/ui/_common_/statusbar-rss.vue index 88604a38a7..635b875ca1 100644 --- a/packages/client/src/ui/_common_/statusbar-rss.vue +++ b/packages/client/src/ui/_common_/statusbar-rss.vue @@ -20,9 +20,11 @@ import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue'; import MarqueeText from '@/components/marquee.vue'; import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; +import { shuffle } from '@/scripts/shuffle'; const props = defineProps<{ url?: string; + shuffle?: boolean; display?: 'marquee' | 'oneByOne'; marqueeDuration?: number; marqueeReverse?: boolean; @@ -37,6 +39,9 @@ let key = $ref(0); const tick = () => { fetch(`/api/fetch-rss?url=${props.url}`, {}).then(res => { res.json().then(feed => { + if (props.shuffle) { + shuffle(feed.items); + } items.value = feed.items; fetching.value = false; key++; diff --git a/packages/client/src/ui/_common_/statusbars.vue b/packages/client/src/ui/_common_/statusbars.vue index e50b4e54c3..114ca5be8c 100644 --- a/packages/client/src/ui/_common_/statusbars.vue +++ b/packages/client/src/ui/_common_/statusbars.vue @@ -10,7 +10,7 @@ }]" > {{ x.name }} - +
@@ -28,6 +28,7 @@ const XUserList = defineAsyncComponent(() => import('./statusbar-user-list.vue') diff --git a/packages/client/src/ui/deck/column.vue b/packages/client/src/ui/deck/column.vue index e8e554d72b..4d34ca9b8e 100644 --- a/packages/client/src/ui/deck/column.vue +++ b/packages/client/src/ui/deck/column.vue @@ -23,7 +23,7 @@ - +
@@ -361,7 +361,6 @@ function onDrop(ev) { z-index: 1; width: var(--deckColumnHeaderHeight); line-height: var(--deckColumnHeaderHeight); - font-size: 16px; color: var(--faceTextButton); &:hover { diff --git a/packages/client/src/ui/universal.vue b/packages/client/src/ui/universal.vue index 2edfb3f12d..e4b5de9918 100644 --- a/packages/client/src/ui/universal.vue +++ b/packages/client/src/ui/universal.vue @@ -61,17 +61,17 @@ import { defineAsyncComponent, provide, onMounted, computed, ref, watch, Compute import XCommon from './_common_/common.vue'; import { instanceName } from '@/config'; import { StickySidebar } from '@/scripts/sticky-sidebar'; -import XDrawerMenu from '@/ui/_common_/sidebar-for-mobile.vue'; +import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue'; import * as os from '@/os'; import { defaultStore } from '@/store'; -import { menuDef } from '@/menu'; +import { navbarItemDef } from '@/navbar'; import { i18n } from '@/i18n'; import { $i } from '@/account'; import { Router } from '@/nirax'; import { mainRouter } from '@/router'; import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); -const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/sidebar.vue')); +const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue')); const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); const DESKTOP_THRESHOLD = 1100; @@ -97,9 +97,9 @@ provideMetadataReceiver((info) => { }); const menuIndicated = computed(() => { - for (const def in menuDef) { + for (const def in navbarItemDef) { if (def === 'notifications') continue; // 通知は下にボタンとして表示されてるから - if (menuDef[def].indicated) return true; + if (navbarItemDef[def].indicated) return true; } return false; }); @@ -365,11 +365,11 @@ const wallpaper = localStorage.getItem('wallpaper') != null; height: calc(var(--vh, 1vh) * 100); width: 240px; box-sizing: border-box; + contain: strict; overflow: auto; overscroll-behavior: contain; - background: var(--bg); + background: var(--navBg); } - } diff --git a/packages/client/src/widgets/rss-ticker.vue b/packages/client/src/widgets/rss-ticker.vue index 06995bc865..c692c0c4ff 100644 --- a/packages/client/src/widgets/rss-ticker.vue +++ b/packages/client/src/widgets/rss-ticker.vue @@ -26,6 +26,7 @@ import { GetFormResultType } from '@/scripts/form'; import * as os from '@/os'; import MkContainer from '@/components/ui/container.vue'; import { useInterval } from '@/scripts/use-interval'; +import { shuffle } from '@/scripts/shuffle'; const name = 'rssTicker'; @@ -34,6 +35,10 @@ const widgetPropsDef = { type: 'string' as const, default: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews', }, + shuffle: { + type: 'boolean' as const, + default: true, + }, refreshIntervalSec: { type: 'number' as const, default: 60, @@ -80,6 +85,9 @@ let key = $ref(0); const tick = () => { fetch(`/api/fetch-rss?url=${widgetProps.url}`, {}).then(res => { res.json().then(feed => { + if (widgetProps.shuffle) { + shuffle(feed.items); + } items.value = feed.items; fetching.value = false; key++; diff --git a/packages/client/vite.json5.ts b/packages/client/vite.json5.ts index 693ee7be06..0a37fbff44 100644 --- a/packages/client/vite.json5.ts +++ b/packages/client/vite.json5.ts @@ -6,33 +6,33 @@ import { createFilter, dataToEsm } from '@rollup/pluginutils'; import { RollupJsonOptions } from '@rollup/plugin-json'; export default function json5(options: RollupJsonOptions = {}): Plugin { - const filter = createFilter(options.include, options.exclude); - const indent = 'indent' in options ? options.indent : '\t'; + const filter = createFilter(options.include, options.exclude); + const indent = 'indent' in options ? options.indent : '\t'; - return { - name: 'json5', + return { + name: 'json5', - // eslint-disable-next-line no-shadow - transform(json, id) { - if (id.slice(-6) !== '.json5' || !filter(id)) return null; + // eslint-disable-next-line no-shadow + transform(json, id) { + if (id.slice(-6) !== '.json5' || !filter(id)) return null; - try { - const parsed = JSON5.parse(json); - return { - code: dataToEsm(parsed, { - preferConst: options.preferConst, - compact: options.compact, - namedExports: options.namedExports, - indent - }), - map: { mappings: '' } - }; - } catch (err) { - const message = 'Could not parse JSON file'; - const position = parseInt(/[\d]/.exec(err.message)[0], 10); - this.warn({ message, id, position }); - return null; - } - } - }; + try { + const parsed = JSON5.parse(json); + return { + code: dataToEsm(parsed, { + preferConst: options.preferConst, + compact: options.compact, + namedExports: options.namedExports, + indent, + }), + map: { mappings: '' }, + }; + } catch (err) { + const message = 'Could not parse JSON file'; + const position = parseInt(/[\d]/.exec(err.message)[0], 10); + this.warn({ message, id, position }); + return null; + } + }, + }; }