Compare commits
55 Commits
2024.10.0-
...
2024.10.1-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ebae39cba5 | ||
![]() |
433732bcfc | ||
![]() |
a624546812 | ||
![]() |
4a356f1ba7 | ||
![]() |
0ad31bd5d4 | ||
![]() |
6de7c27522 | ||
![]() |
a304185eb8 | ||
![]() |
c13545f965 | ||
![]() |
0da6f14b3b | ||
![]() |
dd39c5e059 | ||
![]() |
1c99785e7e | ||
![]() |
d0213962bf | ||
![]() |
9858e12078 | ||
![]() |
c14eba3e6d | ||
![]() |
993d3fbe55 | ||
![]() |
3a11d5ede6 | ||
![]() |
ed89b4bd94 | ||
![]() |
03fb688073 | ||
![]() |
8b2780c730 | ||
![]() |
d2f1d45ea3 | ||
![]() |
a594d9f26b | ||
![]() |
26afe1cc96 | ||
![]() |
7933b6662e | ||
![]() |
ddf8e2a3dc | ||
![]() |
ddc799fe3d | ||
![]() |
057a6d731d | ||
![]() |
254c063455 | ||
![]() |
9d026975bc | ||
![]() |
d8cb7305ef | ||
![]() |
043fef9fdf | ||
![]() |
0d7d1091c8 | ||
![]() |
d8bf1ff7e9 | ||
![]() |
88698462a9 | ||
![]() |
ae3c155490 | ||
![]() |
fa06c59eae | ||
![]() |
b36d13d90c | ||
![]() |
3d637af65b | ||
![]() |
2340de035b | ||
![]() |
d8f30fb793 | ||
![]() |
708ffaef5c | ||
![]() |
2639e92e18 | ||
![]() |
ea2675eaab | ||
![]() |
3b0b4f83dd | ||
![]() |
975c2e7bc5 | ||
![]() |
e344650278 | ||
![]() |
1aee260398 | ||
![]() |
2fa805b8f7 | ||
![]() |
ed71b0b7d4 | ||
![]() |
864327b4a7 | ||
![]() |
c1597be458 | ||
![]() |
a08a38c29a | ||
![]() |
650e22c90d | ||
![]() |
5fc8b3bc50 | ||
![]() |
882c8b93c1 | ||
![]() |
e98f66db51 |
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -75,7 +75,7 @@ jobs:
|
||||
- run: corepack enable
|
||||
- run: pnpm i --frozen-lockfile
|
||||
- name: Restore eslint cache
|
||||
uses: actions/cache@v4.0.2
|
||||
uses: actions/cache@v4.1.0
|
||||
with:
|
||||
path: ${{ env.eslint-cache-path }}
|
||||
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
27
CHANGELOG.md
27
CHANGELOG.md
@@ -1,23 +1,46 @@
|
||||
## 2024.10.1
|
||||
|
||||
### General
|
||||
-
|
||||
|
||||
### Client
|
||||
- メールアドレス不要でCaptchaが有効な場合にアカウント登録完了後自動でのログインに失敗する問題を修正
|
||||
|
||||
### Server
|
||||
-
|
||||
|
||||
|
||||
## 2024.10.0
|
||||
|
||||
### Note
|
||||
- サーバー初期設定時に使用する初期パスワードを設定できるようになりました。今後Misskeyサーバーを新たに設置する際には、初回の起動前にコンフィグファイルの`setupPassword`をコメントアウトし、初期パスワードを設定することをおすすめします。(すでに初期設定を完了しているサーバーについては、この変更に伴い対応する必要はありません)
|
||||
- セキュリティ向上のため、サーバー初期設定時に使用する初期パスワードを設定できるようになりました。今後Misskeyサーバーを新たに設置する際には、初回の起動前にコンフィグファイルの`setupPassword`をコメントアウトし、初期パスワードを設定することをおすすめします。(すでに初期設定を完了しているサーバーについては、この変更に伴い対応する必要はありません)
|
||||
- ホスティングサービスを運営している場合は、コンフィグファイルを構築する際に`setupPassword`をランダムな値に設定し、ユーザーに通知するようにシステムを更新することをおすすめします。
|
||||
- なお、初期パスワードが設定されていない場合でも初期設定を行うことが可能です(UI上で初期パスワードの入力欄を空欄にすると続行できます)。
|
||||
- ユーザーデータを読み込む際の型が一部変更されました。
|
||||
- `twoFactorEnabled`, `usePasswordLessLogin`, `securityKeys`: 自分とモデレーター以外のユーザーからは取得できなくなりました
|
||||
|
||||
### General
|
||||
- Feat: サーバー初期設定時に初期パスワードを設定できるように
|
||||
- Feat: 通報にモデレーションノートを残せるように
|
||||
- Feat: 通報の解決種別を設定できるように
|
||||
- Enhance: 通報の解決と転送を個別に行えるように
|
||||
- Enhance: セキュリティ向上のため、サインイン時もCAPTCHAを求めるようになりました
|
||||
- Enhance: 依存関係の更新
|
||||
- Enhance: l10nの更新
|
||||
- Enhance: Playの「人気」タブで10件以上表示可能に #14399
|
||||
- Fix: 連合のホワイトリストが正常に登録されない問題を修正
|
||||
|
||||
### Client
|
||||
- Enhance: デザインの調整
|
||||
- Enhance: ログイン画面の認証フローを改善
|
||||
- Fix: クライアント上での時間ベースの実績獲得動作が実績獲得後も発動していた問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/657)
|
||||
|
||||
### Server
|
||||
- Enhance: セキュリティ向上のため、ログイン時にメール通知を行うように
|
||||
|
||||
- Enhance: 自分とモデレーター以外のユーザーから二要素認証関連のデータが取得できないように
|
||||
- Enhance: 通報および通報解決時に送出されるSystemWebhookにユーザ情報を含めるように ( #14697 )
|
||||
- Fix: `admin/abuse-user-reports`エンドポイントのスキーマが間違っていた問題を修正
|
||||
|
||||
## 2024.9.0
|
||||
|
||||
|
@@ -578,18 +578,18 @@ ESMではディレクトリインポートは廃止されているのと、デ
|
||||
### Lighten CSS vars
|
||||
|
||||
``` css
|
||||
color: hsl(from var(--accent) h s calc(l + 10));
|
||||
color: hsl(from var(--MI_THEME-accent) h s calc(l + 10));
|
||||
```
|
||||
|
||||
### Darken CSS vars
|
||||
|
||||
``` css
|
||||
color: hsl(from var(--accent) h s calc(l - 10));
|
||||
color: hsl(from var(--MI_THEME-accent) h s calc(l - 10));
|
||||
```
|
||||
|
||||
### Add alpha to CSS vars
|
||||
|
||||
``` css
|
||||
color: color(from var(--accent) srgb r g b / 0.5);
|
||||
color: color(from var(--MI_THEME-accent) srgb r g b / 0.5);
|
||||
```
|
||||
|
||||
|
@@ -120,11 +120,16 @@ describe('After user signup', () => {
|
||||
it('signin', () => {
|
||||
cy.visitHome();
|
||||
|
||||
cy.intercept('POST', '/api/signin').as('signin');
|
||||
cy.intercept('POST', '/api/signin-flow').as('signin');
|
||||
|
||||
cy.get('[data-cy-signin]').click();
|
||||
cy.get('[data-cy-signin-username] input').type('alice');
|
||||
// Enterキーでサインインできるかの確認も兼ねる
|
||||
|
||||
cy.get('[data-cy-signin-page-input]').should('be.visible', { timeout: 1000 });
|
||||
// Enterキーで続行できるかの確認も兼ねる
|
||||
cy.get('[data-cy-signin-username] input').type('alice{enter}');
|
||||
|
||||
cy.get('[data-cy-signin-page-password]').should('be.visible', { timeout: 10000 });
|
||||
// Enterキーで続行できるかの確認も兼ねる
|
||||
cy.get('[data-cy-signin-password] input').type('alice1234{enter}');
|
||||
|
||||
cy.wait('@signin');
|
||||
@@ -139,8 +144,9 @@ describe('After user signup', () => {
|
||||
cy.visitHome();
|
||||
|
||||
cy.get('[data-cy-signin]').click();
|
||||
cy.get('[data-cy-signin-username] input').type('alice');
|
||||
cy.get('[data-cy-signin-password] input').type('alice1234{enter}');
|
||||
|
||||
cy.get('[data-cy-signin-page-input]').should('be.visible', { timeout: 1000 });
|
||||
cy.get('[data-cy-signin-username] input').type('alice{enter}');
|
||||
|
||||
// TODO: cypressにブラウザの言語指定できる機能が実装され次第英語のみテストするようにする
|
||||
cy.contains(/アカウントが凍結されています|This account has been suspended due to/gi);
|
||||
|
@@ -48,16 +48,19 @@ Cypress.Commands.add('registerUser', (username, password, isAdmin = false) => {
|
||||
cy.request('POST', route, {
|
||||
username: username,
|
||||
password: password,
|
||||
...(isAdmin ? { setupPassword: 'example_password_please_change_this_or_you_will_get_hacked' } : {}),
|
||||
}).its('body').as(username);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('login', (username, password) => {
|
||||
cy.visitHome();
|
||||
|
||||
cy.intercept('POST', '/api/signin').as('signin');
|
||||
cy.intercept('POST', '/api/signin-flow').as('signin');
|
||||
|
||||
cy.get('[data-cy-signin]').click();
|
||||
cy.get('[data-cy-signin-username] input').type(username);
|
||||
cy.get('[data-cy-signin-page-input]').should('be.visible', { timeout: 1000 });
|
||||
cy.get('[data-cy-signin-username] input').type(`${username}{enter}`);
|
||||
cy.get('[data-cy-signin-page-password]').should('be.visible', { timeout: 10000 });
|
||||
cy.get('[data-cy-signin-password] input').type(`${password}{enter}`);
|
||||
|
||||
cy.wait('@signin').as('signedIn');
|
||||
|
@@ -7,8 +7,8 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import { HttpResponse, http } from 'msw';
|
||||
import { abuseUserReport } from '../../.storybook/fakes.js';
|
||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||
import { abuseUserReport } from '../packages/frontend/.storybook/fakes.js';
|
||||
import { commonHandlers } from '../packages/frontend/.storybook/mocks.js';
|
||||
import MkAbuseReport from './MkAbuseReport.vue';
|
||||
export const Default = {
|
||||
render(args) {
|
@@ -34,7 +34,7 @@ defineProps<{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: not-allowed;
|
||||
--color: color(from var(--error) srgb r g b / 0.25);
|
||||
--color: color(from var(--MI_THEME-error) srgb r g b / 0.25);
|
||||
background-size: auto auto;
|
||||
background-image: repeating-linear-gradient(135deg, transparent, transparent 10px, var(--color) 4px, var(--color) 14px);
|
||||
}
|
||||
|
@@ -626,10 +626,7 @@ abuseReported: "أُرسل البلاغ، شكرًا لك"
|
||||
reporter: "المُبلّغ"
|
||||
reporteeOrigin: "أصل البلاغ"
|
||||
reporterOrigin: "أصل المُبلّغ"
|
||||
forwardReport: "وجّه البلاغ إلى المثيل البعيد"
|
||||
forwardReportIsAnonymous: "في المثيل البعيد سيظهر المبلّغ كحساب مجهول."
|
||||
send: "أرسل"
|
||||
abuseMarkAsResolved: "علّم البلاغ كمحلول"
|
||||
openInNewTab: "افتح في لسان جديد"
|
||||
defaultNavigationBehaviour: "سلوك الملاحة الافتراضي"
|
||||
editTheseSettingsMayBreakAccount: "تعديل هذه الإعدادات قد يسبب عطبًا لحسابك"
|
||||
@@ -1255,7 +1252,6 @@ _theme:
|
||||
buttonBg: "خلفية الأزرار"
|
||||
buttonHoverBg: "خلفية الأزرار (عند التمرير فوقها)"
|
||||
inputBorder: "حواف حقل الإدخال"
|
||||
listItemHoverBg: "خلفية عناصر القائمة (عند التمرير فوقها)"
|
||||
driveFolderBg: "خلفية مجلد قرص التخزين"
|
||||
messageBg: "خلفية المحادثة"
|
||||
_sfx:
|
||||
|
@@ -624,10 +624,7 @@ abuseReported: "আপনার অভিযোগটি দাখিল কর
|
||||
reporter: "অভিযোগকারী"
|
||||
reporteeOrigin: "অভিযোগটির উৎস"
|
||||
reporterOrigin: "অভিযোগকারীর উৎস"
|
||||
forwardReport: "রিমোট ইন্সত্যান্সে অভিযোগটি পাঠান"
|
||||
forwardReportIsAnonymous: "আপনার তথ্য রিমোট ইন্সত্যান্সে পাঠানো হবে না এবং একটি বেনামী সিস্টেম অ্যাকাউন্ট হিসাবে প্রদর্শিত হবে।"
|
||||
send: "পাঠান"
|
||||
abuseMarkAsResolved: "অভিযোগটিকে সমাধাকৃত হিসাবে চিহ্নিত করুন"
|
||||
openInNewTab: "নতুন ট্যাবে খুলুন"
|
||||
openInSideView: "সাইড ভিউতে খুলুন"
|
||||
defaultNavigationBehaviour: "ডিফল্ট নেভিগেশন"
|
||||
@@ -1020,7 +1017,6 @@ _theme:
|
||||
buttonBg: "বাটনের পটভূমি"
|
||||
buttonHoverBg: "বাটনের পটভূমি (হভার)"
|
||||
inputBorder: "ইনপুট ফিল্ডের বর্ডার"
|
||||
listItemHoverBg: "লিস্ট আইটেমের পটভূমি (হোভার)"
|
||||
driveFolderBg: "ড্রাইভ ফোল্ডারের পটভূমি"
|
||||
wallpaperOverlay: "ওয়ালপেপার ওভারলে"
|
||||
badge: "ব্যাজ"
|
||||
|
@@ -8,6 +8,8 @@ search: "Cercar"
|
||||
notifications: "Notificacions"
|
||||
username: "Nom d'usuari"
|
||||
password: "Contrasenya"
|
||||
initialPasswordForSetup: "Contrasenya inicial per la configuració inicial"
|
||||
initialPasswordIsIncorrect: "La contrasenya no és correcta."
|
||||
forgotPassword: "Contrasenya oblidada"
|
||||
fetchingAsApObject: "Cercant en el Fediverse..."
|
||||
ok: "OK"
|
||||
@@ -451,6 +453,7 @@ totpDescription: "Escriu una contrasenya d'un sol us fent servir l'aplicació d'
|
||||
moderator: "Moderador/a"
|
||||
moderation: "Moderació"
|
||||
moderationNote: "Nota de moderació "
|
||||
moderationNoteDescription: "Pots escriure notes que es compartiran entre els moderadors."
|
||||
addModerationNote: "Afegir una nota de moderació "
|
||||
moderationLogs: "Registre de moderació "
|
||||
nUsersMentioned: "{n} usuaris mencionats"
|
||||
@@ -716,10 +719,7 @@ abuseReported: "La teva denúncia s'ha enviat. Moltes gràcies."
|
||||
reporter: "Denunciant "
|
||||
reporteeOrigin: "Origen de la denúncia "
|
||||
reporterOrigin: "Origen del denunciant"
|
||||
forwardReport: "Transferir la denúncia a una instància remota"
|
||||
forwardReportIsAnonymous: "En lloc del teu compte, es farà servir un compte anònim com a denunciant al servidor remot."
|
||||
send: "Envia"
|
||||
abuseMarkAsResolved: "Marca la denúncia com a resolta"
|
||||
openInNewTab: "Obre a una pestanya nova"
|
||||
openInSideView: "Obre a una vista lateral"
|
||||
defaultNavigationBehaviour: "Navegació per defecte"
|
||||
@@ -921,6 +921,7 @@ followersVisibility: "Visibilitat dels seguidors"
|
||||
continueThread: "Veure la continuació del fil"
|
||||
deleteAccountConfirm: "Això eliminarà el teu compte irreversiblement. Procedir?"
|
||||
incorrectPassword: "Contrasenya incorrecta."
|
||||
incorrectTotp: "La contrasenya no és correcta, o ha caducat."
|
||||
voteConfirm: "Confirma el teu vot \"{choice}\""
|
||||
hide: "Amagar"
|
||||
useDrawerReactionPickerForMobile: "Mostrar el selector de reaccions com un calaix al mòbil "
|
||||
@@ -1284,6 +1285,14 @@ unknownWebAuthnKey: "Passkey desconeguda"
|
||||
passkeyVerificationFailed: "La verificació a fallat"
|
||||
passkeyVerificationSucceededButPasswordlessLoginDisabled: "La verificació de la passkey a estat correcta, però s'ha deshabilitat l'inici de sessió sense contrasenya."
|
||||
messageToFollower: "Missatge als meus seguidors"
|
||||
target: "Assumpte "
|
||||
_abuseUserReport:
|
||||
forward: "Reenviar "
|
||||
forwardDescription: "Reenvia l'informe a una altra instància com un compte del sistema anònima."
|
||||
resolve: "Solució "
|
||||
accept: "Acceptar "
|
||||
reject: "Rebutjar"
|
||||
resolveTutorial: "Si l'informe és legítim selecciona \"Acceptar\" per resoldre'l positivament. Però si l'informe no és legítim selecciona \"Rebutjar\" per resoldre'l negativament."
|
||||
_delivery:
|
||||
status: "Estat d'entrega "
|
||||
stop: "Suspés"
|
||||
@@ -1974,7 +1983,6 @@ _theme:
|
||||
buttonBg: "Fons botó "
|
||||
buttonHoverBg: "Fons botó (en passar-hi per sobre)"
|
||||
inputBorder: "Contorn del cap d'introducció "
|
||||
listItemHoverBg: "Fons dels elements d'una llista"
|
||||
driveFolderBg: "Fons de la carpeta Disc"
|
||||
wallpaperOverlay: "Superposició del fons de pantalla "
|
||||
badge: "Insígnia "
|
||||
@@ -2520,6 +2528,8 @@ _moderationLogTypes:
|
||||
markSensitiveDriveFile: "Fitxer marcat com a sensible"
|
||||
unmarkSensitiveDriveFile: "S'ha tret la marca de sensible del fitxer"
|
||||
resolveAbuseReport: "Informe resolt"
|
||||
forwardAbuseReport: "Informe reenviat"
|
||||
updateAbuseReportNote: "Nota de moderació d'un informe actualitzat"
|
||||
createInvitation: "Crear codi d'invitació "
|
||||
createAd: "Anunci creat"
|
||||
deleteAd: "Anunci esborrat"
|
||||
|
@@ -657,10 +657,7 @@ abuseReported: "Nahlášení bylo odesláno. Děkujeme převelice."
|
||||
reporter: "Nahlásil"
|
||||
reporteeOrigin: "Původ nahlášení"
|
||||
reporterOrigin: "Původ nahlasovače"
|
||||
forwardReport: "Přeposlat nahlášení do vzdálené instance"
|
||||
forwardReportIsAnonymous: "Místo vašeho účtu se ve vzdálené instanci zobrazí anonymní systémový účet jako nahlašovač."
|
||||
send: "Odeslat"
|
||||
abuseMarkAsResolved: "Označit nahlášení jako vyřešené"
|
||||
openInNewTab: "Otevřít v nové kartě"
|
||||
openInSideView: "Otevřít v bočním panelu"
|
||||
defaultNavigationBehaviour: "Výchozí chování navigace"
|
||||
@@ -1632,7 +1629,6 @@ _theme:
|
||||
buttonBg: "Pozadí tlačítka"
|
||||
buttonHoverBg: "Pozadí tlačítka (Hover)"
|
||||
inputBorder: "Ohraničení vstupního pole"
|
||||
listItemHoverBg: "Pozadí položky seznamu (Hover)"
|
||||
driveFolderBg: "Pozadí složky disku"
|
||||
wallpaperOverlay: "Překrytí tapety"
|
||||
badge: "Odznak"
|
||||
|
@@ -686,10 +686,7 @@ abuseReported: "Deine Meldung wurde versendet. Vielen Dank."
|
||||
reporter: "Melder"
|
||||
reporteeOrigin: "Herkunft des Gemeldeten"
|
||||
reporterOrigin: "Herkunft des Meldenden"
|
||||
forwardReport: "Meldung an fremde Instanz weiterleiten"
|
||||
forwardReportIsAnonymous: "Anstatt deines Benutzerkontos wird bei der fremden Instanz ein anonymes Systemkonto als Melder angezeigt."
|
||||
send: "Senden"
|
||||
abuseMarkAsResolved: "Meldung als gelöst markieren"
|
||||
openInNewTab: "In neuem Tab öffnen"
|
||||
openInSideView: "In Seitenansicht öffnen"
|
||||
defaultNavigationBehaviour: "Standardnavigationsverhalten"
|
||||
@@ -1787,7 +1784,6 @@ _theme:
|
||||
buttonBg: "Hintergrund von Schaltflächen"
|
||||
buttonHoverBg: "Hintergrund von Schaltflächen (Mouseover)"
|
||||
inputBorder: "Rahmen von Eingabefeldern"
|
||||
listItemHoverBg: "Hintergrund von Listeneinträgen (Mouseover)"
|
||||
driveFolderBg: "Hintergrund von Drive-Ordnern"
|
||||
wallpaperOverlay: "Hintergrundbild-Overlay"
|
||||
badge: "Wappen"
|
||||
|
@@ -8,6 +8,9 @@ search: "Search"
|
||||
notifications: "Notifications"
|
||||
username: "Username"
|
||||
password: "Password"
|
||||
initialPasswordForSetup: "Initial password for setup"
|
||||
initialPasswordIsIncorrect: "Initial password for setup is incorrect"
|
||||
initialPasswordForSetupDescription: "Use the password you entered in the configuration file if you installed Misskey yourself.\n If you are using a Misskey hosting service, use the password provided.\n If you have not set a password, leave it blank to continue."
|
||||
forgotPassword: "Forgot password"
|
||||
fetchingAsApObject: "Fetching from the Fediverse..."
|
||||
ok: "OK"
|
||||
@@ -109,7 +112,7 @@ enterEmoji: "Enter an emoji"
|
||||
renote: "Renote"
|
||||
unrenote: "Remove renote"
|
||||
renoted: "Renoted."
|
||||
renotedToX: "Renote to {name}."
|
||||
renotedToX: "Renoted to {name}."
|
||||
cantRenote: "This post can't be renoted."
|
||||
cantReRenote: "A renote can't be renoted."
|
||||
quote: "Quote"
|
||||
@@ -451,6 +454,7 @@ totpDescription: "Use an authenticator app to enter one-time passwords"
|
||||
moderator: "Moderator"
|
||||
moderation: "Moderation"
|
||||
moderationNote: "Moderation note"
|
||||
moderationNoteDescription: "You can fill in notes that will be shared only among moderators."
|
||||
addModerationNote: "Add moderation note"
|
||||
moderationLogs: "Moderation logs"
|
||||
nUsersMentioned: "Mentioned by {n} users"
|
||||
@@ -716,10 +720,7 @@ abuseReported: "Your report has been sent. Thank you very much."
|
||||
reporter: "Reporter"
|
||||
reporteeOrigin: "Reportee Origin"
|
||||
reporterOrigin: "Reporter Origin"
|
||||
forwardReport: "Forward report to remote instance"
|
||||
forwardReportIsAnonymous: "Instead of your account, an anonymous system account will be displayed as reporter at the remote instance."
|
||||
send: "Send"
|
||||
abuseMarkAsResolved: "Mark report as resolved"
|
||||
openInNewTab: "Open in new tab"
|
||||
openInSideView: "Open in side view"
|
||||
defaultNavigationBehaviour: "Default navigation behavior"
|
||||
@@ -921,6 +922,7 @@ followersVisibility: "Visibility of followers"
|
||||
continueThread: "View thread continuation"
|
||||
deleteAccountConfirm: "This will irreversibly delete your account. Proceed?"
|
||||
incorrectPassword: "Incorrect password."
|
||||
incorrectTotp: "The one-time password is incorrect or has expired."
|
||||
voteConfirm: "Confirm your vote for \"{choice}\"?"
|
||||
hide: "Hide"
|
||||
useDrawerReactionPickerForMobile: "Display reaction picker as drawer on mobile"
|
||||
@@ -1283,6 +1285,15 @@ signinWithPasskey: "Sign in with Passkey"
|
||||
unknownWebAuthnKey: "Unknown Passkey"
|
||||
passkeyVerificationFailed: "Passkey verification has failed."
|
||||
passkeyVerificationSucceededButPasswordlessLoginDisabled: "Passkey verification has succeeded but password-less login is disabled."
|
||||
messageToFollower: "Message to followers"
|
||||
target: "Target"
|
||||
_abuseUserReport:
|
||||
forward: "Forward"
|
||||
forwardDescription: "Forward the report to a remote server as an anonymous system account."
|
||||
resolve: "Resolve"
|
||||
accept: "Accept"
|
||||
reject: "Reject"
|
||||
resolveTutorial: "If the report is legitimate in content, select \"Accept\" to mark the case as resolved in the affirmative.\nIf the content of the report is not legitimate, select \"Reject\" to mark the case as resolved in the negative."
|
||||
_delivery:
|
||||
status: "Delivery status"
|
||||
stop: "Suspended"
|
||||
@@ -1736,7 +1747,7 @@ _role:
|
||||
canManageAvatarDecorations: "Manage avatar decorations"
|
||||
driveCapacity: "Drive capacity"
|
||||
alwaysMarkNsfw: "Always mark files as NSFW"
|
||||
canUpdateBioMedia: "Allow to edit an icon or a banner image"
|
||||
canUpdateBioMedia: "Can edit an icon or a banner image"
|
||||
pinMax: "Maximum number of pinned notes"
|
||||
antennaMax: "Maximum number of antennas"
|
||||
wordMuteMax: "Maximum number of characters allowed in word mutes"
|
||||
@@ -1973,7 +1984,6 @@ _theme:
|
||||
buttonBg: "Button background"
|
||||
buttonHoverBg: "Button background (Hover)"
|
||||
inputBorder: "Input field border"
|
||||
listItemHoverBg: "List item background (Hover)"
|
||||
driveFolderBg: "Drive folder background"
|
||||
wallpaperOverlay: "Wallpaper overlay"
|
||||
badge: "Badge"
|
||||
@@ -2392,6 +2402,7 @@ _notification:
|
||||
followedBySomeUsers: "Followed by {n} users"
|
||||
flushNotification: "Clear notifications"
|
||||
exportOfXCompleted: "Export of {x} has been completed"
|
||||
login: "Someone logged in"
|
||||
_types:
|
||||
all: "All"
|
||||
note: "New notes"
|
||||
@@ -2471,22 +2482,22 @@ _webhookSettings:
|
||||
reaction: "When receiving a reaction"
|
||||
mention: "When being mentioned"
|
||||
_systemEvents:
|
||||
abuseReport: "When received a new abuse report"
|
||||
abuseReportResolved: "When resolved abuse report"
|
||||
abuseReport: "When received a new report"
|
||||
abuseReportResolved: "When resolved report"
|
||||
userCreated: "When user is created"
|
||||
deleteConfirm: "Are you sure you want to delete the Webhook?"
|
||||
testRemarks: "Click the button to the right of the switch to send a test Webhook with dummy data."
|
||||
_abuseReport:
|
||||
_notificationRecipient:
|
||||
createRecipient: "Add a recipient for abuse reports"
|
||||
modifyRecipient: "Edit a recipient for abuse reports"
|
||||
createRecipient: "Add a recipient for reports"
|
||||
modifyRecipient: "Edit a recipient for reports"
|
||||
recipientType: "Notification type"
|
||||
_recipientType:
|
||||
mail: "Email"
|
||||
webhook: "Webhook"
|
||||
_captions:
|
||||
mail: "Send the email to moderators' email addresses when you receive abuse."
|
||||
webhook: "Send a notification to SystemWebhook when you receive or resolve abuse."
|
||||
mail: "Send the email to moderators' email addresses when you receive reports."
|
||||
webhook: "Send a notification to System Webhook when you receive or resolve reports."
|
||||
keywords: "Keywords"
|
||||
notifiedUser: "Users to notify"
|
||||
notifiedWebhook: "Webhook to use"
|
||||
@@ -2519,6 +2530,8 @@ _moderationLogTypes:
|
||||
markSensitiveDriveFile: "File marked as sensitive"
|
||||
unmarkSensitiveDriveFile: "File unmarked as sensitive"
|
||||
resolveAbuseReport: "Report resolved"
|
||||
forwardAbuseReport: "Report forwarded"
|
||||
updateAbuseReportNote: "Moderation note of a report updated"
|
||||
createInvitation: "Invite generated"
|
||||
createAd: "Ad created"
|
||||
deleteAd: "Ad deleted"
|
||||
@@ -2526,18 +2539,18 @@ _moderationLogTypes:
|
||||
createAvatarDecoration: "Avatar decoration created"
|
||||
updateAvatarDecoration: "Avatar decoration updated"
|
||||
deleteAvatarDecoration: "Avatar decoration deleted"
|
||||
unsetUserAvatar: "Unset this user's avatar"
|
||||
unsetUserBanner: "Unset this user's banner"
|
||||
createSystemWebhook: "Create SystemWebhook"
|
||||
updateSystemWebhook: "Update SystemWebhook"
|
||||
deleteSystemWebhook: "Delete SystemWebhook"
|
||||
createAbuseReportNotificationRecipient: "Create a recipient for abuse reports"
|
||||
updateAbuseReportNotificationRecipient: "Update recipients for abuse reports"
|
||||
deleteAbuseReportNotificationRecipient: "Delete a recipient for abuse reports"
|
||||
deleteAccount: "Delete the account"
|
||||
deletePage: "Delete the page"
|
||||
deleteFlash: "Delete Play"
|
||||
deleteGalleryPost: "Delete the gallery post"
|
||||
unsetUserAvatar: "User avatar unset"
|
||||
unsetUserBanner: "User banner unset"
|
||||
createSystemWebhook: "System Webhook created"
|
||||
updateSystemWebhook: "System Webhook updated"
|
||||
deleteSystemWebhook: "System Webhook deleted"
|
||||
createAbuseReportNotificationRecipient: "Recipient for reports created"
|
||||
updateAbuseReportNotificationRecipient: "Recipient for reports updated"
|
||||
deleteAbuseReportNotificationRecipient: "Recipient for reports deleted"
|
||||
deleteAccount: "Account deleted"
|
||||
deletePage: "Page deleted"
|
||||
deleteFlash: "Play deleted"
|
||||
deleteGalleryPost: "Gallery post deleted"
|
||||
_fileViewer:
|
||||
title: "File details"
|
||||
type: "File type"
|
||||
|
@@ -700,10 +700,7 @@ abuseReported: "Se ha enviado el reporte. Muchas gracias."
|
||||
reporter: "Reportador"
|
||||
reporteeOrigin: "Reportar a"
|
||||
reporterOrigin: "Origen del reporte"
|
||||
forwardReport: "Transferir un informe a una instancia remota"
|
||||
forwardReportIsAnonymous: "No puede ver su información de la instancia remota y aparecerá como una cuenta anónima del sistema"
|
||||
send: "Enviar"
|
||||
abuseMarkAsResolved: "Marcar reporte como resuelto"
|
||||
openInNewTab: "Abrir en una Nueva Pestaña"
|
||||
openInSideView: "Abrir en una vista al costado"
|
||||
defaultNavigationBehaviour: "Navegación por defecto"
|
||||
@@ -1918,7 +1915,6 @@ _theme:
|
||||
buttonBg: "Fondo de botón"
|
||||
buttonHoverBg: "Fondo de botón (hover)"
|
||||
inputBorder: "Borde de los campos de entrada"
|
||||
listItemHoverBg: "Fondo de elemento de listas (hover)"
|
||||
driveFolderBg: "Fondo de capeta del drive"
|
||||
wallpaperOverlay: "Transparencia del fondo de pantalla"
|
||||
badge: "Medalla"
|
||||
|
@@ -691,10 +691,7 @@ abuseReported: "Le rapport est envoyé. Merci."
|
||||
reporter: "Signalé par"
|
||||
reporteeOrigin: "Origine du signalement"
|
||||
reporterOrigin: "Signalé par"
|
||||
forwardReport: "Transférer le signalement à l’instance distante"
|
||||
forwardReportIsAnonymous: "L'instance distante ne sera pas en mesure de voir vos informations et apparaîtra comme un compte anonyme du système."
|
||||
send: "Envoyer"
|
||||
abuseMarkAsResolved: "Marquer le signalement comme résolu"
|
||||
openInNewTab: "Ouvrir dans un nouvel onglet"
|
||||
openInSideView: "Ouvrir en vue latérale"
|
||||
defaultNavigationBehaviour: "Navigation par défaut"
|
||||
@@ -1704,7 +1701,6 @@ _theme:
|
||||
buttonBg: "Arrière-plan du bouton"
|
||||
buttonHoverBg: "Arrière-plan du bouton (survolé)"
|
||||
inputBorder: "Cadre de la zone de texte"
|
||||
listItemHoverBg: "Arrière-plan d'item de liste (survolé)"
|
||||
driveFolderBg: "Arrière-plan du dossier de disque"
|
||||
wallpaperOverlay: "Superposition de fond d'écran"
|
||||
badge: "Badge"
|
||||
|
@@ -702,10 +702,7 @@ abuseReported: "Laporan kamu telah dikirimkan. Terima kasih."
|
||||
reporter: "Pelapor"
|
||||
reporteeOrigin: "Yang dilaporkan"
|
||||
reporterOrigin: "Pelapor"
|
||||
forwardReport: "Teruskan laporan ke instansi luar"
|
||||
forwardReportIsAnonymous: "Untuk melindungi privasi akun kamu, akun anonim dari sistem akan digunakan sebagai pelapor pada instansi luar."
|
||||
send: "Kirim"
|
||||
abuseMarkAsResolved: "Tandai laporan sebagai selesai"
|
||||
openInNewTab: "Buka di tab baru"
|
||||
openInSideView: "Buka di tampilan samping"
|
||||
defaultNavigationBehaviour: "Navigasi bawaan"
|
||||
@@ -1927,7 +1924,6 @@ _theme:
|
||||
buttonBg: "Latar belakang tombol"
|
||||
buttonHoverBg: "Latar belakang tombol (Mengambang)"
|
||||
inputBorder: "Batas bidang masukan"
|
||||
listItemHoverBg: "Latar belakang daftar item (Mengambang)"
|
||||
driveFolderBg: "Latar belakang folder drive"
|
||||
wallpaperOverlay: "Lapisan wallpaper"
|
||||
badge: "Lencana"
|
||||
|
63
locales/index.d.ts
vendored
63
locales/index.d.ts
vendored
@@ -1834,6 +1834,10 @@ export interface Locale extends ILocale {
|
||||
* モデレーションノート
|
||||
*/
|
||||
"moderationNote": string;
|
||||
/**
|
||||
* モデレーター間でだけ共有されるメモを記入することができます。
|
||||
*/
|
||||
"moderationNoteDescription": string;
|
||||
/**
|
||||
* モデレーションノートを追加する
|
||||
*/
|
||||
@@ -2894,22 +2898,10 @@ export interface Locale extends ILocale {
|
||||
* 通報元
|
||||
*/
|
||||
"reporterOrigin": string;
|
||||
/**
|
||||
* リモートサーバーに通報を転送する
|
||||
*/
|
||||
"forwardReport": string;
|
||||
/**
|
||||
* リモートサーバーからはあなたの情報は見れず、匿名のシステムアカウントとして表示されます。
|
||||
*/
|
||||
"forwardReportIsAnonymous": string;
|
||||
/**
|
||||
* 送信
|
||||
*/
|
||||
"send": string;
|
||||
/**
|
||||
* 対応済みにする
|
||||
*/
|
||||
"abuseMarkAsResolved": string;
|
||||
/**
|
||||
* 新しいタブで開く
|
||||
*/
|
||||
@@ -3714,6 +3706,10 @@ export interface Locale extends ILocale {
|
||||
* パスワードが間違っています。
|
||||
*/
|
||||
"incorrectPassword": string;
|
||||
/**
|
||||
* ワンタイムパスワードが間違っているか、期限切れになっています。
|
||||
*/
|
||||
"incorrectTotp": string;
|
||||
/**
|
||||
* 「{choice}」に投票しますか?
|
||||
*/
|
||||
@@ -5166,6 +5162,37 @@ export interface Locale extends ILocale {
|
||||
* フォロワーへのメッセージ
|
||||
*/
|
||||
"messageToFollower": string;
|
||||
/**
|
||||
* 対象
|
||||
*/
|
||||
"target": string;
|
||||
"_abuseUserReport": {
|
||||
/**
|
||||
* 転送
|
||||
*/
|
||||
"forward": string;
|
||||
/**
|
||||
* 匿名のシステムアカウントとして、リモートサーバーに通報を転送します。
|
||||
*/
|
||||
"forwardDescription": string;
|
||||
/**
|
||||
* 解決
|
||||
*/
|
||||
"resolve": string;
|
||||
/**
|
||||
* 是認
|
||||
*/
|
||||
"accept": string;
|
||||
/**
|
||||
* 否認
|
||||
*/
|
||||
"reject": string;
|
||||
/**
|
||||
* 内容が正当である通報に対応した場合は「是認」を選択し、肯定的にケースが解決されたことをマークします。
|
||||
* 内容が正当でない通報の場合は「否認」を選択し、否定的にケースが解決されたことをマークします。
|
||||
*/
|
||||
"resolveTutorial": string;
|
||||
};
|
||||
"_delivery": {
|
||||
/**
|
||||
* 配信状態
|
||||
@@ -7678,10 +7705,6 @@ export interface Locale extends ILocale {
|
||||
* 入力ボックスの縁取り
|
||||
*/
|
||||
"inputBorder": string;
|
||||
/**
|
||||
* リスト項目の背景 (ホバー)
|
||||
*/
|
||||
"listItemHoverBg": string;
|
||||
/**
|
||||
* ドライブフォルダーの背景
|
||||
*/
|
||||
@@ -9781,6 +9804,14 @@ export interface Locale extends ILocale {
|
||||
* 通報を解決
|
||||
*/
|
||||
"resolveAbuseReport": string;
|
||||
/**
|
||||
* 通報を転送
|
||||
*/
|
||||
"forwardAbuseReport": string;
|
||||
/**
|
||||
* 通報のモデレーションノート更新
|
||||
*/
|
||||
"updateAbuseReportNote": string;
|
||||
/**
|
||||
* 招待コードを作成
|
||||
*/
|
||||
|
@@ -8,6 +8,9 @@ search: "Cerca"
|
||||
notifications: "Notifiche"
|
||||
username: "Nome utente"
|
||||
password: "Password"
|
||||
initialPasswordForSetup: "Password iniziale, per avviare le impostazioni"
|
||||
initialPasswordIsIncorrect: "Password iniziale, sbagliata."
|
||||
initialPasswordForSetupDescription: "Se hai installato Misskey di persona, usa la password che hai indicato nel file di configurazione.\nSe stai utilizzando un servizio di hosting Misskey, usa la password fornita dal gestore.\nSe non hai una password preimpostata, lascia il campo vuoto e continua."
|
||||
forgotPassword: "Hai dimenticato la password?"
|
||||
fetchingAsApObject: "Recuperando dal Fediverso..."
|
||||
ok: "OK"
|
||||
@@ -716,10 +719,7 @@ abuseReported: "La segnalazione è stata inviata. Grazie."
|
||||
reporter: "il corrispondente"
|
||||
reporteeOrigin: "Segnalazione a"
|
||||
reporterOrigin: "Segnalazione da"
|
||||
forwardReport: "Inoltro di un report a un'istanza remota."
|
||||
forwardReportIsAnonymous: "L'istanza remota non vedrà le tue informazioni, apparirai come profilo di sistema, anonimo."
|
||||
send: "Inviare"
|
||||
abuseMarkAsResolved: "Risolvi segnalazione"
|
||||
openInNewTab: "Apri in una nuova scheda"
|
||||
openInSideView: "Apri in vista laterale"
|
||||
defaultNavigationBehaviour: "Navigazione preimpostata"
|
||||
@@ -921,6 +921,7 @@ followersVisibility: "Visibilità dei profili che ti seguono"
|
||||
continueThread: "Altre conversazioni"
|
||||
deleteAccountConfirm: "Così verrà eliminato il profilo. Vuoi procedere?"
|
||||
incorrectPassword: "La password è errata."
|
||||
incorrectTotp: "Il codice OTP è sbagliato, oppure scaduto."
|
||||
voteConfirm: "Votare per「{choice}」?"
|
||||
hide: "Nascondere"
|
||||
useDrawerReactionPickerForMobile: "Mostra sul drawer da dispositivo mobile"
|
||||
@@ -1974,7 +1975,6 @@ _theme:
|
||||
buttonBg: "Sfondo del pulsante"
|
||||
buttonHoverBg: "Sfondo del pulsante (sorvolato)"
|
||||
inputBorder: "Inquadra casella di testo"
|
||||
listItemHoverBg: "Sfondo della voce di elenco (sorvolato)"
|
||||
driveFolderBg: "Sfondo della cartella di disco"
|
||||
wallpaperOverlay: "Sovrapposizione dello sfondo"
|
||||
badge: "Distintivo"
|
||||
@@ -2393,6 +2393,7 @@ _notification:
|
||||
followedBySomeUsers: "{n} follower"
|
||||
flushNotification: "Azzera le notifiche"
|
||||
exportOfXCompleted: "Abbiamo completato l'esportazione di {x}"
|
||||
login: "Autenticazione avvenuta"
|
||||
_types:
|
||||
all: "Tutto"
|
||||
note: "Nuove Note"
|
||||
|
@@ -454,6 +454,7 @@ totpDescription: "認証アプリを使ってワンタイムパスワードを
|
||||
moderator: "モデレーター"
|
||||
moderation: "モデレーション"
|
||||
moderationNote: "モデレーションノート"
|
||||
moderationNoteDescription: "モデレーター間でだけ共有されるメモを記入することができます。"
|
||||
addModerationNote: "モデレーションノートを追加する"
|
||||
moderationLogs: "モデログ"
|
||||
nUsersMentioned: "{n}人が投稿"
|
||||
@@ -719,10 +720,7 @@ abuseReported: "内容が送信されました。ご報告ありがとうござ
|
||||
reporter: "通報者"
|
||||
reporteeOrigin: "通報先"
|
||||
reporterOrigin: "通報元"
|
||||
forwardReport: "リモートサーバーに通報を転送する"
|
||||
forwardReportIsAnonymous: "リモートサーバーからはあなたの情報は見れず、匿名のシステムアカウントとして表示されます。"
|
||||
send: "送信"
|
||||
abuseMarkAsResolved: "対応済みにする"
|
||||
openInNewTab: "新しいタブで開く"
|
||||
openInSideView: "サイドビューで開く"
|
||||
defaultNavigationBehaviour: "デフォルトのナビゲーション"
|
||||
@@ -924,6 +922,7 @@ followersVisibility: "フォロワーの公開範囲"
|
||||
continueThread: "さらにスレッドを見る"
|
||||
deleteAccountConfirm: "アカウントが削除されます。よろしいですか?"
|
||||
incorrectPassword: "パスワードが間違っています。"
|
||||
incorrectTotp: "ワンタイムパスワードが間違っているか、期限切れになっています。"
|
||||
voteConfirm: "「{choice}」に投票しますか?"
|
||||
hide: "隠す"
|
||||
useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示"
|
||||
@@ -1287,6 +1286,15 @@ unknownWebAuthnKey: "登録されていないパスキーです。"
|
||||
passkeyVerificationFailed: "パスキーの検証に失敗しました。"
|
||||
passkeyVerificationSucceededButPasswordlessLoginDisabled: "パスキーの検証に成功しましたが、パスワードレスログインが無効になっています。"
|
||||
messageToFollower: "フォロワーへのメッセージ"
|
||||
target: "対象"
|
||||
|
||||
_abuseUserReport:
|
||||
forward: "転送"
|
||||
forwardDescription: "匿名のシステムアカウントとして、リモートサーバーに通報を転送します。"
|
||||
resolve: "解決"
|
||||
accept: "是認"
|
||||
reject: "否認"
|
||||
resolveTutorial: "内容が正当である通報に対応した場合は「是認」を選択し、肯定的にケースが解決されたことをマークします。\n内容が正当でない通報の場合は「否認」を選択し、否定的にケースが解決されたことをマークします。"
|
||||
|
||||
_delivery:
|
||||
status: "配信状態"
|
||||
@@ -2010,7 +2018,6 @@ _theme:
|
||||
buttonBg: "ボタンの背景"
|
||||
buttonHoverBg: "ボタンの背景 (ホバー)"
|
||||
inputBorder: "入力ボックスの縁取り"
|
||||
listItemHoverBg: "リスト項目の背景 (ホバー)"
|
||||
driveFolderBg: "ドライブフォルダーの背景"
|
||||
wallpaperOverlay: "壁紙のオーバーレイ"
|
||||
badge: "バッジ"
|
||||
@@ -2592,6 +2599,8 @@ _moderationLogTypes:
|
||||
markSensitiveDriveFile: "ファイルをセンシティブ付与"
|
||||
unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
|
||||
resolveAbuseReport: "通報を解決"
|
||||
forwardAbuseReport: "通報を転送"
|
||||
updateAbuseReportNote: "通報のモデレーションノート更新"
|
||||
createInvitation: "招待コードを作成"
|
||||
createAd: "広告を作成"
|
||||
deleteAd: "広告を削除"
|
||||
|
@@ -707,10 +707,7 @@ abuseReported: "無事内容が送信されたみたいやで。おおきに〜
|
||||
reporter: "通報者"
|
||||
reporteeOrigin: "通報先"
|
||||
reporterOrigin: "通報元"
|
||||
forwardReport: "リモートサーバーに通報を転送するで"
|
||||
forwardReportIsAnonymous: "リモートサーバーからはあんたの情報は見えんなって、匿名のシステムアカウントとして表示されるで。"
|
||||
send: "送信"
|
||||
abuseMarkAsResolved: "対応したで"
|
||||
openInNewTab: "新しいタブで開く"
|
||||
openInSideView: "サイドビューで開く"
|
||||
defaultNavigationBehaviour: "デフォルトのナビゲーション"
|
||||
@@ -1946,7 +1943,6 @@ _theme:
|
||||
buttonBg: "ボタンの背景"
|
||||
buttonHoverBg: "ボタンの背景 (ホバー)"
|
||||
inputBorder: "入力ボックスの縁取り"
|
||||
listItemHoverBg: "リスト項目の背景 (ホバー)"
|
||||
driveFolderBg: "ドライブフォルダーの背景"
|
||||
wallpaperOverlay: "壁紙のオーバーレイ"
|
||||
badge: "バッジ"
|
||||
|
@@ -601,8 +601,6 @@ reportAbuseOf: "{name}님얼 신고하기"
|
||||
reporter: "신고한 사람"
|
||||
reporteeOrigin: "신고덴 사람"
|
||||
reporterOrigin: "신고한 곳"
|
||||
forwardReport: "웬겍 서버에 신고 보내기"
|
||||
forwardReportIsAnonymous: "웬겍 서버서는 나으 정보럴 몬 보고 익멩으 시스템 게정어로 보입니다."
|
||||
waitingFor: "{x}(얼)럴 지달리고 잇십니다"
|
||||
random: "무작이"
|
||||
system: "시스템"
|
||||
|
@@ -8,6 +8,9 @@ search: "검색"
|
||||
notifications: "알림"
|
||||
username: "유저명"
|
||||
password: "비밀번호"
|
||||
initialPasswordForSetup: "초기 설정용 비밀번호"
|
||||
initialPasswordIsIncorrect: "초기 설정용 비밀번호가 올바르지 않습니다."
|
||||
initialPasswordForSetupDescription: "Misskey를 직접 설치하는 경우, 설정 파일에 입력해둔 비밀번호를 사용하세요.\nMisskey 설치를 도와주는 호스팅 서비스 등을 사용하는 경우, 서비스 제공자로부터 받은 비밀번호를 사용하세요.\n비밀번호를 따로 설정하지 않은 경우, 아무것도 입력하지 않아도 됩니다."
|
||||
forgotPassword: "비밀번호 재설정"
|
||||
fetchingAsApObject: "연합에서 찾아보는 중"
|
||||
ok: "확인"
|
||||
@@ -451,6 +454,7 @@ totpDescription: "인증 앱을 사용하여 일회성 비밀번호 입력"
|
||||
moderator: "모더레이터"
|
||||
moderation: "조정"
|
||||
moderationNote: "조정 기록"
|
||||
moderationNoteDescription: "모더레이터 역할을 가진 유저만 보이는 메모를 적을 수 있습니다."
|
||||
addModerationNote: "조정 기록 추가하기"
|
||||
moderationLogs: "모더레이션 로그"
|
||||
nUsersMentioned: "{n}명이 언급함"
|
||||
@@ -716,10 +720,7 @@ abuseReported: "신고를 보냈습니다. 신고해 주셔서 감사합니다."
|
||||
reporter: "신고자"
|
||||
reporteeOrigin: "피신고자"
|
||||
reporterOrigin: "신고자"
|
||||
forwardReport: "리모트 서버에도 신고 내용 보내기"
|
||||
forwardReportIsAnonymous: "리모트 서버에서는 나의 정보를 볼 수 없으며, 익명의 시스템 계정으로 표시됩니다."
|
||||
send: "전송"
|
||||
abuseMarkAsResolved: "해결됨으로 표시"
|
||||
openInNewTab: "새 탭에서 열기"
|
||||
openInSideView: "사이드뷰로 열기"
|
||||
defaultNavigationBehaviour: "기본 탐색 동작"
|
||||
@@ -921,6 +922,7 @@ followersVisibility: "팔로워의 공개 범위"
|
||||
continueThread: "글타래 더 보기"
|
||||
deleteAccountConfirm: "계정이 삭제되고 되돌릴 수 없게 됩니다. 계속하시겠습니까? "
|
||||
incorrectPassword: "비밀번호가 올바르지 않습니다."
|
||||
incorrectTotp: "OTP 번호가 틀렸거나 유효기간이 만료되어 있을 수 있습니다."
|
||||
voteConfirm: "\"{choice}\"에 투표하시겠습니까?"
|
||||
hide: "숨기기"
|
||||
useDrawerReactionPickerForMobile: "모바일에서 드로어 메뉴로 표시"
|
||||
@@ -1120,7 +1122,7 @@ preservedUsernames: "예약한 사용자 이름"
|
||||
preservedUsernamesDescription: "예약할 사용자명을 한 줄에 하나씩 입력합니다. 여기에서 지정한 사용자명으로는 계정을 생성할 수 없게 됩니다. 단, 관리자 권한으로 계정을 생성할 때에는 해당되지 않으며, 이미 존재하는 계정도 영향을 받지 않습니다."
|
||||
createNoteFromTheFile: "이 파일로 노트를 작성"
|
||||
archive: "아카이브"
|
||||
archived: "보관됨"
|
||||
archived: "아카이브 됨"
|
||||
unarchive: "보관 취소"
|
||||
channelArchiveConfirmTitle: "{name} 채널을 보존하시겠습니까?"
|
||||
channelArchiveConfirmDescription: "보존한 채널은 채널 목록과 검색 결과에 표시되지 않으며 새로운 노트도 작성할 수 없습니다."
|
||||
@@ -1284,6 +1286,14 @@ unknownWebAuthnKey: "등록되지 않은 패스키입니다."
|
||||
passkeyVerificationFailed: "패스키 검증을 실패했습니다."
|
||||
passkeyVerificationSucceededButPasswordlessLoginDisabled: "패스키를 검증했으나, 비밀번호 없이 로그인하기가 꺼져 있습니다."
|
||||
messageToFollower: "팔로워에 보낼 메시지"
|
||||
target: "대상"
|
||||
_abuseUserReport:
|
||||
forward: "전달"
|
||||
forwardDescription: "익명 시스템 계정을 사용하여 리모트 서버에 신고 내용을 전달할 수 있습니다."
|
||||
resolve: "해결됨"
|
||||
accept: "인용"
|
||||
reject: "기각"
|
||||
resolveTutorial: "적절한 신고 내용에 대응한 경우, \"인용\"을 선택하여 \"해결됨\"으로 기록합니다.\n적절하지 않은 신고를 받은 경우, \"기각\"을 선택하여 \"기각\"으로 기록합니다."
|
||||
_delivery:
|
||||
status: "전송 상태"
|
||||
stop: "정지됨"
|
||||
@@ -1974,7 +1984,6 @@ _theme:
|
||||
buttonBg: "버튼 배경"
|
||||
buttonHoverBg: "버튼 배경 (호버)"
|
||||
inputBorder: "입력 필드 테두리"
|
||||
listItemHoverBg: "리스트 항목 배경 (호버)"
|
||||
driveFolderBg: "드라이브 폴더 배경"
|
||||
wallpaperOverlay: "배경화면 오버레이"
|
||||
badge: "배지"
|
||||
@@ -1990,7 +1999,7 @@ _sfx:
|
||||
_soundSettings:
|
||||
driveFile: "드라이브에 있는 오디오를 사용"
|
||||
driveFileWarn: "드라이브에 있는 파일을 선택하세요."
|
||||
driveFileTypeWarn: "이 파일은 지원되지 않습니다."
|
||||
driveFileTypeWarn: "이 파이"
|
||||
driveFileTypeWarnDescription: "오디오 파일을 선택하세요."
|
||||
driveFileDurationWarn: "오디오가 너무 깁니다"
|
||||
driveFileDurationWarnDescription: "긴 오디오로 설정할 경우 미스키 사용에 지장이 갈 수도 있습니다. 그래도 괜찮습니까?"
|
||||
@@ -2393,6 +2402,7 @@ _notification:
|
||||
followedBySomeUsers: "{n}명에게 팔로우됨"
|
||||
flushNotification: "알림 이력을 초기화"
|
||||
exportOfXCompleted: "{x} 추출에 성공했습니다."
|
||||
login: "로그인 알림이 있습니다"
|
||||
_types:
|
||||
all: "전부"
|
||||
note: "사용자의 새 글"
|
||||
@@ -2472,7 +2482,7 @@ _webhookSettings:
|
||||
reaction: "누군가 내 노트에 리액션했을 때"
|
||||
mention: "누군가 나를 멘션했을 때"
|
||||
_systemEvents:
|
||||
abuseReport: "유저로부터 신고를 받았을 때"
|
||||
abuseReport: "유저롭"
|
||||
abuseReportResolved: "받은 신고를 처리했을 때"
|
||||
userCreated: "유저가 생성되었을 때"
|
||||
deleteConfirm: "Webhook을 삭제할까요?"
|
||||
@@ -2520,6 +2530,8 @@ _moderationLogTypes:
|
||||
markSensitiveDriveFile: "파일에 열람주의를 설정"
|
||||
unmarkSensitiveDriveFile: "파일에 열람주의를 해제"
|
||||
resolveAbuseReport: "신고 처리"
|
||||
forwardAbuseReport: "신고 전달"
|
||||
updateAbuseReportNote: "신고 조정 노트 갱신"
|
||||
createInvitation: "초대 코드 생성"
|
||||
createAd: "광고 생성"
|
||||
deleteAd: "광고 삭제"
|
||||
@@ -2659,7 +2671,7 @@ _urlPreviewSetting:
|
||||
timeoutDescription: "미리보기를 로딩하는데 걸리는 시간이 정한 시간보다 오래 걸리는 경우, 미리보기를 생성하지 않습니다."
|
||||
maximumContentLength: "Content-Length의 최대치 (byte)"
|
||||
maximumContentLengthDescription: "Content-Length가 이 값을 넘어서면 미리보기를 생성하지 않습니다."
|
||||
requireContentLength: "Content-Length를 얻었을 때만 미리보기 만들기"
|
||||
requireContentLength: "Content-Length를 받아온 경우에만 "
|
||||
requireContentLengthDescription: "상대 서버가 Content-Length를 되돌려주지 않는다면 미리보기를 만들지 않습니다."
|
||||
userAgent: "User-Agent"
|
||||
userAgentDescription: "미리보기를 얻을 때 사용한 User-Agent를 설정합니다. 비어 있다면 기본값의 User-Agent를 사용합니다."
|
||||
|
@@ -689,10 +689,7 @@ abuseReported: "Twoje zgłoszenie zostało wysłane. Dziękujemy."
|
||||
reporter: "Zgłaszający"
|
||||
reporteeOrigin: "Pochodzenie zgłoszonego"
|
||||
reporterOrigin: "Pochodzenie zgłaszającego"
|
||||
forwardReport: "Przekaż zgłoszenie do innej instancji"
|
||||
forwardReportIsAnonymous: "Zamiast twojego konta, anonimowe konto systemowe będzie wyświetlone jako zgłaszający na instancji zdalnej."
|
||||
send: "Wyślij"
|
||||
abuseMarkAsResolved: "Oznacz zgłoszenie jako rozwiązane"
|
||||
openInNewTab: "Otwórz w nowej karcie"
|
||||
openInSideView: "Otwórz w bocznym widoku"
|
||||
defaultNavigationBehaviour: "Domyślne zachowanie nawigacji"
|
||||
@@ -1208,7 +1205,6 @@ _theme:
|
||||
buttonBg: "Tło przycisku"
|
||||
buttonHoverBg: "Tło przycisku (po najechaniu)"
|
||||
inputBorder: "Obramowanie pola wejścia"
|
||||
listItemHoverBg: "Tło elementu listy (po najechaniu)"
|
||||
driveFolderBg: "Tło folderu na dysku"
|
||||
wallpaperOverlay: "Nakładka tapety"
|
||||
badge: "Odznaka"
|
||||
|
@@ -1,5 +1,5 @@
|
||||
---
|
||||
_lang_: "日本語"
|
||||
_lang_: "Português"
|
||||
headlineMisskey: "Uma rede ligada por notas"
|
||||
introMisskey: "Bem-vindo! O Misskey é um serviço de microblog descentralizado de código aberto.\nCrie \"notas\" para compartilhar o que está acontecendo agora ou para se expressar com todos à sua volta 📡\nVocê também pode adicionar rapidamente reações às notas de outras pessoas usando a função \"Reações\" 👍\nVamos explorar um novo mundo 🚀"
|
||||
poweredByMisskeyDescription: "{name} é uma instância da plataforma de código aberto <b>Misskey</b>."
|
||||
@@ -25,7 +25,7 @@ basicSettings: "Configurações básicas"
|
||||
otherSettings: "Outras configurações"
|
||||
openInWindow: "Abrir em um janela"
|
||||
profile: "Perfil"
|
||||
timeline: "Cronologia"
|
||||
timeline: "Linha do tempo"
|
||||
noAccountDescription: "Este usuário não tem uma descrição."
|
||||
login: "Iniciar sessão"
|
||||
loggingIn: "Iniciando sessão…"
|
||||
@@ -707,10 +707,7 @@ abuseReported: "Denúncia enviada. Obrigado por sua ajuda."
|
||||
reporter: "Denunciante"
|
||||
reporteeOrigin: "Origem da denúncia"
|
||||
reporterOrigin: "Origem do denunciante"
|
||||
forwardReport: "Encaminhar a denúncia para o servidor remoto"
|
||||
forwardReportIsAnonymous: "No servidor remoto, suas informações não serão visíveis, e você será apresentado como uma conta do sistema anônima."
|
||||
send: "Enviar"
|
||||
abuseMarkAsResolved: "Marcar denúncia como resolvida"
|
||||
openInNewTab: "Abrir em nova aba"
|
||||
openInSideView: "Abrir em visão lateral"
|
||||
defaultNavigationBehaviour: "Navegação padrão"
|
||||
@@ -1061,7 +1058,7 @@ resetPasswordConfirm: "Deseja realmente mudar a sua senha?"
|
||||
sensitiveWords: "Palavras sensíveis"
|
||||
sensitiveWordsDescription: "A visibilidade de todas as notas contendo as palavras configuradas será colocadas como \"Início\" automaticamente. Você pode listar várias delas separando-as por linha."
|
||||
sensitiveWordsDescription2: "Utilizar espaços irá criar expressões aditivas (AND) e cercar palavras-chave com barras irá transformá-las em expressões regulares (RegEx)"
|
||||
prohibitedWords: "Palavras proibídas"
|
||||
prohibitedWords: "Palavras proibidas"
|
||||
prohibitedWordsDescription: "Habilita um erro ao tentar publicar uma nota contendo as palavras escolhidas. Várias palavras podem ser escolhidas, separando-as por linha."
|
||||
prohibitedWordsDescription2: "Utilizar espaços irá criar expressões aditivas (AND) e cercar palavras-chave com barras irá transformá-las em expressões regulares (RegEx)"
|
||||
hiddenTags: "Hashtags escondidas"
|
||||
@@ -1419,7 +1416,7 @@ _achievements:
|
||||
_types:
|
||||
_notes1:
|
||||
title: "Configurando o meu misskey"
|
||||
description: "Post uma nota pela primeira vez"
|
||||
description: "Poste uma nota pela primeira vez"
|
||||
flavor: "Divirta-se com o Misskey!"
|
||||
_notes10:
|
||||
title: "Algumas notas"
|
||||
@@ -1947,7 +1944,6 @@ _theme:
|
||||
buttonBg: "Plano de fundo de botão"
|
||||
buttonHoverBg: "Plano de fundo de botão (Selecionado)"
|
||||
inputBorder: "Borda de campo digitável"
|
||||
listItemHoverBg: "Plano de fundo do item de uma lista (Selecionado)"
|
||||
driveFolderBg: "Plano de fundo da pasta no Drive"
|
||||
wallpaperOverlay: "Sobreposição do papel de parede."
|
||||
badge: "Emblema"
|
||||
|
@@ -625,10 +625,7 @@ abuseReported: "Raportul tău a fost trimis. Mulțumim."
|
||||
reporter: "Raportorul"
|
||||
reporteeOrigin: "Originea raportatului"
|
||||
reporterOrigin: "Originea raportorului"
|
||||
forwardReport: "Redirecționează raportul către instanța externă"
|
||||
forwardReportIsAnonymous: "În locul contului tău, va fi afișat un cont anonim, de sistem, ca raportor către instanța externă."
|
||||
send: "Trimite"
|
||||
abuseMarkAsResolved: "Marchează raportul ca rezolvat"
|
||||
openInNewTab: "Deschide în tab nou"
|
||||
openInSideView: "Deschide în vedere laterală"
|
||||
defaultNavigationBehaviour: "Comportament de navigare implicit"
|
||||
|
@@ -700,10 +700,7 @@ abuseReported: "Жалоба отправлена. Большое спасибо
|
||||
reporter: "Сообщивший"
|
||||
reporteeOrigin: "О ком сообщено"
|
||||
reporterOrigin: "Кто сообщил"
|
||||
forwardReport: "Отправить жалобу на инстанс автора."
|
||||
forwardReportIsAnonymous: "Жалоба на удалённый инстанс будет отправлена анонимно. Вместо ваших данных у получателя будет отображена системная учётная запись."
|
||||
send: "Отправить"
|
||||
abuseMarkAsResolved: "Отметить жалобу как решённую"
|
||||
openInNewTab: "Открыть в новой вкладке"
|
||||
openInSideView: "Открывать в боковой колонке"
|
||||
defaultNavigationBehaviour: "Поведение навигации по умолчанию"
|
||||
@@ -1697,7 +1694,6 @@ _theme:
|
||||
buttonBg: "Фон кнопки"
|
||||
buttonHoverBg: "Текст кнопки"
|
||||
inputBorder: "Рамка поля ввода"
|
||||
listItemHoverBg: "Фон пункта списка (под указателем)"
|
||||
driveFolderBg: "Фон папки «Диска»"
|
||||
wallpaperOverlay: "Слой обоев"
|
||||
badge: "Значок"
|
||||
|
@@ -631,10 +631,7 @@ abuseReported: "Vaše nahlásenie je odoslané. Veľmi pekne ďakujeme."
|
||||
reporter: "Nahlásil"
|
||||
reporteeOrigin: "Pôvod nahláseného"
|
||||
reporterOrigin: "Pôvod nahlasovača"
|
||||
forwardReport: "Preposlať nahlásenie na server"
|
||||
forwardReportIsAnonymous: "Namiesto vášho účtu bude zobrazený anonymný systémový účet na vzdialenom serveri ako autor nahlásenia."
|
||||
send: "Poslať"
|
||||
abuseMarkAsResolved: "Označiť nahlásenia ako vyriešené"
|
||||
openInNewTab: "Otvoriť v novom tabe"
|
||||
openInSideView: "Otvoriť v bočnom paneli"
|
||||
defaultNavigationBehaviour: "Predvolené správanie navigácie"
|
||||
@@ -1111,7 +1108,6 @@ _theme:
|
||||
buttonBg: "Pozadie tlačidla"
|
||||
buttonHoverBg: "Pozadie tlačidla (pod kurzorom)"
|
||||
inputBorder: "Okraj vstupného poľa"
|
||||
listItemHoverBg: "Pozadie položky zoznamu (pod kurzorom)"
|
||||
driveFolderBg: "Pozadie priečinu disku"
|
||||
wallpaperOverlay: "Vrstvenie pozadia"
|
||||
badge: "Odznak"
|
||||
|
@@ -707,10 +707,7 @@ abuseReported: "เราได้ส่งรายงานของคุณ
|
||||
reporter: "ผู้รายงาน"
|
||||
reporteeOrigin: "ปลายทางรายงาน"
|
||||
reporterOrigin: "แหล่งผู้รายงาน"
|
||||
forwardReport: "ส่งต่อรายงานไปยังเซิร์ฟเวอร์ระยะไกล"
|
||||
forwardReportIsAnonymous: "ข้อมูลของคุณจะไม่ปรากฏบนเซิร์ฟเวอร์ระยะไกลและปรากฏเป็นบัญชีระบบที่ไม่ระบุชื่อ"
|
||||
send: "ส่ง"
|
||||
abuseMarkAsResolved: "ทำเครื่องหมายรายงานว่าแก้ไขแล้ว"
|
||||
openInNewTab: "เปิดในแท็บใหม่"
|
||||
openInSideView: "เปิดในมุมมองด้านข้าง"
|
||||
defaultNavigationBehaviour: "พฤติกรรมการนำทางที่เป็นค่าเริ่มต้น"
|
||||
@@ -1946,7 +1943,6 @@ _theme:
|
||||
buttonBg: "ปุ่มพื้นหลัง"
|
||||
buttonHoverBg: "ปุ่มพื้นหลัง (โฮเวอร์)"
|
||||
inputBorder: "เส้นขอบของช่องป้อนข้อมูล"
|
||||
listItemHoverBg: "รายการไอเทมพื้นหลัง (โฮเวอร์)"
|
||||
driveFolderBg: "พื้นหลังโฟลเดอร์ไดรฟ์"
|
||||
wallpaperOverlay: "วอลล์เปเปอร์ซ้อนทับ"
|
||||
badge: "ตรา"
|
||||
|
@@ -630,10 +630,7 @@ abuseReported: "Дякуємо, вашу скаргу було відправл
|
||||
reporter: "Репортер"
|
||||
reporteeOrigin: "Про кого повідомлено"
|
||||
reporterOrigin: "Хто повідомив"
|
||||
forwardReport: "Переслати звіт на віддалений інстанс"
|
||||
forwardReportIsAnonymous: "Замість вашого облікового запису анонімний системний обліковий запис буде відображатися як доповідач на віддаленому інстансі"
|
||||
send: "Відправити"
|
||||
abuseMarkAsResolved: "Позначити скаргу як вирішену"
|
||||
openInNewTab: "Відкрити в новій вкладці"
|
||||
openInSideView: "Відкрити збоку"
|
||||
defaultNavigationBehaviour: "Поведінка навігації за замовчуванням"
|
||||
@@ -1305,7 +1302,6 @@ _theme:
|
||||
buttonBg: "Фон кнопки"
|
||||
buttonHoverBg: "Фон кнопки (при наведенні)"
|
||||
inputBorder: "Край поля вводу"
|
||||
listItemHoverBg: "Фон елементу в списку (при наведенні)"
|
||||
driveFolderBg: "Фон папки на диску"
|
||||
wallpaperOverlay: "Накладання шпалер"
|
||||
badge: "Значок"
|
||||
|
@@ -629,10 +629,7 @@ abuseReported: "Shikoyatingiz yetkazildi. Ma'lumot uchun rahmat."
|
||||
reporter: "Shikoyat qiluvchi"
|
||||
reporteeOrigin: "Xabarning kelib chiqishi"
|
||||
reporterOrigin: "Xabarchining joylashuvi"
|
||||
forwardReport: "Xabarni masofadagi serverga yuborish"
|
||||
forwardReportIsAnonymous: "Sizning yuborayotgan xabaringiz o'z akkountingiz emas balki anonim tarzda qoladi"
|
||||
send: "Yuborish"
|
||||
abuseMarkAsResolved: "Yuborilgan xabarni hal qilingan deb belgilash"
|
||||
openInNewTab: "Yangi tab da ochish"
|
||||
openInSideView: "Yon panelda ochish"
|
||||
defaultNavigationBehaviour: "Standart navigatsiya harakati"
|
||||
|
@@ -675,10 +675,7 @@ abuseReported: "Báo cáo đã được gửi. Cảm ơn bạn nhiều."
|
||||
reporter: "Người báo cáo"
|
||||
reporteeOrigin: "Bị báo cáo"
|
||||
reporterOrigin: "Máy chủ người báo cáo"
|
||||
forwardReport: "Chuyển tiếp báo cáo cho máy chủ từ xa"
|
||||
forwardReportIsAnonymous: "Thay vì tài khoản của bạn, một tài khoản hệ thống ẩn danh sẽ được hiển thị dưới dạng người báo cáo ở máy chủ từ xa."
|
||||
send: "Gửi"
|
||||
abuseMarkAsResolved: "Đánh dấu đã xử lý"
|
||||
openInNewTab: "Mở trong tab mới"
|
||||
openInSideView: "Mở trong thanh bên"
|
||||
defaultNavigationBehaviour: "Thao tác điều hướng mặc định"
|
||||
@@ -1549,7 +1546,6 @@ _theme:
|
||||
buttonBg: "Nền nút"
|
||||
buttonHoverBg: "Nền nút (Chạm)"
|
||||
inputBorder: "Đường viền khung soạn thảo"
|
||||
listItemHoverBg: "Nền mục liệt kê (Chạm)"
|
||||
driveFolderBg: "Nền thư mục Ổ đĩa"
|
||||
wallpaperOverlay: "Lớp phủ hình nền"
|
||||
badge: "Huy hiệu"
|
||||
|
@@ -8,6 +8,9 @@ search: "搜索"
|
||||
notifications: "通知"
|
||||
username: "用户名"
|
||||
password: "密码"
|
||||
initialPasswordForSetup: "初始化密码"
|
||||
initialPasswordIsIncorrect: "初始化密码不正确"
|
||||
initialPasswordForSetupDescription: "如果是自己安装的 Misskey,请输入配置文件里设好的密码。\n如果使用的是 Misskey 的托管服务等,请输入服务商提供的密码。\n如果没有设置密码,请留空并继续。"
|
||||
forgotPassword: "忘记密码"
|
||||
fetchingAsApObject: "在联邦宇宙查询中..."
|
||||
ok: "OK"
|
||||
@@ -451,6 +454,7 @@ totpDescription: "使用验证器输入一次性密码"
|
||||
moderator: "监察员"
|
||||
moderation: "管理"
|
||||
moderationNote: "管理笔记"
|
||||
moderationNoteDescription: "可以用来记录仅在管理员之间共享的笔记。"
|
||||
addModerationNote: "添加管理笔记"
|
||||
moderationLogs: "管理日志"
|
||||
nUsersMentioned: "{n} 被提到"
|
||||
@@ -716,10 +720,7 @@ abuseReported: "内容已发送。感谢您提交信息。"
|
||||
reporter: "举报者"
|
||||
reporteeOrigin: "举报来源"
|
||||
reporterOrigin: "举报者来源"
|
||||
forwardReport: "将该举报信息转发给远程服务器"
|
||||
forwardReportIsAnonymous: "在远程实例上显示的报告者是匿名的系统账号,而不是您的账号。"
|
||||
send: "发送"
|
||||
abuseMarkAsResolved: "处理完毕"
|
||||
openInNewTab: "在新标签页中打开"
|
||||
openInSideView: "在侧边栏中打开"
|
||||
defaultNavigationBehaviour: "默认导航"
|
||||
@@ -921,6 +922,7 @@ followersVisibility: "关注者的公开范围"
|
||||
continueThread: "查看更多帖子"
|
||||
deleteAccountConfirm: "将要删除账户。是否确认?"
|
||||
incorrectPassword: "密码错误"
|
||||
incorrectTotp: "一次性密码不正确或已过期"
|
||||
voteConfirm: "确定投给 “{choice}” ?"
|
||||
hide: "隐藏"
|
||||
useDrawerReactionPickerForMobile: "在移动设备上使用抽屉显示"
|
||||
@@ -1197,10 +1199,10 @@ followingOrFollower: "关注中或关注者"
|
||||
fileAttachedOnly: "仅限媒体"
|
||||
showRepliesToOthersInTimeline: "在时间线中包含给别人的回复"
|
||||
hideRepliesToOthersInTimeline: "在时间线中隐藏给别人的回复"
|
||||
showRepliesToOthersInTimelineAll: "在时间线中包含现在关注的所有人的回复"
|
||||
hideRepliesToOthersInTimelineAll: "在时间线中隐藏现在关注的所有人的回复"
|
||||
confirmShowRepliesAll: "此操作不可撤销。确认要在时间线中包含现在关注的所有人的回复吗?"
|
||||
confirmHideRepliesAll: "此操作不可撤销。确认要在时间线中隐藏现在关注的所有人的回复吗?"
|
||||
showRepliesToOthersInTimelineAll: "在时间线中显示所有现在关注的人的回复"
|
||||
hideRepliesToOthersInTimelineAll: "在时间线中隐藏所有现在关注的人的回复"
|
||||
confirmShowRepliesAll: "此操作不可撤销。确认要在时间线中显示所有现在关注的人的回复吗?"
|
||||
confirmHideRepliesAll: "此操作不可撤销。确认要在时间线中隐藏所有现在关注的人的回复吗?"
|
||||
externalServices: "外部服务"
|
||||
sourceCode: "源代码"
|
||||
sourceCodeIsNotYetProvided: "还未提供源代码。要解决此问题请联系管理员。"
|
||||
@@ -1284,6 +1286,14 @@ unknownWebAuthnKey: "此通行密钥未注册。"
|
||||
passkeyVerificationFailed: "验证通行密钥失败。"
|
||||
passkeyVerificationSucceededButPasswordlessLoginDisabled: "通行密钥验证成功,但账户未开启无密码登录。"
|
||||
messageToFollower: "给关注者的消息"
|
||||
target: "对象"
|
||||
_abuseUserReport:
|
||||
forward: "转发"
|
||||
forwardDescription: "目标是匿名系统账户,将把举报转发给远程服务器。"
|
||||
resolve: "解决"
|
||||
accept: "确认"
|
||||
reject: "拒绝"
|
||||
resolveTutorial: "如果举报内容有理且已解决,选择「确认」将案件以肯定的态度标记为已解决。\n如果举报内容站不住脚,选择「拒绝」将案件以否定的态度标记为已解决。"
|
||||
_delivery:
|
||||
status: "投递状态"
|
||||
stop: "停止投递"
|
||||
@@ -1620,7 +1630,7 @@ _achievements:
|
||||
_postedAt0min0sec:
|
||||
title: "报时"
|
||||
description: "在 0 点发布一篇帖子"
|
||||
flavor: "报时信号最后一响,零点整"
|
||||
flavor: "嘟 · 嘟 · 嘟 · 哔——"
|
||||
_selfQuote:
|
||||
title: "自我引用"
|
||||
description: "引用了自己的帖子"
|
||||
@@ -1974,7 +1984,6 @@ _theme:
|
||||
buttonBg: "按钮背景"
|
||||
buttonHoverBg: "按钮背景(悬停)"
|
||||
inputBorder: "输入框边框"
|
||||
listItemHoverBg: "下拉列表项目背景(悬停)"
|
||||
driveFolderBg: "网盘的文件夹背景"
|
||||
wallpaperOverlay: "壁纸叠加层"
|
||||
badge: "徽章"
|
||||
@@ -2393,6 +2402,7 @@ _notification:
|
||||
followedBySomeUsers: "被 {n} 人关注"
|
||||
flushNotification: "重置通知历史"
|
||||
exportOfXCompleted: "已完成 {x} 个导出"
|
||||
login: "有新的登录"
|
||||
_types:
|
||||
all: "全部"
|
||||
note: "用户的新帖子"
|
||||
@@ -2520,6 +2530,8 @@ _moderationLogTypes:
|
||||
markSensitiveDriveFile: "标记网盘文件为敏感媒体"
|
||||
unmarkSensitiveDriveFile: "取消标记网盘文件为敏感媒体"
|
||||
resolveAbuseReport: "处理举报"
|
||||
forwardAbuseReport: "转发举报"
|
||||
updateAbuseReportNote: "更新举报用管理笔记"
|
||||
createInvitation: "生成邀请码"
|
||||
createAd: "创建了广告"
|
||||
deleteAd: "删除了广告"
|
||||
|
@@ -8,6 +8,9 @@ search: "搜尋"
|
||||
notifications: "通知"
|
||||
username: "使用者名稱"
|
||||
password: "密碼"
|
||||
initialPasswordForSetup: "初始設定用的密碼"
|
||||
initialPasswordIsIncorrect: "初始設定用的密碼錯誤。"
|
||||
initialPasswordForSetupDescription: "如果您自己安裝了 Misskey,請使用您在設定檔中輸入的密碼。\n如果您使用 Misskey 的託管服務之類的服務,請使用提供的密碼。\n如果您尚未設定密碼,請將其留空並繼續。"
|
||||
forgotPassword: "忘記密碼"
|
||||
fetchingAsApObject: "從聯邦宇宙取得中..."
|
||||
ok: "OK"
|
||||
@@ -716,10 +719,7 @@ abuseReported: "檢舉完成。感謝您的報告。"
|
||||
reporter: "檢舉者"
|
||||
reporteeOrigin: "檢舉來源"
|
||||
reporterOrigin: "檢舉者來源"
|
||||
forwardReport: "將報告轉送給遠端伺服器"
|
||||
forwardReportIsAnonymous: "在遠端實例上看不到您的資訊,顯示的報告者是匿名的系统帳戶。"
|
||||
send: "發送"
|
||||
abuseMarkAsResolved: "處理完畢"
|
||||
openInNewTab: "在新分頁中開啟"
|
||||
openInSideView: "在側欄中開啟"
|
||||
defaultNavigationBehaviour: "預設導航"
|
||||
@@ -921,6 +921,7 @@ followersVisibility: "追隨者的可見性"
|
||||
continueThread: "查看更多貼文"
|
||||
deleteAccountConfirm: "將要刪除帳戶。是否確定?"
|
||||
incorrectPassword: "密碼錯誤。"
|
||||
incorrectTotp: "一次性密碼錯誤,或者已過期。"
|
||||
voteConfirm: "確定投給「{choice}」?"
|
||||
hide: "隱藏"
|
||||
useDrawerReactionPickerForMobile: "在移動設備上使用抽屜顯示"
|
||||
@@ -1974,7 +1975,6 @@ _theme:
|
||||
buttonBg: "按鈕背景"
|
||||
buttonHoverBg: "按鈕背景 (漂浮)"
|
||||
inputBorder: "輸入框邊框"
|
||||
listItemHoverBg: "列表物品背景 (漂浮)"
|
||||
driveFolderBg: "雲端硬碟文件夾背景"
|
||||
wallpaperOverlay: "壁紙覆蓋層"
|
||||
badge: "徽章"
|
||||
@@ -2393,6 +2393,7 @@ _notification:
|
||||
followedBySomeUsers: "被{n}人追隨了"
|
||||
flushNotification: "重置通知歷史紀錄"
|
||||
exportOfXCompleted: "{x} 的匯出已完成。"
|
||||
login: "已登入"
|
||||
_types:
|
||||
all: "全部 "
|
||||
note: "使用者的最新貼文"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2024.10.0-alpha.1",
|
||||
"version": "2024.10.1-alpha.0",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class RefineAbuseUserReport1728085812127 {
|
||||
name = 'RefineAbuseUserReport1728085812127'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "moderationNote" character varying(8192) NOT NULL DEFAULT ''`);
|
||||
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "resolvedAs" character varying(128)`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "resolvedAs"`);
|
||||
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "moderationNote"`);
|
||||
}
|
||||
}
|
@@ -101,7 +101,7 @@
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "1.20.3",
|
||||
"bullmq": "5.13.2",
|
||||
"bullmq": "5.15.0",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"cbor": "9.0.2",
|
||||
"chalk": "5.3.0",
|
||||
@@ -166,7 +166,7 @@
|
||||
"rename": "1.0.4",
|
||||
"rss-parser": "3.13.0",
|
||||
"rxjs": "7.8.1",
|
||||
"sanitize-html": "2.13.0",
|
||||
"sanitize-html": "2.13.1",
|
||||
"secure-json-parse": "2.7.0",
|
||||
"sharp": "0.33.5",
|
||||
"slacc": "0.0.10",
|
||||
@@ -194,7 +194,7 @@
|
||||
"@types/archiver": "6.0.2",
|
||||
"@types/bcryptjs": "2.4.6",
|
||||
"@types/body-parser": "1.19.5",
|
||||
"@types/color-convert": "2.0.3",
|
||||
"@types/color-convert": "2.0.4",
|
||||
"@types/content-disposition": "0.5.8",
|
||||
"@types/fluent-ffmpeg": "2.1.26",
|
||||
"@types/htmlescape": "1.1.3",
|
||||
|
@@ -22,6 +22,7 @@ import { RoleService } from '@/core/RoleService.js';
|
||||
import { RecipientMethod } from '@/models/AbuseReportNotificationRecipient.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { IdService } from './IdService.js';
|
||||
|
||||
@Injectable()
|
||||
@@ -42,6 +43,7 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
|
||||
private emailService: EmailService,
|
||||
private moderationLogService: ModerationLogService,
|
||||
private globalEventService: GlobalEventService,
|
||||
private userEntityService: UserEntityService,
|
||||
) {
|
||||
this.redisForSub.on('message', this.onMessage);
|
||||
}
|
||||
@@ -135,6 +137,26 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
|
||||
return;
|
||||
}
|
||||
|
||||
const usersMap = await this.userEntityService.packMany(
|
||||
[
|
||||
...new Set([
|
||||
...abuseReports.map(it => it.reporter ?? it.reporterId),
|
||||
...abuseReports.map(it => it.targetUser ?? it.targetUserId),
|
||||
...abuseReports.map(it => it.assignee ?? it.assigneeId),
|
||||
].filter(x => x != null)),
|
||||
],
|
||||
null,
|
||||
{ schema: 'UserLite' },
|
||||
).then(it => new Map(it.map(it => [it.id, it])));
|
||||
const convertedReports = abuseReports.map(it => {
|
||||
return {
|
||||
...it,
|
||||
reporter: usersMap.get(it.reporterId),
|
||||
targetUser: usersMap.get(it.targetUserId),
|
||||
assignee: it.assigneeId ? usersMap.get(it.assigneeId) : null,
|
||||
};
|
||||
});
|
||||
|
||||
const recipientWebhookIds = await this.fetchWebhookRecipients()
|
||||
.then(it => it
|
||||
.filter(it => it.isActive && it.systemWebhookId && it.method === 'webhook')
|
||||
@@ -142,7 +164,7 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
|
||||
.filter(x => x != null));
|
||||
for (const webhookId of recipientWebhookIds) {
|
||||
await Promise.all(
|
||||
abuseReports.map(it => {
|
||||
convertedReports.map(it => {
|
||||
return this.systemWebhookService.enqueueSystemWebhook(
|
||||
webhookId,
|
||||
type,
|
||||
|
@@ -20,8 +20,10 @@ export class AbuseReportService {
|
||||
constructor(
|
||||
@Inject(DI.abuseUserReportsRepository)
|
||||
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
||||
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
|
||||
private idService: IdService,
|
||||
private abuseReportNotificationService: AbuseReportNotificationService,
|
||||
private queueService: QueueService,
|
||||
@@ -77,16 +79,16 @@ export class AbuseReportService {
|
||||
* - SystemWebhook
|
||||
*
|
||||
* @param params 通報内容. もし複数件の通報に対応した時のために、あらかじめ複数件を処理できる前提で考える
|
||||
* @param operator 通報を処理したユーザ
|
||||
* @param moderator 通報を処理したユーザ
|
||||
* @see AbuseReportNotificationService.notify
|
||||
*/
|
||||
@bindThis
|
||||
public async resolve(
|
||||
params: {
|
||||
reportId: string;
|
||||
forward: boolean;
|
||||
resolvedAs: MiAbuseUserReport['resolvedAs'];
|
||||
}[],
|
||||
operator: MiUser,
|
||||
moderator: MiUser,
|
||||
) {
|
||||
const paramsMap = new Map(params.map(it => [it.reportId, it]));
|
||||
const reports = await this.abuseUserReportsRepository.findBy({
|
||||
@@ -99,25 +101,15 @@ export class AbuseReportService {
|
||||
|
||||
await this.abuseUserReportsRepository.update(report.id, {
|
||||
resolved: true,
|
||||
assigneeId: operator.id,
|
||||
forwarded: ps.forward && report.targetUserHost !== null,
|
||||
assigneeId: moderator.id,
|
||||
resolvedAs: ps.resolvedAs,
|
||||
});
|
||||
|
||||
if (ps.forward && report.targetUserHost != null) {
|
||||
const actor = await this.instanceActorService.getInstanceActor();
|
||||
const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
|
||||
|
||||
// eslint-disable-next-line
|
||||
const flag = this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment);
|
||||
const contextAssignedFlag = this.apRendererService.addContext(flag);
|
||||
this.queueService.deliver(actor, contextAssignedFlag, targetUser.inbox, false);
|
||||
}
|
||||
|
||||
this.moderationLogService
|
||||
.log(operator, 'resolveAbuseReport', {
|
||||
.log(moderator, 'resolveAbuseReport', {
|
||||
reportId: report.id,
|
||||
report: report,
|
||||
forwarded: ps.forward && report.targetUserHost !== null,
|
||||
resolvedAs: ps.resolvedAs,
|
||||
})
|
||||
.then();
|
||||
}
|
||||
@@ -125,4 +117,62 @@ export class AbuseReportService {
|
||||
return this.abuseUserReportsRepository.findBy({ id: In(reports.map(it => it.id)) })
|
||||
.then(reports => this.abuseReportNotificationService.notifySystemWebhook(reports, 'abuseReportResolved'));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async forward(
|
||||
reportId: MiAbuseUserReport['id'],
|
||||
moderator: MiUser,
|
||||
) {
|
||||
const report = await this.abuseUserReportsRepository.findOneByOrFail({ id: reportId });
|
||||
|
||||
if (report.targetUserHost == null) {
|
||||
throw new Error('The target user host is null.');
|
||||
}
|
||||
|
||||
if (report.forwarded) {
|
||||
throw new Error('The report has already been forwarded.');
|
||||
}
|
||||
|
||||
await this.abuseUserReportsRepository.update(report.id, {
|
||||
forwarded: true,
|
||||
});
|
||||
|
||||
const actor = await this.instanceActorService.getInstanceActor();
|
||||
const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
|
||||
|
||||
const flag = this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment);
|
||||
const contextAssignedFlag = this.apRendererService.addContext(flag);
|
||||
this.queueService.deliver(actor, contextAssignedFlag, targetUser.inbox, false);
|
||||
|
||||
this.moderationLogService
|
||||
.log(moderator, 'forwardAbuseReport', {
|
||||
reportId: report.id,
|
||||
report: report,
|
||||
})
|
||||
.then();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async update(
|
||||
reportId: MiAbuseUserReport['id'],
|
||||
params: {
|
||||
moderationNote?: MiAbuseUserReport['moderationNote'];
|
||||
},
|
||||
moderator: MiUser,
|
||||
) {
|
||||
const report = await this.abuseUserReportsRepository.findOneByOrFail({ id: reportId });
|
||||
|
||||
await this.abuseUserReportsRepository.update(report.id, {
|
||||
moderationNote: params.moderationNote,
|
||||
});
|
||||
|
||||
if (params.moderationNote != null && report.moderationNote !== params.moderationNote) {
|
||||
this.moderationLogService.log(moderator, 'updateAbuseReportNote', {
|
||||
reportId: report.id,
|
||||
report: report,
|
||||
before: report.moderationNote,
|
||||
after: params.moderationNote,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationSe
|
||||
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { UserSearchService } from '@/core/UserSearchService.js';
|
||||
import { WebhookTestService } from '@/core/WebhookTestService.js';
|
||||
import { FlashService } from '@/core/FlashService.js';
|
||||
import { AccountMoveService } from './AccountMoveService.js';
|
||||
import { AccountUpdateService } from './AccountUpdateService.js';
|
||||
import { AiService } from './AiService.js';
|
||||
@@ -217,6 +218,7 @@ const $SystemWebhookService: Provider = { provide: 'SystemWebhookService', useEx
|
||||
const $WebhookTestService: Provider = { provide: 'WebhookTestService', useExisting: WebhookTestService };
|
||||
const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService };
|
||||
const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: FileInfoService };
|
||||
const $FlashService: Provider = { provide: 'FlashService', useExisting: FlashService };
|
||||
const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService };
|
||||
const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService };
|
||||
const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService };
|
||||
@@ -367,6 +369,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
WebhookTestService,
|
||||
UtilityService,
|
||||
FileInfoService,
|
||||
FlashService,
|
||||
SearchService,
|
||||
ClipService,
|
||||
FeaturedService,
|
||||
@@ -513,6 +516,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
$WebhookTestService,
|
||||
$UtilityService,
|
||||
$FileInfoService,
|
||||
$FlashService,
|
||||
$SearchService,
|
||||
$ClipService,
|
||||
$FeaturedService,
|
||||
@@ -660,6 +664,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
WebhookTestService,
|
||||
UtilityService,
|
||||
FileInfoService,
|
||||
FlashService,
|
||||
SearchService,
|
||||
ClipService,
|
||||
FeaturedService,
|
||||
|
40
packages/backend/src/core/FlashService.ts
Normal file
40
packages/backend/src/core/FlashService.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { type FlashsRepository } from '@/models/_.js';
|
||||
|
||||
/**
|
||||
* MisskeyPlay関係のService
|
||||
*/
|
||||
@Injectable()
|
||||
export class FlashService {
|
||||
constructor(
|
||||
@Inject(DI.flashsRepository)
|
||||
private flashRepository: FlashsRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 人気のあるPlay一覧を取得する.
|
||||
*/
|
||||
public async featured(opts?: { offset?: number, limit: number }) {
|
||||
const builder = this.flashRepository.createQueryBuilder('flash')
|
||||
.andWhere('flash.likedCount > 0')
|
||||
.andWhere('flash.visibility = :visibility', { visibility: 'public' })
|
||||
.addOrderBy('flash.likedCount', 'DESC')
|
||||
.addOrderBy('flash.updatedAt', 'DESC')
|
||||
.addOrderBy('flash.id', 'DESC');
|
||||
|
||||
if (opts?.offset) {
|
||||
builder.skip(opts.offset);
|
||||
}
|
||||
|
||||
builder.take(opts?.limit ?? 10);
|
||||
|
||||
return await builder.getMany();
|
||||
}
|
||||
}
|
@@ -218,7 +218,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||
private utilityService: UtilityService,
|
||||
private userBlockingService: UserBlockingService,
|
||||
) {
|
||||
this.updateNotesCountQueue = new CollapsedQueue(60 * 1000 * 5, this.collapseNotesCount, this.performUpdateNotesCount);
|
||||
this.updateNotesCountQueue = new CollapsedQueue(process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseNotesCount, this.performUpdateNotesCount);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
@@ -15,8 +15,14 @@ import { QueueService } from '@/core/QueueService.js';
|
||||
|
||||
const oneDayMillis = 24 * 60 * 60 * 1000;
|
||||
|
||||
function generateAbuseReport(override?: Partial<MiAbuseUserReport>): MiAbuseUserReport {
|
||||
return {
|
||||
type AbuseUserReportDto = Omit<MiAbuseUserReport, 'targetUser' | 'reporter' | 'assignee'> & {
|
||||
targetUser: Packed<'UserLite'> | null,
|
||||
reporter: Packed<'UserLite'> | null,
|
||||
assignee: Packed<'UserLite'> | null,
|
||||
};
|
||||
|
||||
function generateAbuseReport(override?: Partial<MiAbuseUserReport>): AbuseUserReportDto {
|
||||
const result: MiAbuseUserReport = {
|
||||
id: 'dummy-abuse-report1',
|
||||
targetUserId: 'dummy-target-user',
|
||||
targetUser: null,
|
||||
@@ -29,8 +35,17 @@ function generateAbuseReport(override?: Partial<MiAbuseUserReport>): MiAbuseUser
|
||||
comment: 'This is a dummy report for testing purposes.',
|
||||
targetUserHost: null,
|
||||
reporterHost: null,
|
||||
resolvedAs: null,
|
||||
moderationNote: 'foo',
|
||||
...override,
|
||||
};
|
||||
|
||||
return {
|
||||
...result,
|
||||
targetUser: result.targetUser ? toPackedUserLite(result.targetUser) : null,
|
||||
reporter: result.reporter ? toPackedUserLite(result.reporter) : null,
|
||||
assignee: result.assignee ? toPackedUserLite(result.assignee) : null,
|
||||
};
|
||||
}
|
||||
|
||||
function generateDummyUser(override?: Partial<MiUser>): MiUser {
|
||||
@@ -268,7 +283,8 @@ const dummyUser3 = generateDummyUser({
|
||||
|
||||
@Injectable()
|
||||
export class WebhookTestService {
|
||||
public static NoSuchWebhookError = class extends Error {};
|
||||
public static NoSuchWebhookError = class extends Error {
|
||||
};
|
||||
|
||||
constructor(
|
||||
private userWebhookService: UserWebhookService,
|
||||
|
@@ -53,6 +53,8 @@ export class AbuseUserReportEntityService {
|
||||
schema: 'UserDetailedNotMe',
|
||||
}) : null,
|
||||
forwarded: report.forwarded,
|
||||
resolvedAs: report.resolvedAs,
|
||||
moderationNote: report.moderationNote,
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -5,10 +5,8 @@
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { FlashsRepository, FlashLikesRepository } from '@/models/_.js';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import type { FlashLikesRepository, FlashsRepository } from '@/models/_.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import type { } from '@/models/Blocking.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import type { MiFlash } from '@/models/Flash.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
@@ -20,10 +18,8 @@ export class FlashEntityService {
|
||||
constructor(
|
||||
@Inject(DI.flashsRepository)
|
||||
private flashsRepository: FlashsRepository,
|
||||
|
||||
@Inject(DI.flashLikesRepository)
|
||||
private flashLikesRepository: FlashLikesRepository,
|
||||
|
||||
private userEntityService: UserEntityService,
|
||||
private idService: IdService,
|
||||
) {
|
||||
@@ -34,25 +30,36 @@ export class FlashEntityService {
|
||||
src: MiFlash['id'] | MiFlash,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
hint?: {
|
||||
packedUser?: Packed<'UserLite'>
|
||||
packedUser?: Packed<'UserLite'>,
|
||||
likedFlashIds?: MiFlash['id'][],
|
||||
},
|
||||
): Promise<Packed<'Flash'>> {
|
||||
const meId = me ? me.id : null;
|
||||
const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
return await awaitAll({
|
||||
// { schema: 'UserDetailed' } すると無限ループするので注意
|
||||
const user = hint?.packedUser ?? await this.userEntityService.pack(flash.user ?? flash.userId, me);
|
||||
|
||||
let isLiked = undefined;
|
||||
if (meId) {
|
||||
isLiked = hint?.likedFlashIds
|
||||
? hint.likedFlashIds.includes(flash.id)
|
||||
: await this.flashLikesRepository.exists({ where: { flashId: flash.id, userId: meId } });
|
||||
}
|
||||
|
||||
return {
|
||||
id: flash.id,
|
||||
createdAt: this.idService.parse(flash.id).date.toISOString(),
|
||||
updatedAt: flash.updatedAt.toISOString(),
|
||||
userId: flash.userId,
|
||||
user: hint?.packedUser ?? this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
|
||||
user: user,
|
||||
title: flash.title,
|
||||
summary: flash.summary,
|
||||
script: flash.script,
|
||||
visibility: flash.visibility,
|
||||
likedCount: flash.likedCount,
|
||||
isLiked: meId ? await this.flashLikesRepository.exists({ where: { flashId: flash.id, userId: meId } }) : undefined,
|
||||
});
|
||||
isLiked: isLiked,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -63,7 +70,19 @@ export class FlashEntityService {
|
||||
const _users = flashes.map(({ user, userId }) => user ?? userId);
|
||||
const _userMap = await this.userEntityService.packMany(_users, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(flashes.map(flash => this.pack(flash, me, { packedUser: _userMap.get(flash.userId) })));
|
||||
const _likedFlashIds = me
|
||||
? await this.flashLikesRepository.createQueryBuilder('flashLike')
|
||||
.select('flashLike.flashId')
|
||||
.where('flashLike.userId = :userId', { userId: me.id })
|
||||
.getRawMany<{ flashLike_flashId: string }>()
|
||||
.then(likes => [...new Set(likes.map(like => like.flashLike_flashId))])
|
||||
: [];
|
||||
return Promise.all(
|
||||
flashes.map(flash => this.pack(flash, me, {
|
||||
packedUser: _userMap.get(flash.userId),
|
||||
likedFlashIds: _likedFlashIds,
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -545,11 +545,6 @@ export class UserEntityService implements OnModuleInit {
|
||||
publicReactions: this.isLocalUser(user) ? profile!.publicReactions : false, // https://github.com/misskey-dev/misskey/issues/12964
|
||||
followersVisibility: profile!.followersVisibility,
|
||||
followingVisibility: profile!.followingVisibility,
|
||||
twoFactorEnabled: profile!.twoFactorEnabled,
|
||||
usePasswordLessLogin: profile!.usePasswordLessLogin,
|
||||
securityKeys: profile!.twoFactorEnabled
|
||||
? this.userSecurityKeysRepository.countBy({ userId: user.id }).then(result => result >= 1)
|
||||
: false,
|
||||
roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).sort((a, b) => b.displayOrder - a.displayOrder).map(role => ({
|
||||
id: role.id,
|
||||
name: role.name,
|
||||
@@ -564,6 +559,14 @@ export class UserEntityService implements OnModuleInit {
|
||||
moderationNote: iAmModerator ? (profile!.moderationNote ?? '') : undefined,
|
||||
} : {}),
|
||||
|
||||
...(isDetailed && (isMe || iAmModerator) ? {
|
||||
twoFactorEnabled: profile!.twoFactorEnabled,
|
||||
usePasswordLessLogin: profile!.usePasswordLessLogin,
|
||||
securityKeys: profile!.twoFactorEnabled
|
||||
? this.userSecurityKeysRepository.countBy({ userId: user.id }).then(result => result >= 1)
|
||||
: false,
|
||||
} : {}),
|
||||
|
||||
...(isDetailed && isMe ? {
|
||||
avatarId: user.avatarId,
|
||||
bannerId: user.bannerId,
|
||||
|
@@ -50,6 +50,9 @@ export class MiAbuseUserReport {
|
||||
})
|
||||
public resolved: boolean;
|
||||
|
||||
/**
|
||||
* リモートサーバーに転送したかどうか
|
||||
*/
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
})
|
||||
@@ -60,6 +63,21 @@ export class MiAbuseUserReport {
|
||||
})
|
||||
public comment: string;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 8192, default: '',
|
||||
})
|
||||
public moderationNote: string;
|
||||
|
||||
/**
|
||||
* accept 是認 ... 通報内容が正当であり、肯定的に対応された
|
||||
* reject 否認 ... 通報内容が正当でなく、否定的に対応された
|
||||
* null ... その他
|
||||
*/
|
||||
@Column('varchar', {
|
||||
length: 128, nullable: true,
|
||||
})
|
||||
public resolvedAs: 'accept' | 'reject' | null;
|
||||
|
||||
//#region Denormalized fields
|
||||
@Index()
|
||||
@Column('varchar', {
|
||||
|
@@ -7,6 +7,9 @@ import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typ
|
||||
import { id } from './util/id.js';
|
||||
import { MiUser } from './User.js';
|
||||
|
||||
export const flashVisibility = ['public', 'private'] as const;
|
||||
export type FlashVisibility = typeof flashVisibility[number];
|
||||
|
||||
@Entity('flash')
|
||||
export class MiFlash {
|
||||
@PrimaryColumn(id())
|
||||
@@ -63,5 +66,5 @@ export class MiFlash {
|
||||
@Column('varchar', {
|
||||
length: 512, default: 'public',
|
||||
})
|
||||
public visibility: 'public' | 'private';
|
||||
public visibility: FlashVisibility;
|
||||
}
|
||||
|
@@ -346,21 +346,6 @@ export const packedUserDetailedNotMeOnlySchema = {
|
||||
nullable: false, optional: false,
|
||||
enum: ['public', 'followers', 'private'],
|
||||
},
|
||||
twoFactorEnabled: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: false,
|
||||
default: false,
|
||||
},
|
||||
usePasswordLessLogin: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: false,
|
||||
default: false,
|
||||
},
|
||||
securityKeys: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: false,
|
||||
default: false,
|
||||
},
|
||||
roles: {
|
||||
type: 'array',
|
||||
nullable: false, optional: false,
|
||||
@@ -382,6 +367,18 @@ export const packedUserDetailedNotMeOnlySchema = {
|
||||
type: 'string',
|
||||
nullable: false, optional: true,
|
||||
},
|
||||
twoFactorEnabled: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: true,
|
||||
},
|
||||
usePasswordLessLogin: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: true,
|
||||
},
|
||||
securityKeys: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: true,
|
||||
},
|
||||
//#region relations
|
||||
isFollowing: {
|
||||
type: 'boolean',
|
||||
@@ -630,6 +627,21 @@ export const packedMeDetailedOnlySchema = {
|
||||
nullable: false, optional: false,
|
||||
ref: 'RolePolicies',
|
||||
},
|
||||
twoFactorEnabled: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: false,
|
||||
default: false,
|
||||
},
|
||||
usePasswordLessLogin: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: false,
|
||||
default: false,
|
||||
},
|
||||
securityKeys: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: false,
|
||||
default: false,
|
||||
},
|
||||
//#region secrets
|
||||
email: {
|
||||
type: 'string',
|
||||
|
@@ -59,7 +59,7 @@ export class InboxProcessorService implements OnApplicationShutdown {
|
||||
private queueLoggerService: QueueLoggerService,
|
||||
) {
|
||||
this.logger = this.queueLoggerService.logger.createSubLogger('inbox');
|
||||
this.updateInstanceQueue = new CollapsedQueue(60 * 1000 * 5, this.collapseUpdateInstanceJobs, this.performUpdateInstance);
|
||||
this.updateInstanceQueue = new CollapsedQueue(process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseUpdateInstanceJobs, this.performUpdateInstance);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
@@ -125,7 +125,7 @@ export class ApiServerService {
|
||||
fastify.post<{
|
||||
Body: {
|
||||
username: string;
|
||||
password: string;
|
||||
password?: string;
|
||||
token?: string;
|
||||
credential?: AuthenticationResponseJSON;
|
||||
'hcaptcha-response'?: string;
|
||||
@@ -133,7 +133,7 @@ export class ApiServerService {
|
||||
'turnstile-response'?: string;
|
||||
'm-captcha-response'?: string;
|
||||
};
|
||||
}>('/signin', (request, reply) => this.signinApiService.signin(request, reply));
|
||||
}>('/signin-flow', (request, reply) => this.signinApiService.signin(request, reply));
|
||||
|
||||
fastify.post<{
|
||||
Body: {
|
||||
|
@@ -68,6 +68,8 @@ import * as ep___admin_relays_list from './endpoints/admin/relays/list.js';
|
||||
import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js';
|
||||
import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js';
|
||||
import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js';
|
||||
import * as ep___admin_forwardAbuseUserReport from './endpoints/admin/forward-abuse-user-report.js';
|
||||
import * as ep___admin_updateAbuseUserReport from './endpoints/admin/update-abuse-user-report.js';
|
||||
import * as ep___admin_sendEmail from './endpoints/admin/send-email.js';
|
||||
import * as ep___admin_serverInfo from './endpoints/admin/server-info.js';
|
||||
import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js';
|
||||
@@ -453,6 +455,8 @@ const $admin_relays_list: Provider = { provide: 'ep:admin/relays/list', useClass
|
||||
const $admin_relays_remove: Provider = { provide: 'ep:admin/relays/remove', useClass: ep___admin_relays_remove.default };
|
||||
const $admin_resetPassword: Provider = { provide: 'ep:admin/reset-password', useClass: ep___admin_resetPassword.default };
|
||||
const $admin_resolveAbuseUserReport: Provider = { provide: 'ep:admin/resolve-abuse-user-report', useClass: ep___admin_resolveAbuseUserReport.default };
|
||||
const $admin_forwardAbuseUserReport: Provider = { provide: 'ep:admin/forward-abuse-user-report', useClass: ep___admin_forwardAbuseUserReport.default };
|
||||
const $admin_updateAbuseUserReport: Provider = { provide: 'ep:admin/update-abuse-user-report', useClass: ep___admin_updateAbuseUserReport.default };
|
||||
const $admin_sendEmail: Provider = { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default };
|
||||
const $admin_serverInfo: Provider = { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default };
|
||||
const $admin_showModerationLogs: Provider = { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default };
|
||||
@@ -842,6 +846,8 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
||||
$admin_relays_remove,
|
||||
$admin_resetPassword,
|
||||
$admin_resolveAbuseUserReport,
|
||||
$admin_forwardAbuseUserReport,
|
||||
$admin_updateAbuseUserReport,
|
||||
$admin_sendEmail,
|
||||
$admin_serverInfo,
|
||||
$admin_showModerationLogs,
|
||||
@@ -1225,6 +1231,8 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
||||
$admin_relays_remove,
|
||||
$admin_resetPassword,
|
||||
$admin_resolveAbuseUserReport,
|
||||
$admin_forwardAbuseUserReport,
|
||||
$admin_updateAbuseUserReport,
|
||||
$admin_sendEmail,
|
||||
$admin_serverInfo,
|
||||
$admin_showModerationLogs,
|
||||
|
@@ -5,13 +5,14 @@
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import * as OTPAuth from 'otpauth';
|
||||
import { IsNull } from 'typeorm';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type {
|
||||
MiMeta,
|
||||
SigninsRepository,
|
||||
UserProfilesRepository,
|
||||
UserSecurityKeysRepository,
|
||||
UsersRepository,
|
||||
} from '@/models/_.js';
|
||||
import type { Config } from '@/config.js';
|
||||
@@ -43,6 +44,9 @@ export class SigninApiService {
|
||||
@Inject(DI.userProfilesRepository)
|
||||
private userProfilesRepository: UserProfilesRepository,
|
||||
|
||||
@Inject(DI.userSecurityKeysRepository)
|
||||
private userSecurityKeysRepository: UserSecurityKeysRepository,
|
||||
|
||||
@Inject(DI.signinsRepository)
|
||||
private signinsRepository: SigninsRepository,
|
||||
|
||||
@@ -60,7 +64,7 @@ export class SigninApiService {
|
||||
request: FastifyRequest<{
|
||||
Body: {
|
||||
username: string;
|
||||
password: string;
|
||||
password?: string;
|
||||
token?: string;
|
||||
credential?: AuthenticationResponseJSON;
|
||||
'hcaptcha-response'?: string;
|
||||
@@ -103,11 +107,6 @@ export class SigninApiService {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof password !== 'string') {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (token != null && typeof token !== 'string') {
|
||||
reply.code(400);
|
||||
return;
|
||||
@@ -132,11 +131,32 @@ export class SigninApiService {
|
||||
}
|
||||
|
||||
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
|
||||
const securityKeysAvailable = await this.userSecurityKeysRepository.countBy({ userId: user.id }).then(result => result >= 1);
|
||||
|
||||
if (password == null) {
|
||||
reply.code(200);
|
||||
if (profile.twoFactorEnabled) {
|
||||
return {
|
||||
finished: false,
|
||||
next: 'password',
|
||||
} satisfies Misskey.entities.SigninFlowResponse;
|
||||
} else {
|
||||
return {
|
||||
finished: false,
|
||||
next: 'captcha',
|
||||
} satisfies Misskey.entities.SigninFlowResponse;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof password !== 'string') {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(password, profile.password!);
|
||||
|
||||
const fail = async (status?: number, failure?: { id: string }) => {
|
||||
const fail = async (status?: number, failure?: { id: string; }) => {
|
||||
// Append signin history
|
||||
await this.signinsRepository.insert({
|
||||
id: this.idService.gen(),
|
||||
@@ -217,7 +237,7 @@ export class SigninApiService {
|
||||
id: '93b86c4b-72f9-40eb-9815-798928603d1e',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
} else if (securityKeysAvailable) {
|
||||
if (!same && !profile.usePasswordLessLogin) {
|
||||
return await fail(403, {
|
||||
id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c',
|
||||
@@ -227,7 +247,23 @@ export class SigninApiService {
|
||||
const authRequest = await this.webAuthnService.initiateAuthentication(user.id);
|
||||
|
||||
reply.code(200);
|
||||
return authRequest;
|
||||
return {
|
||||
finished: false,
|
||||
next: 'passkey',
|
||||
authRequest,
|
||||
} satisfies Misskey.entities.SigninFlowResponse;
|
||||
} else {
|
||||
if (!same || !profile.twoFactorEnabled) {
|
||||
return await fail(403, {
|
||||
id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c',
|
||||
});
|
||||
} else {
|
||||
reply.code(200);
|
||||
return {
|
||||
finished: false,
|
||||
next: 'totp',
|
||||
} satisfies Misskey.entities.SigninFlowResponse;
|
||||
}
|
||||
}
|
||||
// never get here
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { SigninsRepository, UserProfilesRepository } from '@/models/_.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
@@ -57,9 +58,10 @@ export class SigninService {
|
||||
|
||||
reply.code(200);
|
||||
return {
|
||||
finished: true,
|
||||
id: user.id,
|
||||
i: user.token,
|
||||
};
|
||||
i: user.token!,
|
||||
} satisfies Misskey.entities.SigninFlowResponse;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -74,6 +74,8 @@ import * as ep___admin_relays_list from './endpoints/admin/relays/list.js';
|
||||
import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js';
|
||||
import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js';
|
||||
import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js';
|
||||
import * as ep___admin_forwardAbuseUserReport from './endpoints/admin/forward-abuse-user-report.js';
|
||||
import * as ep___admin_updateAbuseUserReport from './endpoints/admin/update-abuse-user-report.js';
|
||||
import * as ep___admin_sendEmail from './endpoints/admin/send-email.js';
|
||||
import * as ep___admin_serverInfo from './endpoints/admin/server-info.js';
|
||||
import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js';
|
||||
@@ -457,6 +459,8 @@ const eps = [
|
||||
['admin/relays/remove', ep___admin_relays_remove],
|
||||
['admin/reset-password', ep___admin_resetPassword],
|
||||
['admin/resolve-abuse-user-report', ep___admin_resolveAbuseUserReport],
|
||||
['admin/forward-abuse-user-report', ep___admin_forwardAbuseUserReport],
|
||||
['admin/update-abuse-user-report', ep___admin_updateAbuseUserReport],
|
||||
['admin/send-email', ep___admin_sendEmail],
|
||||
['admin/server-info', ep___admin_serverInfo],
|
||||
['admin/show-moderation-logs', ep___admin_showModerationLogs],
|
||||
|
@@ -71,9 +71,22 @@ export const meta = {
|
||||
},
|
||||
assignee: {
|
||||
type: 'object',
|
||||
nullable: true, optional: true,
|
||||
nullable: true, optional: false,
|
||||
ref: 'UserDetailedNotMe',
|
||||
},
|
||||
forwarded: {
|
||||
type: 'boolean',
|
||||
nullable: false, optional: false,
|
||||
},
|
||||
resolvedAs: {
|
||||
type: 'string',
|
||||
nullable: true, optional: false,
|
||||
enum: ['accept', 'reject', null],
|
||||
},
|
||||
moderationNote: {
|
||||
type: 'string',
|
||||
nullable: false, optional: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -88,7 +101,6 @@ export const paramDef = {
|
||||
state: { type: 'string', nullable: true, default: null },
|
||||
reporterOrigin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'combined' },
|
||||
targetUserOrigin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'combined' },
|
||||
forwarded: { type: 'boolean', default: false },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { AbuseUserReportsRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import { AbuseReportService } from '@/core/AbuseReportService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
kind: 'write:admin:resolve-abuse-user-report',
|
||||
|
||||
errors: {
|
||||
noSuchAbuseReport: {
|
||||
message: 'No such abuse report.',
|
||||
code: 'NO_SUCH_ABUSE_REPORT',
|
||||
id: '8763e21b-d9bc-40be-acf6-54c1a6986493',
|
||||
kind: 'server',
|
||||
httpStatusCode: 404,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
reportId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['reportId'],
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
constructor(
|
||||
@Inject(DI.abuseUserReportsRepository)
|
||||
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
||||
private abuseReportService: AbuseReportService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId });
|
||||
if (!report) {
|
||||
throw new ApiError(meta.errors.noSuchAbuseReport);
|
||||
}
|
||||
|
||||
await this.abuseReportService.forward(report.id, me);
|
||||
});
|
||||
}
|
||||
}
|
@@ -32,7 +32,7 @@ export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
reportId: { type: 'string', format: 'misskey:id' },
|
||||
forward: { type: 'boolean', default: false },
|
||||
resolvedAs: { type: 'string', enum: ['accept', 'reject', null], nullable: true },
|
||||
},
|
||||
required: ['reportId'],
|
||||
} as const;
|
||||
@@ -50,7 +50,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new ApiError(meta.errors.noSuchAbuseReport);
|
||||
}
|
||||
|
||||
await this.abuseReportService.resolve([{ reportId: report.id, forward: ps.forward }], me);
|
||||
await this.abuseReportService.resolve([{ reportId: report.id, resolvedAs: ps.resolvedAs ?? null }], me);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { AbuseUserReportsRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import { AbuseReportService } from '@/core/AbuseReportService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
kind: 'write:admin:resolve-abuse-user-report',
|
||||
|
||||
errors: {
|
||||
noSuchAbuseReport: {
|
||||
message: 'No such abuse report.',
|
||||
code: 'NO_SUCH_ABUSE_REPORT',
|
||||
id: '15f51cf5-46d1-4b1d-a618-b35bcbed0662',
|
||||
kind: 'server',
|
||||
httpStatusCode: 404,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
reportId: { type: 'string', format: 'misskey:id' },
|
||||
moderationNote: { type: 'string' },
|
||||
},
|
||||
required: ['reportId'],
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
constructor(
|
||||
@Inject(DI.abuseUserReportsRepository)
|
||||
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
||||
private abuseReportService: AbuseReportService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId });
|
||||
if (!report) {
|
||||
throw new ApiError(meta.errors.noSuchAbuseReport);
|
||||
}
|
||||
|
||||
await this.abuseReportService.update(report.id, {
|
||||
moderationNote: ps.moderationNote,
|
||||
}, me);
|
||||
});
|
||||
}
|
||||
}
|
@@ -8,6 +8,7 @@ import type { FlashsRepository } from '@/models/_.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { FlashService } from '@/core/FlashService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['flash'],
|
||||
@@ -27,26 +28,25 @@ export const meta = {
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
properties: {
|
||||
offset: { type: 'integer', minimum: 0, default: 0 },
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
constructor(
|
||||
@Inject(DI.flashsRepository)
|
||||
private flashsRepository: FlashsRepository,
|
||||
|
||||
private flashService: FlashService,
|
||||
private flashEntityService: FlashEntityService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const query = this.flashsRepository.createQueryBuilder('flash')
|
||||
.andWhere('flash.likedCount > 0')
|
||||
.orderBy('flash.likedCount', 'DESC');
|
||||
|
||||
const flashs = await query.limit(10).getMany();
|
||||
|
||||
return await this.flashEntityService.packMany(flashs, me);
|
||||
const result = await this.flashService.featured({
|
||||
offset: ps.offset,
|
||||
limit: ps.limit,
|
||||
});
|
||||
return await this.flashEntityService.packMany(result, me);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -98,7 +98,7 @@
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme) {
|
||||
for (const [k, v] of Object.entries(JSON.parse(theme))) {
|
||||
document.documentElement.style.setProperty(`--${k}`, v.toString());
|
||||
document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
|
||||
|
||||
// HTMLの theme-color 適用
|
||||
if (k === 'htmlThemeColor') {
|
||||
|
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
html {
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
background-color: var(--MI_THEME-bg);
|
||||
color: var(--MI_THEME-fg);
|
||||
}
|
||||
|
||||
#splash {
|
||||
@@ -17,7 +17,7 @@ html {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
cursor: wait;
|
||||
background-color: var(--bg);
|
||||
background-color: var(--MI_THEME-bg);
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ html {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
transform: translateY(70px);
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
#splashSpinner > .spinner {
|
||||
|
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
html {
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
background-color: var(--MI_THEME-bg);
|
||||
color: var(--MI_THEME-fg);
|
||||
}
|
||||
|
||||
html.embed {
|
||||
@@ -24,7 +24,7 @@ html.embed {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
cursor: wait;
|
||||
background-color: var(--bg);
|
||||
background-color: var(--MI_THEME-bg);
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ html.embed #splash {
|
||||
box-sizing: border-box;
|
||||
min-height: 300px;
|
||||
border-radius: var(--radius, 12px);
|
||||
border: 1px solid var(--divider, #e8e8e8);
|
||||
border: 1px solid var(--MI_THEME-divider, #e8e8e8);
|
||||
}
|
||||
|
||||
html.embed.norounded #splash {
|
||||
@@ -67,7 +67,7 @@ html.embed.noborder #splash {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
transform: translateY(70px);
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
#splashSpinner > .spinner {
|
||||
|
@@ -99,6 +99,8 @@ export const moderationLogTypes = [
|
||||
'markSensitiveDriveFile',
|
||||
'unmarkSensitiveDriveFile',
|
||||
'resolveAbuseReport',
|
||||
'forwardAbuseReport',
|
||||
'updateAbuseReportNote',
|
||||
'createInvitation',
|
||||
'createAd',
|
||||
'updateAd',
|
||||
@@ -267,7 +269,18 @@ export type ModerationLogPayloads = {
|
||||
resolveAbuseReport: {
|
||||
reportId: string;
|
||||
report: any;
|
||||
forwarded: boolean;
|
||||
forwarded?: boolean;
|
||||
resolvedAs?: string | null;
|
||||
};
|
||||
forwardAbuseReport: {
|
||||
reportId: string;
|
||||
report: any;
|
||||
};
|
||||
updateAbuseReportNote: {
|
||||
reportId: string;
|
||||
report: any;
|
||||
before: string;
|
||||
after: string;
|
||||
};
|
||||
createInvitation: {
|
||||
invitations: any[];
|
||||
|
@@ -136,13 +136,7 @@ describe('2要素認証', () => {
|
||||
keyName: string,
|
||||
credentialId: Buffer,
|
||||
requestOptions: PublicKeyCredentialRequestOptionsJSON,
|
||||
}): {
|
||||
username: string,
|
||||
password: string,
|
||||
credential: AuthenticationResponseJSON,
|
||||
'g-recaptcha-response'?: string | null,
|
||||
'hcaptcha-response'?: string | null,
|
||||
} => {
|
||||
}): misskey.entities.SigninFlowRequest => {
|
||||
// AuthenticatorAssertionResponse.authenticatorData
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorAssertionResponse/authenticatorData
|
||||
const authenticatorData = Buffer.concat([
|
||||
@@ -202,17 +196,21 @@ describe('2要素認証', () => {
|
||||
}, alice);
|
||||
assert.strictEqual(doneResponse.status, 200);
|
||||
|
||||
const usersShowResponse = await api('users/show', {
|
||||
username,
|
||||
}, alice);
|
||||
assert.strictEqual(usersShowResponse.status, 200);
|
||||
assert.strictEqual((usersShowResponse.body as unknown as { twoFactorEnabled: boolean }).twoFactorEnabled, true);
|
||||
const signinWithoutTokenResponse = await api('signin-flow', {
|
||||
...signinParam(),
|
||||
});
|
||||
assert.strictEqual(signinWithoutTokenResponse.status, 200);
|
||||
assert.deepStrictEqual(signinWithoutTokenResponse.body, {
|
||||
finished: false,
|
||||
next: 'totp',
|
||||
});
|
||||
|
||||
const signinResponse = await api('signin', {
|
||||
const signinResponse = await api('signin-flow', {
|
||||
...signinParam(),
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
});
|
||||
assert.strictEqual(signinResponse.status, 200);
|
||||
assert.strictEqual(signinResponse.body.finished, true);
|
||||
assert.notEqual(signinResponse.body.i, undefined);
|
||||
|
||||
// 後片付け
|
||||
@@ -253,27 +251,23 @@ describe('2要素認証', () => {
|
||||
assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url'));
|
||||
assert.strictEqual(keyDoneResponse.body.name, keyName);
|
||||
|
||||
const usersShowResponse = await api('users/show', {
|
||||
username,
|
||||
});
|
||||
assert.strictEqual(usersShowResponse.status, 200);
|
||||
assert.strictEqual((usersShowResponse.body as unknown as { securityKeys: boolean }).securityKeys, true);
|
||||
|
||||
const signinResponse = await api('signin', {
|
||||
const signinResponse = await api('signin-flow', {
|
||||
...signinParam(),
|
||||
});
|
||||
assert.strictEqual(signinResponse.status, 200);
|
||||
assert.strictEqual(signinResponse.body.i, undefined);
|
||||
assert.notEqual((signinResponse.body as unknown as { challenge: unknown | undefined }).challenge, undefined);
|
||||
assert.notEqual((signinResponse.body as unknown as { allowCredentials: unknown | undefined }).allowCredentials, undefined);
|
||||
assert.strictEqual((signinResponse.body as unknown as { allowCredentials: {id: string}[] }).allowCredentials[0].id, credentialId.toString('base64url'));
|
||||
assert.strictEqual(signinResponse.body.finished, false);
|
||||
assert.strictEqual(signinResponse.body.next, 'passkey');
|
||||
assert.notEqual(signinResponse.body.authRequest.challenge, undefined);
|
||||
assert.notEqual(signinResponse.body.authRequest.allowCredentials, undefined);
|
||||
assert.strictEqual(signinResponse.body.authRequest.allowCredentials && signinResponse.body.authRequest.allowCredentials[0]?.id, credentialId.toString('base64url'));
|
||||
|
||||
const signinResponse2 = await api('signin', signinWithSecurityKeyParam({
|
||||
const signinResponse2 = await api('signin-flow', signinWithSecurityKeyParam({
|
||||
keyName,
|
||||
credentialId,
|
||||
requestOptions: signinResponse.body,
|
||||
} as any));
|
||||
requestOptions: signinResponse.body.authRequest,
|
||||
}));
|
||||
assert.strictEqual(signinResponse2.status, 200);
|
||||
assert.strictEqual(signinResponse2.body.finished, true);
|
||||
assert.notEqual(signinResponse2.body.i, undefined);
|
||||
|
||||
// 後片付け
|
||||
@@ -315,28 +309,30 @@ describe('2要素認証', () => {
|
||||
}, alice);
|
||||
assert.strictEqual(passwordLessResponse.status, 204);
|
||||
|
||||
const usersShowResponse = await api('users/show', {
|
||||
username,
|
||||
});
|
||||
assert.strictEqual(usersShowResponse.status, 200);
|
||||
assert.strictEqual((usersShowResponse.body as unknown as { usePasswordLessLogin: boolean }).usePasswordLessLogin, true);
|
||||
const iResponse = await api('i', {}, alice);
|
||||
assert.strictEqual(iResponse.status, 200);
|
||||
assert.strictEqual(iResponse.body.usePasswordLessLogin, true);
|
||||
|
||||
const signinResponse = await api('signin', {
|
||||
const signinResponse = await api('signin-flow', {
|
||||
...signinParam(),
|
||||
password: '',
|
||||
});
|
||||
assert.strictEqual(signinResponse.status, 200);
|
||||
assert.strictEqual(signinResponse.body.i, undefined);
|
||||
assert.strictEqual(signinResponse.body.finished, false);
|
||||
assert.strictEqual(signinResponse.body.next, 'passkey');
|
||||
assert.notEqual(signinResponse.body.authRequest.challenge, undefined);
|
||||
assert.notEqual(signinResponse.body.authRequest.allowCredentials, undefined);
|
||||
|
||||
const signinResponse2 = await api('signin', {
|
||||
const signinResponse2 = await api('signin-flow', {
|
||||
...signinWithSecurityKeyParam({
|
||||
keyName,
|
||||
credentialId,
|
||||
requestOptions: signinResponse.body,
|
||||
requestOptions: signinResponse.body.authRequest,
|
||||
} as any),
|
||||
password: '',
|
||||
});
|
||||
assert.strictEqual(signinResponse2.status, 200);
|
||||
assert.strictEqual(signinResponse2.body.finished, true);
|
||||
assert.notEqual(signinResponse2.body.i, undefined);
|
||||
|
||||
// 後片付け
|
||||
@@ -424,11 +420,11 @@ describe('2要素認証', () => {
|
||||
assert.strictEqual(keyDoneResponse.status, 200);
|
||||
|
||||
// テストの実行順によっては複数残ってるので全部消す
|
||||
const iResponse = await api('i', {
|
||||
const beforeIResponse = await api('i', {
|
||||
}, alice);
|
||||
assert.strictEqual(iResponse.status, 200);
|
||||
assert.ok(iResponse.body.securityKeysList);
|
||||
for (const key of iResponse.body.securityKeysList) {
|
||||
assert.strictEqual(beforeIResponse.status, 200);
|
||||
assert.ok(beforeIResponse.body.securityKeysList);
|
||||
for (const key of beforeIResponse.body.securityKeysList) {
|
||||
const removeKeyResponse = await api('i/2fa/remove-key', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
password,
|
||||
@@ -437,17 +433,16 @@ describe('2要素認証', () => {
|
||||
assert.strictEqual(removeKeyResponse.status, 200);
|
||||
}
|
||||
|
||||
const usersShowResponse = await api('users/show', {
|
||||
username,
|
||||
});
|
||||
assert.strictEqual(usersShowResponse.status, 200);
|
||||
assert.strictEqual((usersShowResponse.body as unknown as { securityKeys: boolean }).securityKeys, false);
|
||||
const afterIResponse = await api('i', {}, alice);
|
||||
assert.strictEqual(afterIResponse.status, 200);
|
||||
assert.strictEqual(afterIResponse.body.securityKeys, false);
|
||||
|
||||
const signinResponse = await api('signin', {
|
||||
const signinResponse = await api('signin-flow', {
|
||||
...signinParam(),
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
});
|
||||
assert.strictEqual(signinResponse.status, 200);
|
||||
assert.strictEqual(signinResponse.body.finished, true);
|
||||
assert.notEqual(signinResponse.body.i, undefined);
|
||||
|
||||
// 後片付け
|
||||
@@ -468,11 +463,9 @@ describe('2要素認証', () => {
|
||||
}, alice);
|
||||
assert.strictEqual(doneResponse.status, 200);
|
||||
|
||||
const usersShowResponse = await api('users/show', {
|
||||
username,
|
||||
});
|
||||
assert.strictEqual(usersShowResponse.status, 200);
|
||||
assert.strictEqual((usersShowResponse.body as unknown as { twoFactorEnabled: boolean }).twoFactorEnabled, true);
|
||||
const iResponse = await api('i', {}, alice);
|
||||
assert.strictEqual(iResponse.status, 200);
|
||||
assert.strictEqual(iResponse.body.twoFactorEnabled, true);
|
||||
|
||||
const unregisterResponse = await api('i/2fa/unregister', {
|
||||
token: otpToken(registerResponse.body.secret),
|
||||
@@ -480,10 +473,11 @@ describe('2要素認証', () => {
|
||||
}, alice);
|
||||
assert.strictEqual(unregisterResponse.status, 204);
|
||||
|
||||
const signinResponse = await api('signin', {
|
||||
const signinResponse = await api('signin-flow', {
|
||||
...signinParam(),
|
||||
});
|
||||
assert.strictEqual(signinResponse.status, 200);
|
||||
assert.strictEqual(signinResponse.body.finished, true);
|
||||
assert.notEqual(signinResponse.body.i, undefined);
|
||||
|
||||
// 後片付け
|
||||
|
@@ -66,9 +66,9 @@ describe('Endpoints', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('signin', () => {
|
||||
describe('signin-flow', () => {
|
||||
test('間違ったパスワードでサインインできない', async () => {
|
||||
const res = await api('signin', {
|
||||
const res = await api('signin-flow', {
|
||||
username: 'test1',
|
||||
password: 'bar',
|
||||
});
|
||||
@@ -77,7 +77,7 @@ describe('Endpoints', () => {
|
||||
});
|
||||
|
||||
test('クエリをインジェクションできない', async () => {
|
||||
const res = await api('signin', {
|
||||
const res = await api('signin-flow', {
|
||||
username: 'test1',
|
||||
// @ts-expect-error password must be string
|
||||
password: {
|
||||
@@ -89,7 +89,7 @@ describe('Endpoints', () => {
|
||||
});
|
||||
|
||||
test('正しい情報でサインインできる', async () => {
|
||||
const res = await api('signin', {
|
||||
const res = await api('signin-flow', {
|
||||
username: 'test1',
|
||||
password: 'test1',
|
||||
});
|
||||
|
@@ -157,7 +157,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: webhookBody1.body.id,
|
||||
forward: false,
|
||||
}, admin);
|
||||
});
|
||||
|
||||
@@ -214,7 +213,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: abuseReportId,
|
||||
forward: false,
|
||||
}, admin);
|
||||
});
|
||||
|
||||
@@ -257,7 +255,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: webhookBody1.body.id,
|
||||
forward: false,
|
||||
}, admin);
|
||||
}).catch(e => e.message);
|
||||
|
||||
@@ -288,7 +285,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: abuseReportId,
|
||||
forward: false,
|
||||
}, admin);
|
||||
}).catch(e => e.message);
|
||||
|
||||
@@ -319,7 +315,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: abuseReportId,
|
||||
forward: false,
|
||||
}, admin);
|
||||
}).catch(e => e.message);
|
||||
|
||||
@@ -350,7 +345,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: abuseReportId,
|
||||
forward: false,
|
||||
}, admin);
|
||||
}).catch(e => e.message);
|
||||
|
||||
|
@@ -83,9 +83,6 @@ describe('ユーザー', () => {
|
||||
publicReactions: user.publicReactions,
|
||||
followingVisibility: user.followingVisibility,
|
||||
followersVisibility: user.followersVisibility,
|
||||
twoFactorEnabled: user.twoFactorEnabled,
|
||||
usePasswordLessLogin: user.usePasswordLessLogin,
|
||||
securityKeys: user.securityKeys,
|
||||
roles: user.roles,
|
||||
memo: user.memo,
|
||||
});
|
||||
@@ -149,6 +146,9 @@ describe('ユーザー', () => {
|
||||
achievements: user.achievements,
|
||||
loggedInDays: user.loggedInDays,
|
||||
policies: user.policies,
|
||||
twoFactorEnabled: user.twoFactorEnabled,
|
||||
usePasswordLessLogin: user.usePasswordLessLogin,
|
||||
securityKeys: user.securityKeys,
|
||||
...(security ? {
|
||||
email: user.email,
|
||||
emailVerified: user.emailVerified,
|
||||
@@ -343,9 +343,6 @@ describe('ユーザー', () => {
|
||||
assert.strictEqual(response.publicReactions, true);
|
||||
assert.strictEqual(response.followingVisibility, 'public');
|
||||
assert.strictEqual(response.followersVisibility, 'public');
|
||||
assert.strictEqual(response.twoFactorEnabled, false);
|
||||
assert.strictEqual(response.usePasswordLessLogin, false);
|
||||
assert.strictEqual(response.securityKeys, false);
|
||||
assert.deepStrictEqual(response.roles, []);
|
||||
assert.strictEqual(response.memo, null);
|
||||
|
||||
@@ -385,6 +382,9 @@ describe('ユーザー', () => {
|
||||
assert.deepStrictEqual(response.achievements, []);
|
||||
assert.deepStrictEqual(response.loggedInDays, 0);
|
||||
assert.deepStrictEqual(response.policies, DEFAULT_POLICIES);
|
||||
assert.strictEqual(response.twoFactorEnabled, false);
|
||||
assert.strictEqual(response.usePasswordLessLogin, false);
|
||||
assert.strictEqual(response.securityKeys, false);
|
||||
assert.notStrictEqual(response.email, undefined);
|
||||
assert.strictEqual(response.emailVerified, false);
|
||||
assert.deepStrictEqual(response.securityKeysList, []);
|
||||
@@ -618,6 +618,9 @@ describe('ユーザー', () => {
|
||||
{ label: 'Moderatorになっている', user: () => userModerator, me: () => userModerator, selector: (user: misskey.entities.MeDetailed) => user.isModerator },
|
||||
// @ts-expect-error UserDetailedNotMe doesn't include isModerator
|
||||
{ label: '自分以外から見たときはModeratorか判定できない', user: () => userModerator, selector: (user: misskey.entities.UserDetailedNotMe) => user.isModerator, expected: () => undefined },
|
||||
{ label: '自分から見た場合に二要素認証関連のプロパティがセットされている', user: () => alice, me: () => alice, selector: (user: misskey.entities.MeDetailed) => user.twoFactorEnabled, expected: () => false },
|
||||
{ label: '自分以外から見た場合に二要素認証関連のプロパティがセットされていない', user: () => alice, me: () => bob, selector: (user: misskey.entities.UserDetailedNotMe) => user.twoFactorEnabled, expected: () => undefined },
|
||||
{ label: 'モデレーターから見た場合に二要素認証関連のプロパティがセットされている', user: () => alice, me: () => userModerator, selector: (user: misskey.entities.UserDetailedNotMe) => user.twoFactorEnabled, expected: () => false },
|
||||
{ label: 'サイレンスになっている', user: () => userSilenced, selector: (user: misskey.entities.UserDetailed) => user.isSilenced },
|
||||
// FIXME: 落ちる
|
||||
//{ label: 'サスペンドになっている', user: () => userSuspended, selector: (user: misskey.entities.UserDetailed) => user.isSuspended },
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
import { jest } from '@jest/globals';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { randomString } from '../utils.js';
|
||||
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
|
||||
import {
|
||||
AbuseReportNotificationRecipientRepository,
|
||||
@@ -25,7 +26,7 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { RecipientMethod } from '@/models/AbuseReportNotificationRecipient.js';
|
||||
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { randomString } from '../utils.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
@@ -110,6 +111,9 @@ describe('AbuseReportNotificationService', () => {
|
||||
{
|
||||
provide: SystemWebhookService, useFactory: () => ({ enqueueSystemWebhook: jest.fn() }),
|
||||
},
|
||||
{
|
||||
provide: UserEntityService, useFactory: () => ({ pack: (v: any) => v }),
|
||||
},
|
||||
{
|
||||
provide: EmailService, useFactory: () => ({ sendEmail: jest.fn() }),
|
||||
},
|
||||
|
152
packages/backend/test/unit/FlashService.ts
Normal file
152
packages/backend/test/unit/FlashService.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { FlashService } from '@/core/FlashService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { FlashsRepository, MiFlash, MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
|
||||
describe('FlashService', () => {
|
||||
let app: TestingModule;
|
||||
let service: FlashService;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
let flashsRepository: FlashsRepository;
|
||||
let usersRepository: UsersRepository;
|
||||
let userProfilesRepository: UserProfilesRepository;
|
||||
let idService: IdService;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
let root: MiUser;
|
||||
let alice: MiUser;
|
||||
let bob: MiUser;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
async function createFlash(data: Partial<MiFlash>) {
|
||||
return flashsRepository.insert({
|
||||
id: idService.gen(),
|
||||
updatedAt: new Date(),
|
||||
userId: root.id,
|
||||
title: 'title',
|
||||
summary: 'summary',
|
||||
script: 'script',
|
||||
permissions: [],
|
||||
likedCount: 0,
|
||||
...data,
|
||||
}).then(x => flashsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}
|
||||
|
||||
async function createUser(data: Partial<MiUser> = {}) {
|
||||
const user = await usersRepository
|
||||
.insert({
|
||||
id: idService.gen(),
|
||||
...data,
|
||||
})
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
await userProfilesRepository.insert({
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
beforeEach(async () => {
|
||||
app = await Test.createTestingModule({
|
||||
imports: [
|
||||
GlobalModule,
|
||||
],
|
||||
providers: [
|
||||
FlashService,
|
||||
IdService,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = app.get(FlashService);
|
||||
|
||||
flashsRepository = app.get(DI.flashsRepository);
|
||||
usersRepository = app.get(DI.usersRepository);
|
||||
userProfilesRepository = app.get(DI.userProfilesRepository);
|
||||
idService = app.get(IdService);
|
||||
|
||||
root = await createUser({ username: 'root', usernameLower: 'root', isRoot: true });
|
||||
alice = await createUser({ username: 'alice', usernameLower: 'alice', isRoot: false });
|
||||
bob = await createUser({ username: 'bob', usernameLower: 'bob', isRoot: false });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await usersRepository.delete({});
|
||||
await userProfilesRepository.delete({});
|
||||
await flashsRepository.delete({});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
describe('featured', () => {
|
||||
test('should return featured flashes', async () => {
|
||||
const flash1 = await createFlash({ likedCount: 1 });
|
||||
const flash2 = await createFlash({ likedCount: 2 });
|
||||
const flash3 = await createFlash({ likedCount: 3 });
|
||||
|
||||
const result = await service.featured({
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
expect(result).toEqual([flash3, flash2, flash1]);
|
||||
});
|
||||
|
||||
test('should return featured flashes public visibility only', async () => {
|
||||
const flash1 = await createFlash({ likedCount: 1, visibility: 'public' });
|
||||
const flash2 = await createFlash({ likedCount: 2, visibility: 'public' });
|
||||
const flash3 = await createFlash({ likedCount: 3, visibility: 'private' });
|
||||
|
||||
const result = await service.featured({
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
expect(result).toEqual([flash2, flash1]);
|
||||
});
|
||||
|
||||
test('should return featured flashes with offset', async () => {
|
||||
const flash1 = await createFlash({ likedCount: 1 });
|
||||
const flash2 = await createFlash({ likedCount: 2 });
|
||||
const flash3 = await createFlash({ likedCount: 3 });
|
||||
|
||||
const result = await service.featured({
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
expect(result).toEqual([flash2, flash1]);
|
||||
});
|
||||
|
||||
test('should return featured flashes with limit', async () => {
|
||||
const flash1 = await createFlash({ likedCount: 1 });
|
||||
const flash2 = await createFlash({ likedCount: 2 });
|
||||
const flash3 = await createFlash({ likedCount: 3 });
|
||||
|
||||
const result = await service.featured({
|
||||
offset: 0,
|
||||
limit: 2,
|
||||
});
|
||||
|
||||
expect(result).toEqual([flash3, flash2]);
|
||||
});
|
||||
});
|
||||
});
|
@@ -18,7 +18,7 @@
|
||||
"@tabler/icons-webfont": "3.3.0",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"@vitejs/plugin-vue": "5.1.4",
|
||||
"@vue/compiler-sfc": "3.5.10",
|
||||
"@vue/compiler-sfc": "3.5.11",
|
||||
"astring": "1.9.0",
|
||||
"buraha": "0.0.1",
|
||||
"estree-walker": "3.0.3",
|
||||
@@ -27,8 +27,8 @@
|
||||
"frontend-shared": "workspace:*",
|
||||
"punycode": "2.3.1",
|
||||
"rollup": "4.22.5",
|
||||
"sass": "1.79.3",
|
||||
"shiki": "1.12.0",
|
||||
"sass": "1.79.4",
|
||||
"shiki": "1.21.0",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tsc-alias": "1.8.10",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
@@ -36,7 +36,7 @@
|
||||
"uuid": "10.0.0",
|
||||
"json5": "2.2.3",
|
||||
"vite": "5.4.8",
|
||||
"vue": "3.5.10"
|
||||
"vue": "3.5.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@misskey-dev/summaly": "5.1.0",
|
||||
@@ -51,10 +51,10 @@
|
||||
"@typescript-eslint/eslint-plugin": "7.17.0",
|
||||
"@typescript-eslint/parser": "7.17.0",
|
||||
"@vitest/coverage-v8": "1.6.0",
|
||||
"@vue/runtime-core": "3.5.10",
|
||||
"@vue/runtime-core": "3.5.11",
|
||||
"acorn": "8.12.1",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint-plugin-import": "2.30.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-vue": "9.28.0",
|
||||
"fast-glob": "3.3.2",
|
||||
"happy-dom": "10.0.3",
|
||||
|
@@ -38,8 +38,6 @@ const props = defineProps<{
|
||||
host?: string | null;
|
||||
url?: string;
|
||||
useOriginalSize?: boolean;
|
||||
menu?: boolean;
|
||||
menuReaction?: boolean;
|
||||
fallbackToImage?: boolean;
|
||||
}>();
|
||||
|
||||
|
@@ -56,7 +56,7 @@ const props = withDefaults(defineProps<{
|
||||
--size: 38px;
|
||||
|
||||
&.colored {
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
&.inline {
|
||||
|
@@ -33,15 +33,15 @@ defineProps<{
|
||||
width: 100%;
|
||||
padding: var(--margin);
|
||||
margin-top: 4px;
|
||||
border: 1px solid var(--inputBorder);
|
||||
border: 1px solid var(--MI_THEME-inputBorder);
|
||||
border-radius: var(--radius);
|
||||
background-color: var(--panel);
|
||||
background-color: var(--MI_THEME-panel);
|
||||
transition: background-color .1s, border-color .1s;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
border-color: var(--inputBorderHover);
|
||||
background-color: var(--buttonHoverBg);
|
||||
border-color: var(--MI_THEME-inputBorderHover);
|
||||
background-color: var(--MI_THEME-buttonHoverBg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div :class="$style.indicators">
|
||||
<div v-if="['image/gif', 'image/apng'].includes(image.type)" :class="$style.indicator">GIF</div>
|
||||
<div v-if="image.comment" :class="$style.indicator">ALT</div>
|
||||
<div v-if="image.isSensitive" :class="$style.indicator" style="color: var(--warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
|
||||
<div v-if="image.isSensitive" :class="$style.indicator" style="color: var(--MI_THEME-warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
|
||||
</div>
|
||||
<i v-if="!hide" class="ti ti-eye-off" :class="$style.hide" @click.stop="hide = true"></i>
|
||||
</div>
|
||||
@@ -94,8 +94,8 @@ async function onclick(ev: MouseEvent) {
|
||||
display: block;
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
background-color: var(--fg);
|
||||
color: var(--accentLighten);
|
||||
background-color: var(--MI_THEME-fg);
|
||||
color: var(--MI_THEME-accentLighten);
|
||||
font-size: 12px;
|
||||
opacity: .5;
|
||||
padding: 5px 8px;
|
||||
@@ -114,19 +114,19 @@ async function onclick(ev: MouseEvent) {
|
||||
|
||||
.visible {
|
||||
position: relative;
|
||||
//box-shadow: 0 0 0 1px var(--divider) inset;
|
||||
background: var(--bg);
|
||||
//box-shadow: 0 0 0 1px var(--MI_THEME-divider) inset;
|
||||
background: var(--MI_THEME-bg);
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
html[data-color-scheme=dark] .visible {
|
||||
--c: rgb(255 255 255 / 2%);
|
||||
background-image: linear-gradient(45deg, var(--c) 16.67%, var(--bg) 16.67%, var(--bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--bg) 66.67%, var(--bg) 100%);
|
||||
background-image: linear-gradient(45deg, var(--c) 16.67%, var(--MI_THEME-bg) 16.67%, var(--MI_THEME-bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--MI_THEME-bg) 66.67%, var(--MI_THEME-bg) 100%);
|
||||
}
|
||||
|
||||
html[data-color-scheme=light] .visible {
|
||||
--c: rgb(0 0 0 / 2%);
|
||||
background-image: linear-gradient(45deg, var(--c) 16.67%, var(--bg) 16.67%, var(--bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--bg) 66.67%, var(--bg) 100%);
|
||||
background-image: linear-gradient(45deg, var(--c) 16.67%, var(--MI_THEME-bg) 16.67%, var(--MI_THEME-bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--MI_THEME-bg) 66.67%, var(--MI_THEME-bg) 100%);
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
@@ -150,10 +150,10 @@ html[data-color-scheme=light] .visible {
|
||||
}
|
||||
|
||||
.indicator {
|
||||
/* Hardcode to black because either --bg or --fg makes it hard to read in dark/light mode */
|
||||
/* Hardcode to black because either --MI_THEME-bg or --MI_THEME-fg makes it hard to read in dark/light mode */
|
||||
background-color: black;
|
||||
border-radius: 6px;
|
||||
color: var(--accentLighten);
|
||||
color: var(--MI_THEME-accentLighten);
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
|
@@ -30,7 +30,7 @@ defineProps<{
|
||||
height: auto;
|
||||
aspect-ratio: 16 / 9;
|
||||
padding: var(--margin);
|
||||
border: 1px solid var(--divider);
|
||||
border: 1px solid var(--MI_THEME-divider);
|
||||
border-radius: var(--radius);
|
||||
background-color: #000;
|
||||
|
||||
@@ -49,7 +49,7 @@ defineProps<{
|
||||
}
|
||||
|
||||
.videoOverlayPlayButton {
|
||||
background: var(--accent);
|
||||
background: var(--MI_THEME-accent);
|
||||
color: #fff;
|
||||
padding: 1rem;
|
||||
border-radius: 99rem;
|
||||
|
@@ -27,7 +27,7 @@ const canonical = props.host === localHost ? `@${props.username}` : `@${props.us
|
||||
|
||||
const url = `/${canonical}`;
|
||||
|
||||
const bg = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--mention'));
|
||||
const bg = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-mention'));
|
||||
bg.setAlpha(0.1);
|
||||
const bgCss = bg.toRgbString();
|
||||
</script>
|
||||
@@ -37,7 +37,7 @@ const bgCss = bg.toRgbString();
|
||||
display: inline-block;
|
||||
padding: 4px 8px 4px 4px;
|
||||
border-radius: 999px;
|
||||
color: var(--mention);
|
||||
color: var(--MI_THEME-mention);
|
||||
}
|
||||
|
||||
.host {
|
||||
|
@@ -6,6 +6,7 @@
|
||||
import { VNode, h, SetupContext, provide } from 'vue';
|
||||
import * as mfm from 'mfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { host } from '@@/js/config.js';
|
||||
import EmUrl from '@/components/EmUrl.vue';
|
||||
import EmTime from '@/components/EmTime.vue';
|
||||
import EmLink from '@/components/EmLink.vue';
|
||||
@@ -13,7 +14,6 @@ import EmMention from '@/components/EmMention.vue';
|
||||
import EmEmoji from '@/components/EmEmoji.vue';
|
||||
import EmCustomEmoji from '@/components/EmCustomEmoji.vue';
|
||||
import EmA from '@/components/EmA.vue';
|
||||
import { host } from '@@/js/config.js';
|
||||
|
||||
function safeParseFloat(str: unknown): number | null {
|
||||
if (typeof str !== 'string' || str === '') return null;
|
||||
@@ -26,8 +26,8 @@ const QUOTE_STYLE = `
|
||||
display: block;
|
||||
margin: 8px;
|
||||
padding: 6px 0 6px 12px;
|
||||
color: var(--fg);
|
||||
border-left: solid 3px var(--fg);
|
||||
color: var(--MI_THEME-fg);
|
||||
border-left: solid 3px var(--MI_THEME-fg);
|
||||
opacity: 0.7;
|
||||
`.split('\n').join(' ');
|
||||
|
||||
@@ -41,9 +41,6 @@ type MfmProps = {
|
||||
rootScale?: number;
|
||||
nyaize?: boolean | 'respect';
|
||||
parsedNodes?: mfm.MfmNode[] | null;
|
||||
enableEmojiMenu?: boolean;
|
||||
enableEmojiMenuReaction?: boolean;
|
||||
linkNavigationBehavior?: string;
|
||||
};
|
||||
|
||||
type MfmEvents = {
|
||||
@@ -52,8 +49,6 @@ type MfmEvents = {
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEvents>['emit'] }) {
|
||||
provide('linkNavigationBehavior', props.linkNavigationBehavior);
|
||||
|
||||
const isNote = props.isNote ?? true;
|
||||
const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat : false : false;
|
||||
|
||||
@@ -256,7 +251,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
||||
}
|
||||
case 'border': {
|
||||
let color = validColor(token.props.args.color);
|
||||
color = color ? `#${color}` : 'var(--accent)';
|
||||
color = color ? `#${color}` : 'var(--MI_THEME-accent)';
|
||||
let b_style = token.props.args.style;
|
||||
if (
|
||||
typeof b_style !== 'string' ||
|
||||
@@ -289,7 +284,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
||||
const child = token.children[0];
|
||||
const unixtime = parseInt(child.type === 'text' ? child.props.text : '');
|
||||
return h('span', {
|
||||
style: 'display: inline-block; font-size: 90%; border: solid 1px var(--divider); border-radius: 999px; padding: 4px 10px 4px 6px;',
|
||||
style: 'display: inline-block; font-size: 90%; border: solid 1px var(--MI_THEME-divider); border-radius: 999px; padding: 4px 10px 4px 6px;',
|
||||
}, [
|
||||
h('i', {
|
||||
class: 'ti ti-clock',
|
||||
@@ -360,7 +355,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
||||
return [h(EmA, {
|
||||
key: Math.random(),
|
||||
to: isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`,
|
||||
style: 'color:var(--hashtag);',
|
||||
style: 'color:var(--MI_THEME-hashtag);',
|
||||
}, `#${token.props.hashtag}`)];
|
||||
}
|
||||
|
||||
@@ -397,8 +392,6 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
||||
normal: props.plain,
|
||||
host: null,
|
||||
useOriginalSize: scale >= 2.5,
|
||||
menu: props.enableEmojiMenu,
|
||||
menuReaction: props.enableEmojiMenuReaction,
|
||||
fallbackToImage: false,
|
||||
})];
|
||||
} else {
|
||||
|
@@ -189,7 +189,7 @@ const isDeleted = ref(false);
|
||||
margin: auto;
|
||||
width: calc(100% - 8px);
|
||||
height: calc(100% - 8px);
|
||||
border: dashed 2px var(--focus);
|
||||
border: dashed 2px var(--MI_THEME-focus);
|
||||
border-radius: var(--radius);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -212,9 +212,9 @@ const isDeleted = ref(false);
|
||||
right: 12px;
|
||||
padding: 0 4px;
|
||||
margin-bottom: 0 !important;
|
||||
background: var(--popup);
|
||||
background: var(--MI_THEME-popup);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 4px 32px var(--shadow);
|
||||
box-shadow: 0px 4px 32px var(--MI_THEME-shadow);
|
||||
}
|
||||
|
||||
.footerButton {
|
||||
@@ -259,7 +259,7 @@ const isDeleted = ref(false);
|
||||
padding: 16px 32px 8px 32px;
|
||||
line-height: 28px;
|
||||
white-space: pre;
|
||||
color: var(--renote);
|
||||
color: var(--MI_THEME-renote);
|
||||
|
||||
& + .article {
|
||||
padding-top: 8px;
|
||||
@@ -382,7 +382,7 @@ const isDeleted = ref(false);
|
||||
|
||||
.showLessLabel {
|
||||
display: inline-block;
|
||||
background: var(--popup);
|
||||
background: var(--MI_THEME-popup);
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8em;
|
||||
border-radius: 999px;
|
||||
@@ -403,16 +403,16 @@ const isDeleted = ref(false);
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
background: linear-gradient(0deg, var(--panel), color(from var(--panel) srgb r g b / 0));
|
||||
background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0));
|
||||
|
||||
&:hover > .collapsedLabel {
|
||||
background: var(--panelHighlight);
|
||||
background: var(--MI_THEME-panelHighlight);
|
||||
}
|
||||
}
|
||||
|
||||
.collapsedLabel {
|
||||
display: inline-block;
|
||||
background: var(--panel);
|
||||
background: var(--MI_THEME-panel);
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8em;
|
||||
border-radius: 999px;
|
||||
@@ -424,12 +424,12 @@ const isDeleted = ref(false);
|
||||
}
|
||||
|
||||
.replyIcon {
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.translation {
|
||||
border: solid 0.5px var(--divider);
|
||||
border: solid 0.5px var(--MI_THEME-divider);
|
||||
border-radius: var(--radius);
|
||||
padding: 12px;
|
||||
margin-top: 8px;
|
||||
@@ -449,7 +449,7 @@ const isDeleted = ref(false);
|
||||
|
||||
.quoteNote {
|
||||
padding: 16px;
|
||||
border: dashed 1px var(--renote);
|
||||
border: dashed 1px var(--MI_THEME-renote);
|
||||
border-radius: 8px;
|
||||
overflow: clip;
|
||||
}
|
||||
@@ -473,7 +473,7 @@ const isDeleted = ref(false);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
color: var(--MI_THEME-fgHighlighted);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -195,7 +195,7 @@ const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||
padding: 16px 32px 8px 32px;
|
||||
line-height: 28px;
|
||||
white-space: pre;
|
||||
color: var(--renote);
|
||||
color: var(--MI_THEME-renote);
|
||||
}
|
||||
|
||||
.renoteAvatar {
|
||||
@@ -281,7 +281,7 @@ const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||
padding: 4px 6px;
|
||||
font-size: 80%;
|
||||
line-height: 1;
|
||||
border: solid 0.5px var(--divider);
|
||||
border: solid 0.5px var(--MI_THEME-divider);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -323,14 +323,14 @@ const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||
}
|
||||
|
||||
.noteReplyTarget {
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.rn {
|
||||
margin-left: 4px;
|
||||
font-style: oblique;
|
||||
color: var(--renote);
|
||||
color: var(--MI_THEME-renote);
|
||||
}
|
||||
|
||||
.reactionOmitted {
|
||||
@@ -350,7 +350,7 @@ const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||
|
||||
.quoteNote {
|
||||
padding: 16px;
|
||||
border: dashed 1px var(--renote);
|
||||
border: dashed 1px var(--MI_THEME-renote);
|
||||
border-radius: 8px;
|
||||
overflow: clip;
|
||||
}
|
||||
@@ -369,7 +369,7 @@ const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||
|
||||
.showLessLabel {
|
||||
display: inline-block;
|
||||
background: var(--popup);
|
||||
background: var(--MI_THEME-popup);
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8em;
|
||||
border-radius: 999px;
|
||||
@@ -390,16 +390,16 @@ const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
background: linear-gradient(0deg, var(--panel), var(--X15));
|
||||
background: linear-gradient(0deg, var(--MI_THEME-panel), var(--MI_THEME-X15));
|
||||
|
||||
&:hover > .collapsedLabel {
|
||||
background: var(--panelHighlight);
|
||||
background: var(--MI_THEME-panelHighlight);
|
||||
}
|
||||
}
|
||||
|
||||
.collapsedLabel {
|
||||
display: inline-block;
|
||||
background: var(--panel);
|
||||
background: var(--MI_THEME-panel);
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8em;
|
||||
border-radius: 999px;
|
||||
@@ -422,7 +422,7 @@ const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
color: var(--MI_THEME-fgHighlighted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,7 +438,7 @@ const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||
opacity: 0.7;
|
||||
|
||||
&.reacted {
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -72,7 +72,7 @@ defineProps<{
|
||||
margin: 0 .5em 0 0;
|
||||
padding: 1px 6px;
|
||||
font-size: 80%;
|
||||
border: solid 0.5px var(--divider);
|
||||
border: solid 0.5px var(--MI_THEME-divider);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
|
@@ -123,7 +123,7 @@ if (props.detail) {
|
||||
}
|
||||
|
||||
.reply, .more {
|
||||
border-left: solid 0.5px var(--divider);
|
||||
border-left: solid 0.5px var(--MI_THEME-divider);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ if (props.detail) {
|
||||
.muted {
|
||||
text-align: center;
|
||||
padding: 8px !important;
|
||||
border: 1px solid var(--divider);
|
||||
border: 1px solid var(--MI_THEME-divider);
|
||||
margin: 8px 8px 0 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
@@ -43,10 +43,10 @@ defineExpose({
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
background: var(--panel);
|
||||
background: var(--MI_THEME-panel);
|
||||
}
|
||||
|
||||
.note {
|
||||
border-bottom: 0.5px solid var(--divider);
|
||||
border-bottom: 0.5px solid var(--MI_THEME-divider);
|
||||
}
|
||||
</style>
|
||||
|
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<li v-for="(choice, i) in poll.choices" :key="i" :class="$style.choice">
|
||||
<div :class="$style.bg" :style="{ 'width': `${choice.votes / total * 100}%` }"></div>
|
||||
<span :class="$style.fg">
|
||||
<template v-if="choice.isVoted"><i class="ti ti-check" style="margin-right: 4px; color: var(--accent);"></i></template>
|
||||
<template v-if="choice.isVoted"><i class="ti ti-check" style="margin-right: 4px; color: var(--MI_THEME-accent);"></i></template>
|
||||
<EmMfm :text="choice.text" :plain="true"/>
|
||||
<span style="margin-left: 4px; opacity: 0.7;">({{ i18n.tsx._poll.votesCount({ n: choice.votes }) }})</span>
|
||||
</span>
|
||||
@@ -52,8 +52,8 @@ const total = computed(() => sum(props.poll.choices.map(x => x.votes)));
|
||||
position: relative;
|
||||
margin: 4px 0;
|
||||
padding: 4px;
|
||||
//border: solid 0.5px var(--divider);
|
||||
background: var(--accentedBg);
|
||||
//border: solid 0.5px var(--MI_THEME-divider);
|
||||
background: var(--MI_THEME-accentedBg);
|
||||
border-radius: 4px;
|
||||
overflow: clip;
|
||||
}
|
||||
@@ -63,8 +63,8 @@ const total = computed(() => sum(props.poll.choices.map(x => x.votes)));
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
background: var(--accent);
|
||||
background: linear-gradient(90deg,var(--buttonGradateA),var(--buttonGradateB));
|
||||
background: var(--MI_THEME-accent);
|
||||
background: linear-gradient(90deg,var(--MI_THEME-buttonGradateA),var(--MI_THEME-buttonGradateB));
|
||||
transition: width 1s ease;
|
||||
}
|
||||
|
||||
@@ -72,11 +72,11 @@ const total = computed(() => sum(props.poll.choices.map(x => x.votes)));
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
background: var(--panel);
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: var(--fg);
|
||||
color: var(--MI_THEME-fg);
|
||||
}
|
||||
</style>
|
||||
|
@@ -38,7 +38,7 @@ const props = defineProps<{
|
||||
justify-content: center;
|
||||
|
||||
&.canToggle {
|
||||
background: var(--buttonBg);
|
||||
background: var(--MI_THEME-buttonBg);
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
@@ -72,12 +72,12 @@ const props = defineProps<{
|
||||
}
|
||||
|
||||
&.reacted, &.reacted:hover {
|
||||
background: var(--accentedBg);
|
||||
color: var(--accent);
|
||||
box-shadow: 0 0 0 1px var(--accent) inset;
|
||||
background: var(--MI_THEME-accentedBg);
|
||||
color: var(--MI_THEME-accent);
|
||||
box-shadow: 0 0 0 1px var(--MI_THEME-accent) inset;
|
||||
|
||||
> .count {
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
> .icon {
|
||||
|
@@ -65,11 +65,11 @@ const collapsed = ref(isLong);
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
background: linear-gradient(0deg, var(--panel), color(from var(--panel) srgb r g b / 0));
|
||||
background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0));
|
||||
|
||||
> .fadeLabel {
|
||||
display: inline-block;
|
||||
background: var(--panel);
|
||||
background: var(--MI_THEME-panel);
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8em;
|
||||
border-radius: 999px;
|
||||
@@ -78,7 +78,7 @@ const collapsed = ref(isLong);
|
||||
|
||||
&:hover {
|
||||
> .fadeLabel {
|
||||
background: var(--panelHighlight);
|
||||
background: var(--MI_THEME-panelHighlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,13 +87,13 @@ const collapsed = ref(isLong);
|
||||
|
||||
.reply {
|
||||
margin-right: 6px;
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
.rp {
|
||||
margin-left: 4px;
|
||||
font-style: oblique;
|
||||
color: var(--renote);
|
||||
color: var(--MI_THEME-renote);
|
||||
}
|
||||
|
||||
.showLess {
|
||||
@@ -105,7 +105,7 @@ const collapsed = ref(isLong);
|
||||
|
||||
.showLessLabel {
|
||||
display: inline-block;
|
||||
background: var(--popup);
|
||||
background: var(--MI_THEME-popup);
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8em;
|
||||
border-radius: 999px;
|
||||
|
@@ -98,10 +98,10 @@ if (!invalid && props.origin === null && (props.mode === 'relative' || props.mod
|
||||
|
||||
<style lang="scss" module>
|
||||
.old1 {
|
||||
color: var(--warn);
|
||||
color: var(--MI_THEME-warn);
|
||||
}
|
||||
|
||||
.old1.old2 {
|
||||
color: var(--error);
|
||||
color: var(--MI_THEME-error);
|
||||
}
|
||||
</style>
|
||||
|
@@ -20,7 +20,7 @@ withDefaults(defineProps<{
|
||||
|
||||
<style module lang="scss">
|
||||
.timelineRoot {
|
||||
background-color: var(--panel);
|
||||
background-color: var(--MI_THEME-panel);
|
||||
height: 100%;
|
||||
max-height: var(--embedMaxHeight, none);
|
||||
display: flex;
|
||||
@@ -29,7 +29,7 @@ withDefaults(defineProps<{
|
||||
|
||||
.header {
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid var(--divider);
|
||||
border-bottom: 1px solid var(--MI_THEME-divider);
|
||||
}
|
||||
|
||||
.body {
|
||||
|
@@ -110,8 +110,8 @@ function top(ev: MouseEvent) {
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
background-color: var(--accentedBg);
|
||||
color: var(--accent);
|
||||
background-color: var(--MI_THEME-accentedBg);
|
||||
color: var(--MI_THEME-accent);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
|
@@ -47,6 +47,6 @@ if (note.value?.url != null || note.value?.uri != null) {
|
||||
|
||||
<style lang="scss" module>
|
||||
.noteEmbedRoot {
|
||||
background-color: var(--panel);
|
||||
background-color: var(--MI_THEME-panel);
|
||||
}
|
||||
</style>
|
||||
|
@@ -93,8 +93,8 @@ function top(ev: MouseEvent) {
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
background-color: var(--accentedBg);
|
||||
color: var(--accent);
|
||||
background-color: var(--MI_THEME-accentedBg);
|
||||
color: var(--MI_THEME-accent);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
|
@@ -17,8 +17,8 @@
|
||||
html {
|
||||
background-color: transparent;
|
||||
color-scheme: light dark;
|
||||
color: var(--fg);
|
||||
accent-color: var(--accent);
|
||||
color: var(--MI_THEME-fg);
|
||||
accent-color: var(--MI_THEME-accent);
|
||||
overflow: clip;
|
||||
overflow-wrap: break-word;
|
||||
font-family: 'Hiragino Maru Gothic Pro', "BIZ UDGothic", Roboto, HelveticaNeue, Arial, sans-serif;
|
||||
@@ -29,7 +29,7 @@ html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
|
||||
&, * {
|
||||
scrollbar-color: var(--scrollbarHandle) transparent;
|
||||
scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
|
||||
scrollbar-width: thin;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
@@ -42,14 +42,14 @@ html {
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbarHandle);
|
||||
background: var(--MI_THEME-scrollbarHandle);
|
||||
|
||||
&:hover {
|
||||
background: var(--scrollbarHandleHover);
|
||||
background: var(--MI_THEME-scrollbarHandleHover);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--accent);
|
||||
background: var(--MI_THEME-accent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ rt {
|
||||
}
|
||||
|
||||
:focus-visible {
|
||||
outline: var(--focus) solid 2px;
|
||||
outline: var(--MI_THEME-focus) solid 2px;
|
||||
outline-offset: -2px;
|
||||
|
||||
&:hover {
|
||||
@@ -151,38 +151,38 @@ rt {
|
||||
|
||||
._buttonGray {
|
||||
@extend ._button;
|
||||
background: var(--buttonBg);
|
||||
background: var(--MI_THEME-buttonBg);
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background: var(--buttonHoverBg);
|
||||
background: var(--MI_THEME-buttonHoverBg);
|
||||
}
|
||||
}
|
||||
|
||||
._buttonPrimary {
|
||||
@extend ._button;
|
||||
color: var(--fgOnAccent);
|
||||
background: var(--accent);
|
||||
color: var(--MI_THEME-fgOnAccent);
|
||||
background: var(--MI_THEME-accent);
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background: hsl(from var(--accent) h s calc(l + 5));
|
||||
background: hsl(from var(--MI_THEME-accent) h s calc(l + 5));
|
||||
}
|
||||
|
||||
&:not(:disabled):active {
|
||||
background: hsl(from var(--accent) h s calc(l - 5));
|
||||
background: hsl(from var(--MI_THEME-accent) h s calc(l - 5));
|
||||
}
|
||||
}
|
||||
|
||||
._buttonGradate {
|
||||
@extend ._buttonPrimary;
|
||||
color: var(--fgOnAccent);
|
||||
background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
|
||||
color: var(--MI_THEME-fgOnAccent);
|
||||
background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
|
||||
background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
|
||||
}
|
||||
|
||||
&:not(:disabled):active {
|
||||
background: linear-gradient(90deg, hsl(from var(--accent) h s calc(l + 5)), hsl(from var(--accent) h s calc(l + 5)));
|
||||
background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,13 +199,13 @@ rt {
|
||||
}
|
||||
|
||||
._help {
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
._textButton {
|
||||
@extend ._button;
|
||||
color: var(--accent);
|
||||
color: var(--MI_THEME-accent);
|
||||
|
||||
&:focus-visible {
|
||||
outline-offset: 2px;
|
||||
@@ -217,7 +217,7 @@ rt {
|
||||
}
|
||||
|
||||
._panel {
|
||||
background: var(--panel);
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: var(--radius);
|
||||
overflow: clip;
|
||||
}
|
||||
@@ -263,22 +263,22 @@ rt {
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
border: solid 0.5px var(--divider);
|
||||
border: solid 0.5px var(--MI_THEME-divider);
|
||||
border-radius: var(--radius);
|
||||
|
||||
&:active {
|
||||
border-color: var(--accent);
|
||||
border-color: var(--MI_THEME-accent);
|
||||
}
|
||||
}
|
||||
|
||||
._popup {
|
||||
background: var(--popup);
|
||||
background: var(--MI_THEME-popup);
|
||||
border-radius: var(--radius);
|
||||
contain: content;
|
||||
}
|
||||
|
||||
._acrylic {
|
||||
background: var(--acrylicPanel);
|
||||
background: var(--MI_THEME-acrylicPanel);
|
||||
-webkit-backdrop-filter: var(--blur, blur(15px));
|
||||
backdrop-filter: var(--blur, blur(15px));
|
||||
}
|
||||
@@ -296,7 +296,7 @@ rt {
|
||||
}
|
||||
|
||||
._link {
|
||||
color: var(--link);
|
||||
color: var(--MI_THEME-link);
|
||||
}
|
||||
|
||||
._caption {
|
||||
|
@@ -61,7 +61,7 @@ export function applyTheme(theme: Theme, persist = true) {
|
||||
}
|
||||
|
||||
for (const [k, v] of Object.entries(props)) {
|
||||
document.documentElement.style.setProperty(`--${k}`, v.toString());
|
||||
document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
|
||||
}
|
||||
|
||||
// iframeを正常に透過させるために、cssのcolor-schemeは `light dark;` 固定にしてある。style.scss参照
|
||||
|
@@ -88,8 +88,8 @@ onUnmounted(() => {
|
||||
<style lang="scss" module>
|
||||
.rootForEmbedPage {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid var(--divider);
|
||||
background-color: var(--bg);
|
||||
border: 1px solid var(--MI_THEME-divider);
|
||||
background-color: var(--MI_THEME-bg);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
height: auto;
|
||||
|
@@ -30,7 +30,7 @@
|
||||
panelHeaderBg: ':lighten<3<@panel',
|
||||
panelHeaderFg: '@fg',
|
||||
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
|
||||
panelBorder: '" solid 1px var(--divider)',
|
||||
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
||||
acrylicPanel: ':alpha<0.5<@panel',
|
||||
windowHeader: ':alpha<0.85<@panel',
|
||||
popup: ':lighten<3<@panel',
|
||||
@@ -67,7 +67,6 @@
|
||||
switchOnFg: '@accent',
|
||||
inputBorder: 'rgba(255, 255, 255, 0.1)',
|
||||
inputBorderHover: 'rgba(255, 255, 255, 0.2)',
|
||||
listItemHoverBg: 'rgba(255, 255, 255, 0.03)',
|
||||
driveFolderBg: ':alpha<0.3<@accent',
|
||||
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
|
||||
badge: '#31b1ce',
|
||||
|
@@ -30,7 +30,7 @@
|
||||
panelHeaderBg: ':lighten<3<@panel',
|
||||
panelHeaderFg: '@fg',
|
||||
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
|
||||
panelBorder: '" solid 1px var(--divider)',
|
||||
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
||||
acrylicPanel: ':alpha<0.5<@panel',
|
||||
windowHeader: ':alpha<0.85<@panel',
|
||||
popup: ':lighten<3<@panel',
|
||||
@@ -67,7 +67,6 @@
|
||||
switchOnFg: '@fgOnAccent',
|
||||
inputBorder: 'rgba(0, 0, 0, 0.1)',
|
||||
inputBorderHover: 'rgba(0, 0, 0, 0.2)',
|
||||
listItemHoverBg: 'rgba(0, 0, 0, 0.03)',
|
||||
driveFolderBg: ':alpha<0.3<@accent',
|
||||
wallpaperOverlay: 'rgba(255, 255, 255, 0.5)',
|
||||
badge: '#31b1ce',
|
||||
|
@@ -36,7 +36,7 @@
|
||||
dateLabelFg: '@fg',
|
||||
inputBorder: 'rgba(255, 255, 255, 0.1)',
|
||||
inputBorderHover: 'rgba(255, 255, 255, 0.2)',
|
||||
panelBorder: '" solid 1px var(--divider)',
|
||||
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
||||
accentDarken: ':darken<10<@accent',
|
||||
acrylicPanel: ':alpha<0.5<@panel',
|
||||
navIndicator: '@accent',
|
||||
@@ -50,7 +50,6 @@
|
||||
htmlThemeColor: '@bg',
|
||||
fgOnWhite: '@accent',
|
||||
panelHighlight: ':lighten<3<@panel',
|
||||
listItemHoverBg: 'rgba(255, 255, 255, 0.03)',
|
||||
scrollbarHandle: 'rgba(255, 255, 255, 0.2)',
|
||||
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
|
||||
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
|
||||
|
@@ -55,7 +55,7 @@
|
||||
codeBoolean: '#c59eff',
|
||||
dateLabelFg: '@fg',
|
||||
inputBorder: 'rgba(255, 255, 255, 0.1)',
|
||||
panelBorder: '" solid 1px var(--divider)',
|
||||
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
||||
accentDarken: ':darken<10<@accent',
|
||||
acrylicPanel: ':alpha<0.5<@panel',
|
||||
navIndicator: '@indicator',
|
||||
@@ -69,7 +69,6 @@
|
||||
buttonGradateB: ':hue<20<@accent',
|
||||
htmlThemeColor: '@bg',
|
||||
panelHighlight: ':lighten<3<@panel',
|
||||
listItemHoverBg: 'rgba(255, 255, 255, 0.03)',
|
||||
scrollbarHandle: 'rgba(255, 255, 255, 0.2)',
|
||||
inputBorderHover: 'rgba(255, 255, 255, 0.2)',
|
||||
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
|
||||
|
@@ -56,7 +56,7 @@
|
||||
codeBoolean: '#c59eff',
|
||||
dateLabelFg: '@fg',
|
||||
inputBorder: 'rgba(255, 255, 255, 0.1)',
|
||||
panelBorder: '" solid 1px var(--divider)',
|
||||
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
||||
accentDarken: ':darken<10<@accent',
|
||||
acrylicPanel: ':alpha<0.5<@panel',
|
||||
navIndicator: '@indicator',
|
||||
@@ -71,7 +71,6 @@
|
||||
buttonGradateB: ':hue<20<@accent',
|
||||
htmlThemeColor: '@bg',
|
||||
panelHighlight: ':lighten<3<@panel',
|
||||
listItemHoverBg: 'rgba(255, 255, 255, 0.03)',
|
||||
scrollbarHandle: '#74747433',
|
||||
inputBorderHover: 'rgba(255, 255, 255, 0.2)',
|
||||
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
|
||||
|
@@ -39,7 +39,7 @@
|
||||
dateLabelFg: '@fg',
|
||||
inputBorder: 'rgba(0, 0, 0, 0.1)',
|
||||
inputBorderHover: 'rgba(0, 0, 0, 0.2)',
|
||||
panelBorder: '" solid 1px var(--divider)',
|
||||
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
||||
accentDarken: ':darken<10<@accent',
|
||||
acrylicPanel: ':alpha<0.5<@panel',
|
||||
navIndicator: '@accent',
|
||||
@@ -52,7 +52,6 @@
|
||||
panelHeaderFg: '@fg',
|
||||
htmlThemeColor: '@bg',
|
||||
panelHighlight: ':darken<3<@panel',
|
||||
listItemHoverBg: 'rgba(0, 0, 0, 0.03)',
|
||||
scrollbarHandle: 'rgba(0, 0, 0, 0.2)',
|
||||
wallpaperOverlay: 'rgba(255, 255, 255, 0.5)',
|
||||
fgTransparentWeak: ':alpha<0.75<@fg',
|
||||
|
@@ -397,7 +397,18 @@ function toStories(component: string): Promise<string> {
|
||||
const globs = await Promise.all([
|
||||
glob('src/components/global/Mk*.vue'),
|
||||
glob('src/components/global/RouterView.vue'),
|
||||
glob('src/components/Mk[A-E]*.vue'),
|
||||
glob('src/components/MkAbuseReportWindow.vue'),
|
||||
glob('src/components/MkAccountMoved.vue'),
|
||||
glob('src/components/MkAchievements.vue'),
|
||||
glob('src/components/MkAnalogClock.vue'),
|
||||
glob('src/components/MkAnimBg.vue'),
|
||||
glob('src/components/MkAnnouncementDialog.vue'),
|
||||
glob('src/components/MkAntennaEditor.vue'),
|
||||
glob('src/components/MkAntennaEditorDialog.vue'),
|
||||
glob('src/components/MkAsUi.vue'),
|
||||
glob('src/components/MkAutocomplete.vue'),
|
||||
glob('src/components/MkAvatars.vue'),
|
||||
glob('src/components/Mk[B-E]*.vue'),
|
||||
glob('src/components/MkFlashPreview.vue'),
|
||||
glob('src/components/MkGalleryPostPreview.vue'),
|
||||
glob('src/components/MkSignupServerRules.vue'),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user