Merge remote-tracking branch 'misskey-original/develop' into develop
# Conflicts: # packages/frontend/src/components/MkEmojiEditDialog.vue # packages/frontend/src/components/MkMenu.vue # packages/frontend/src/pages/about.vue # packages/frontend/src/pages/admin/index.vue # packages/frontend/src/pages/custom-emojis-manager.vue # packages/frontend/src/pages/settings/mute-block.vue # packages/frontend/src/pages/settings/theme.vue # packages/frontend/src/ui/_common_/navbar-for-mobile.vue
This commit is contained in:
commit
8d058b0529
6
.github/ISSUE_TEMPLATE/01_bug-report.yml
vendored
6
.github/ISSUE_TEMPLATE/01_bug-report.yml
vendored
|
@ -89,3 +89,9 @@ body:
|
||||||
render: markdown
|
render: markdown
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Do you want to address this bug yourself?
|
||||||
|
options:
|
||||||
|
- label: Yes, I will patch the bug myself and send a pull request
|
||||||
|
|
|
@ -14,4 +14,9 @@ body:
|
||||||
label: Purpose
|
label: Purpose
|
||||||
description: Describe the specific problem or need you think this feature will solve, and who it will help.
|
description: Describe the specific problem or need you think this feature will solve, and who it will help.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Do you want to implement this feature yourself?
|
||||||
|
options:
|
||||||
|
- label: Yes, I will implement this by myself and send a pull request
|
||||||
|
|
4
.github/workflows/get-api-diff.yml
vendored
4
.github/workflows/get-api-diff.yml
vendored
|
@ -56,7 +56,7 @@ jobs:
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: api-artifact
|
name: api-artifact-${{ matrix.api-json-name }}
|
||||||
path: ${{ matrix.api-json-name }}
|
path: ${{ matrix.api-json-name }}
|
||||||
|
|
||||||
save-pr-number:
|
save-pr-number:
|
||||||
|
@ -69,5 +69,5 @@ jobs:
|
||||||
echo "$PR_NUMBER" > ./pr_number
|
echo "$PR_NUMBER" > ./pr_number
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: api-artifact
|
name: api-artifact-pr-number
|
||||||
path: pr_number
|
path: pr_number
|
||||||
|
|
36
.github/workflows/report-api-diff.yml
vendored
36
.github/workflows/report-api-diff.yml
vendored
|
@ -19,24 +19,28 @@ jobs:
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
run_id: context.payload.workflow_run.id,
|
run_id: context.payload.workflow_run.id,
|
||||||
});
|
});
|
||||||
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
let matchArtifacts = allArtifacts.data.artifacts.filter((artifact) => {
|
||||||
return artifact.name == "api-artifact"
|
return artifact.name.startsWith("api-artifact-") || artifact.name == "api-artifact"
|
||||||
})[0];
|
|
||||||
let download = await github.rest.actions.downloadArtifact({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
artifact_id: matchArtifact.id,
|
|
||||||
archive_format: 'zip',
|
|
||||||
});
|
});
|
||||||
let fs = require('fs');
|
await Promise.all(matchArtifacts.map(async (artifact) => {
|
||||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/api-artifact.zip`, Buffer.from(download.data));
|
let download = await github.rest.actions.downloadArtifact({
|
||||||
- name: Extract artifact
|
owner: context.repo.owner,
|
||||||
run: unzip api-artifact.zip -d artifacts
|
repo: context.repo.repo,
|
||||||
|
artifact_id: artifact.id,
|
||||||
|
archive_format: 'zip',
|
||||||
|
});
|
||||||
|
await fs.promises.writeFile(`${process.env.GITHUB_WORKSPACE}/${artifact.name}.zip`, Buffer.from(download.data));
|
||||||
|
}));
|
||||||
|
- name: Extract all artifacts
|
||||||
|
run: |
|
||||||
|
find . -mindepth 1 -maxdepth 1 -type f -name '*.zip' -exec unzip {} -d artifacts ';'
|
||||||
|
ls -la
|
||||||
- name: Load PR Number
|
- name: Load PR Number
|
||||||
id: load-pr-num
|
id: load-pr-num
|
||||||
run: echo "pr-number=$(cat artifacts/pr_number)" >> "$GITHUB_OUTPUT"
|
run: echo "pr-number=$(cat artifacts/pr_number)" >> "$GITHUB_OUTPUT"
|
||||||
|
@ -83,3 +87,11 @@ jobs:
|
||||||
pr_number: ${{ steps.load-pr-num.outputs.pr-number }}
|
pr_number: ${{ steps.load-pr-num.outputs.pr-number }}
|
||||||
comment_tag: show_diff
|
comment_tag: show_diff
|
||||||
filePath: ./output.md
|
filePath: ./output.md
|
||||||
|
- name: Tell error to PR
|
||||||
|
uses: thollander/actions-comment-pull-request@v2
|
||||||
|
if: failure() && steps.load-pr-num.outputs.pr-number
|
||||||
|
with:
|
||||||
|
pr_number: ${{ steps.load-pr-num.outputs.pr-number }}
|
||||||
|
comment_tag: show_diff_error
|
||||||
|
message: |
|
||||||
|
api.jsonの差分作成中にエラーが発生しました。詳細は[Workflowのログ](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})を確認してください。
|
||||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -12,6 +12,22 @@
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## 2023.12.1
|
||||||
|
|
||||||
|
### General
|
||||||
|
- Enhance: ローカリゼーションの更新
|
||||||
|
- Fix: 自分のdirect noteがuser list timelineに追加されない
|
||||||
|
|
||||||
|
### Client
|
||||||
|
- Fix: 一部のモデログ(logYellowでの表示対象)について、表示の色が変わらない問題を修正
|
||||||
|
- Feat: AiScript専用のMFM構文`$[clickable.ev=EVENTNAME ...]`を追加。`Mk:C:mfm`のオプション`onClickEv`に関数を渡すと、クリック時に`EVENTNAME`を引数にして呼び出す
|
||||||
|
|
||||||
|
### Server
|
||||||
|
- Enhance: センシティブワードの設定がハッシュタグトレンドにも適用されるようになりました
|
||||||
|
- Fix: 1702718871541-ffVisibility.jsのdownが壊れている
|
||||||
|
- Fix:「非センシティブのみ(リモートはいいねのみ)」を設定していても、センシティブに設定されたカスタム絵文字をリアクションできる問題を修正
|
||||||
|
- Fix: ロールアサイン時の通知で,ロールアイコンが縮小されずに表示される問題を修正
|
||||||
|
|
||||||
## 2023.12.0
|
## 2023.12.0
|
||||||
|
|
||||||
### Note
|
### Note
|
||||||
|
@ -98,6 +114,7 @@
|
||||||
- Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
|
- Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
|
||||||
- Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように
|
- Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように
|
||||||
- Enhance: カスタム絵文字のインポート時の動作を改善
|
- Enhance: カスタム絵文字のインポート時の動作を改善
|
||||||
|
- Enhance: json-schema(OpenAPIの戻り値として使用されるスキーマ定義)を出来る限り最新化 #12311
|
||||||
- Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
|
- Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
|
||||||
- Fix: ロールタイムラインが保存されない問題を修正
|
- Fix: ロールタイムラインが保存されない問題を修正
|
||||||
- Fix: api.jsonの生成ロジックを改善 #12402
|
- Fix: api.jsonの生成ロジックを改善 #12402
|
||||||
|
@ -125,7 +142,6 @@
|
||||||
- Feat: 管理者がコントロールパネルからメールアドレスの照会を行えるようになりました
|
- Feat: 管理者がコントロールパネルからメールアドレスの照会を行えるようになりました
|
||||||
- Enhance: ローカリゼーションの更新
|
- Enhance: ローカリゼーションの更新
|
||||||
- Enhance: 依存関係の更新
|
- Enhance: 依存関係の更新
|
||||||
- Enhance: json-schema(OpenAPIの戻り値として使用されるスキーマ定義)を出来る限り最新化 #12311
|
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- Enhance: MFMでルビを振れるように
|
- Enhance: MFMでルビを振れるように
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# Reporting Security Issues
|
# Reporting Security Issues
|
||||||
|
|
||||||
If you discover a security issue in Misskey, please report it by sending an
|
If you discover a security issue in Misskey, please report it by **[this form](https://github.com/misskey-dev/misskey/security/advisories/new)**.
|
||||||
email to [syuilotan@yahoo.co.jp](mailto:syuilotan@yahoo.co.jp).
|
|
||||||
|
|
||||||
This will allow us to assess the risk, and make a fix available before we add a
|
This will allow us to assess the risk, and make a fix available before we add a
|
||||||
bug report to the GitHub repository.
|
bug report to the GitHub repository.
|
||||||
|
|
|
@ -121,6 +121,12 @@ sensitive: "Marcado como sensible"
|
||||||
add: "Agregar"
|
add: "Agregar"
|
||||||
reaction: "Reacción"
|
reaction: "Reacción"
|
||||||
reactions: "Reacción"
|
reactions: "Reacción"
|
||||||
|
emojiPicker: "Selector de emojis"
|
||||||
|
pinnedEmojisForReactionSettingDescription: "Puedes seleccionar reacciones para fijarlos en el selector"
|
||||||
|
pinnedEmojisSettingDescription: "Puedes seleccionar emojis para fijarlos en el selector"
|
||||||
|
emojiPickerDisplay: "Mostrar el selector de emojis"
|
||||||
|
overwriteFromPinnedEmojisForReaction: "Sobreescribir las reacciones fijadas"
|
||||||
|
overwriteFromPinnedEmojis: "Sobreescribir los emojis fijados"
|
||||||
reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir."
|
reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir."
|
||||||
rememberNoteVisibility: "Recordar visibilidad"
|
rememberNoteVisibility: "Recordar visibilidad"
|
||||||
attachCancel: "Quitar adjunto"
|
attachCancel: "Quitar adjunto"
|
||||||
|
@ -260,6 +266,7 @@ removed: "Borrado"
|
||||||
removeAreYouSure: "¿Desea borrar \"{x}\"?"
|
removeAreYouSure: "¿Desea borrar \"{x}\"?"
|
||||||
deleteAreYouSure: "¿Desea borrar \"{x}\"?"
|
deleteAreYouSure: "¿Desea borrar \"{x}\"?"
|
||||||
resetAreYouSure: "¿Desea reestablecer?"
|
resetAreYouSure: "¿Desea reestablecer?"
|
||||||
|
areYouSure: "¿Estás conforme?"
|
||||||
saved: "Guardado"
|
saved: "Guardado"
|
||||||
messaging: "Chat"
|
messaging: "Chat"
|
||||||
upload: "Subir"
|
upload: "Subir"
|
||||||
|
@ -640,6 +647,7 @@ smtpSecure: "Usar SSL/TLS implícito en la conexión SMTP"
|
||||||
smtpSecureInfo: "Apagar cuando se use STARTTLS"
|
smtpSecureInfo: "Apagar cuando se use STARTTLS"
|
||||||
testEmail: "Prueba de envío"
|
testEmail: "Prueba de envío"
|
||||||
wordMute: "Silenciar palabras"
|
wordMute: "Silenciar palabras"
|
||||||
|
hardWordMute: "Filtro de palabra fuerte"
|
||||||
regexpError: "Error de la expresión regular"
|
regexpError: "Error de la expresión regular"
|
||||||
regexpErrorDescription: "Ocurrió un error en la expresión regular en la linea {line} de las palabras muteadas {tab}"
|
regexpErrorDescription: "Ocurrió un error en la expresión regular en la linea {line} de las palabras muteadas {tab}"
|
||||||
instanceMute: "Instancias silenciadas"
|
instanceMute: "Instancias silenciadas"
|
||||||
|
@ -873,6 +881,8 @@ makeReactionsPublicDescription: "Todas las reacciones que hayas hecho serán pú
|
||||||
classic: "Clásico"
|
classic: "Clásico"
|
||||||
muteThread: "Silenciar hilo"
|
muteThread: "Silenciar hilo"
|
||||||
unmuteThread: "Mostrar hilo"
|
unmuteThread: "Mostrar hilo"
|
||||||
|
followingVisibility: "Visibilidad de seguidos"
|
||||||
|
followersVisibility: "Visibilidad de seguidores"
|
||||||
continueThread: "Ver la continuación del hilo"
|
continueThread: "Ver la continuación del hilo"
|
||||||
deleteAccountConfirm: "La cuenta será borrada. ¿Está seguro?"
|
deleteAccountConfirm: "La cuenta será borrada. ¿Está seguro?"
|
||||||
incorrectPassword: "La contraseña es incorrecta"
|
incorrectPassword: "La contraseña es incorrecta"
|
||||||
|
@ -1024,6 +1034,7 @@ sensitiveWords: "Palabras sensibles"
|
||||||
sensitiveWordsDescription: "La visibilidad de todas las notas que contienen cualquiera de las palabras configuradas serán puestas en \"Inicio\" automáticamente. Puedes enumerás varias separándolas con saltos de línea"
|
sensitiveWordsDescription: "La visibilidad de todas las notas que contienen cualquiera de las palabras configuradas serán puestas en \"Inicio\" automáticamente. Puedes enumerás varias separándolas con saltos de línea"
|
||||||
sensitiveWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
|
sensitiveWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
|
||||||
hiddenTags: "Hashtags ocultos"
|
hiddenTags: "Hashtags ocultos"
|
||||||
|
hiddenTagsDescription: "Selecciona las etiquetas que no se mostrarán en tendencias. Una etiqueta por línea."
|
||||||
notesSearchNotAvailable: "No se puede buscar una nota"
|
notesSearchNotAvailable: "No se puede buscar una nota"
|
||||||
license: "Licencia"
|
license: "Licencia"
|
||||||
unfavoriteConfirm: "¿Desea quitar de favoritos?"
|
unfavoriteConfirm: "¿Desea quitar de favoritos?"
|
||||||
|
@ -1152,6 +1163,7 @@ tosAndPrivacyPolicy: "Condiciones de Uso y Política de Privacidad"
|
||||||
avatarDecorations: "Decoraciones de avatar"
|
avatarDecorations: "Decoraciones de avatar"
|
||||||
attach: "Acoplar"
|
attach: "Acoplar"
|
||||||
detach: "Quitar"
|
detach: "Quitar"
|
||||||
|
detachAll: "Quitar todo"
|
||||||
angle: "Ángulo"
|
angle: "Ángulo"
|
||||||
flip: "Echar de un capirotazo"
|
flip: "Echar de un capirotazo"
|
||||||
showAvatarDecorations: "Mostrar decoraciones de avatar"
|
showAvatarDecorations: "Mostrar decoraciones de avatar"
|
||||||
|
@ -1165,6 +1177,10 @@ cwNotationRequired: "Si se ha activado \"ocultar contenido\", es necesario propo
|
||||||
doReaction: "Añadir reacción"
|
doReaction: "Añadir reacción"
|
||||||
code: "Código"
|
code: "Código"
|
||||||
reloadRequiredToApplySettings: "Es necesario recargar para que se aplique la configuración."
|
reloadRequiredToApplySettings: "Es necesario recargar para que se aplique la configuración."
|
||||||
|
remainingN: "Faltan: {n}"
|
||||||
|
overwriteContentConfirm: "¿Quieres sustituir todo el contenido actual?"
|
||||||
|
seasonalScreenEffect: "Efectos de pantalla asociados a estaciones"
|
||||||
|
decorate: "Decorar"
|
||||||
_announcement:
|
_announcement:
|
||||||
forExistingUsers: "Solo para usuarios registrados"
|
forExistingUsers: "Solo para usuarios registrados"
|
||||||
forExistingUsersDescription: "Este anuncio solo se mostrará a aquellos usuarios registrados en el momento de su publicación. Si se deshabilita esta opción, aquellos usuarios que se registren tras su publicación también lo verán."
|
forExistingUsersDescription: "Este anuncio solo se mostrará a aquellos usuarios registrados en el momento de su publicación. Si se deshabilita esta opción, aquellos usuarios que se registren tras su publicación también lo verán."
|
||||||
|
@ -1222,6 +1238,45 @@ _initialTutorial:
|
||||||
home: "Puedes ver los posts de las cuentas que sigues."
|
home: "Puedes ver los posts de las cuentas que sigues."
|
||||||
local: "Puedes ver los posts de todos los usuarios de este servidor."
|
local: "Puedes ver los posts de todos los usuarios de este servidor."
|
||||||
social: "Se ven los posts de la línea de tiempo de inicio junto con los de la línea de tiempo local."
|
social: "Se ven los posts de la línea de tiempo de inicio junto con los de la línea de tiempo local."
|
||||||
|
global: "Puedes ver notas de todos los servidores conectados."
|
||||||
|
description2: "Puedes cambiar la línea de tiempo en la parte superior de la pantalla cuando quieras."
|
||||||
|
description3: "Además, hay listas de líneas de tiempo y listas de canales. Para más detalle, por favor visita este enlace: {link}"
|
||||||
|
_postNote:
|
||||||
|
title: "Ajustes de publicación de nota"
|
||||||
|
description1: "Cuando publicas una nota en Misskey, hay varias opciones disponibles. El formulario tiene este aspecto."
|
||||||
|
_visibility:
|
||||||
|
description: "Puedes limitar quién puede ver tu nota."
|
||||||
|
public: "Tu nota será visible para todos los usuarios."
|
||||||
|
home: "Publicar solo en la línea de tiempo de Inicio. La nota se verá en tu perfil, la verán tus seguidores y también cuando sea renotada."
|
||||||
|
followers: "Visible solo para seguidores. Sólo tus seguidores podrán ver la nota, y no podrá ser renotada por otras personas."
|
||||||
|
direct: "Visible sólo para usuarios específicos, y el destinatario será notificado. Puede usarse como alternativa a la mensajería directa."
|
||||||
|
doNotSendConfidencialOnDirect1: "¡Ten cuidado cuando vayas a enviar información sensible!"
|
||||||
|
doNotSendConfidencialOnDirect2: "Los administradores del servidor pueden leer lo que escribes. Ten cuidado cuando envíes información sensible en notas directas en servidores no confiables."
|
||||||
|
localOnly: "Publicando con esta opción seleccionada, la nota no se federará hacia otros servidores. Los usuarios de otros servidores no podrán ver estas notas directamente, sin importar los ajustes seleccionados más arriba."
|
||||||
|
_cw:
|
||||||
|
title: "Alerta de contenido (CW)"
|
||||||
|
description: "En lugar de mostrarse el contenido de la nota, se mostrará lo que escribas en el campo \"comentarios\". Pulsando en \"leer más\" desplegará el contenido de la nota."
|
||||||
|
_exampleNote:
|
||||||
|
cw: "¡Esto te hará tener hambre!"
|
||||||
|
note: "Acabo de comerme un donut de chocolate glaseado 🍩😋"
|
||||||
|
useCases: "Esto se usa cuando las normas del servidor lo requieren, o para ocultar spoilers o contenido sensible."
|
||||||
|
_howToMakeAttachmentsSensitive:
|
||||||
|
title: "¿Cómo puedo marcar adjuntos como contenido sensible?"
|
||||||
|
description: "Cuando las normas del servidor lo requieran, o el contenido lo requiera, marca la opción de \"contenido sensible\" para el adjunto."
|
||||||
|
tryThisFile: "¡Prueba a marcar la imagen adjunta como contenido sensible!"
|
||||||
|
_exampleNote:
|
||||||
|
note: "Ups, la he liado al abrir la tapa del natto..."
|
||||||
|
method: "Para marcar un adjunto como sensible, haz clic en la miniatura, abre el menú, y haz clic en \"Marcar como sensible\"."
|
||||||
|
sensitiveSucceeded: "Cuando adjuntes archivos, por favor, ten en cuenta las normas del servidor para marcarlos como contenido sensible."
|
||||||
|
doItToContinue: "Marca el archivo adjunto como sensible para continuar."
|
||||||
|
_done:
|
||||||
|
title: "¡Has completado el tutorial! 🎉"
|
||||||
|
description: "Las funciones que mostramos aquí son sólo una pequeña parte. Para más detalles sobre el funcionamiento de Misskey, pulsa en este enlace: {link}"
|
||||||
|
_timelineDescription:
|
||||||
|
home: "En la línea de tiempo de Inicio puedes ver las notas de las cuentas a las que sigues."
|
||||||
|
local: "En la línea de tiempo Local puedes ver las notas de todos los usuarios del servidor."
|
||||||
|
social: "En la línea de tiempo Social verás las notas de Inicio y Local a la vez."
|
||||||
|
global: "En la línea de tiempo Global verás las notas de todos los servidores conectados."
|
||||||
_serverRules:
|
_serverRules:
|
||||||
description: "Un conjunto de reglas que serán mostradas antes del registro. Configurar un sumario de términos de servicio es recomendado."
|
description: "Un conjunto de reglas que serán mostradas antes del registro. Configurar un sumario de términos de servicio es recomendado."
|
||||||
_serverSettings:
|
_serverSettings:
|
||||||
|
@ -1233,6 +1288,7 @@ _serverSettings:
|
||||||
manifestJsonOverride: "Sobreescribir manifest.json"
|
manifestJsonOverride: "Sobreescribir manifest.json"
|
||||||
shortName: "Nombre corto"
|
shortName: "Nombre corto"
|
||||||
shortNameDescription: "Forma corta del nombre de la instancia que puede mostrarse si el nombre completo es demasiado largo."
|
shortNameDescription: "Forma corta del nombre de la instancia que puede mostrarse si el nombre completo es demasiado largo."
|
||||||
|
fanoutTimelineDescription: "Incrementa el rendimiento de forma significativa cuando se obtienen las líneas de tiempo y reduce la carga en la base de datos. A cambio, el uso de la memoria en Redis incrementará. Considera desactivar esta opción en caso de que tu servidor tenga poca memoria o detectes inestabilidad."
|
||||||
_accountMigration:
|
_accountMigration:
|
||||||
moveFrom: "Trasladar de otra cuenta a ésta"
|
moveFrom: "Trasladar de otra cuenta a ésta"
|
||||||
moveFromSub: "Crear un alias para otra cuenta."
|
moveFromSub: "Crear un alias para otra cuenta."
|
||||||
|
@ -1490,6 +1546,9 @@ _achievements:
|
||||||
_smashTestNotificationButton:
|
_smashTestNotificationButton:
|
||||||
title: "Sobrecarga de pruebas"
|
title: "Sobrecarga de pruebas"
|
||||||
description: "Envía muchas notificaciones de prueba en un corto espacio de tiempo"
|
description: "Envía muchas notificaciones de prueba en un corto espacio de tiempo"
|
||||||
|
_tutorialCompleted:
|
||||||
|
title: "Diploma del Curso Básico de Misskey"
|
||||||
|
description: "Tutorial completado"
|
||||||
_role:
|
_role:
|
||||||
new: "Crear rol"
|
new: "Crear rol"
|
||||||
edit: "Editar rol"
|
edit: "Editar rol"
|
||||||
|
@ -1500,7 +1559,9 @@ _role:
|
||||||
assignTarget: "Asignar objetivo"
|
assignTarget: "Asignar objetivo"
|
||||||
descriptionOfAssignTarget: "<b>Manual</b> Para cambiar manualmente lo que se incluye en este rol.\n<b>Condicional</b> configura una condición, y los usuarios que cumplan la condición serán incluídos automáticamente."
|
descriptionOfAssignTarget: "<b>Manual</b> Para cambiar manualmente lo que se incluye en este rol.\n<b>Condicional</b> configura una condición, y los usuarios que cumplan la condición serán incluídos automáticamente."
|
||||||
manual: "manual"
|
manual: "manual"
|
||||||
|
manualRoles: "Roles manuales"
|
||||||
conditional: "condicional"
|
conditional: "condicional"
|
||||||
|
conditionalRoles: "Roles condicionales"
|
||||||
condition: "condición"
|
condition: "condición"
|
||||||
isConditionalRole: "Esto es un rol condicional"
|
isConditionalRole: "Esto es un rol condicional"
|
||||||
isPublic: "Publicar rol"
|
isPublic: "Publicar rol"
|
||||||
|
@ -1549,6 +1610,7 @@ _role:
|
||||||
canHideAds: "Puede ocultar anuncios"
|
canHideAds: "Puede ocultar anuncios"
|
||||||
canSearchNotes: "Uso de la búsqueda de notas"
|
canSearchNotes: "Uso de la búsqueda de notas"
|
||||||
canUseTranslator: "Uso de traductor"
|
canUseTranslator: "Uso de traductor"
|
||||||
|
avatarDecorationLimit: "Número máximo de decoraciones de avatar"
|
||||||
_condition:
|
_condition:
|
||||||
isLocal: "Usuario local"
|
isLocal: "Usuario local"
|
||||||
isRemote: "Usuario remoto"
|
isRemote: "Usuario remoto"
|
||||||
|
@ -1577,6 +1639,7 @@ _emailUnavailable:
|
||||||
disposable: "No es un correo reutilizable"
|
disposable: "No es un correo reutilizable"
|
||||||
mx: "Servidor de correo inválido"
|
mx: "Servidor de correo inválido"
|
||||||
smtp: "Servidor de correo no disponible"
|
smtp: "Servidor de correo no disponible"
|
||||||
|
banned: "Email no disponible"
|
||||||
_ffVisibility:
|
_ffVisibility:
|
||||||
public: "Publicar"
|
public: "Publicar"
|
||||||
followers: "Visible solo para seguidores"
|
followers: "Visible solo para seguidores"
|
||||||
|
@ -1653,6 +1716,7 @@ _aboutMisskey:
|
||||||
donate: "Donar a Misskey"
|
donate: "Donar a Misskey"
|
||||||
morePatrons: "Muchas más personas nos apoyan. Muchas gracias🥰"
|
morePatrons: "Muchas más personas nos apoyan. Muchas gracias🥰"
|
||||||
patrons: "Patrocinadores"
|
patrons: "Patrocinadores"
|
||||||
|
projectMembers: "Miembros del proyecto"
|
||||||
_displayOfSensitiveMedia:
|
_displayOfSensitiveMedia:
|
||||||
respect: "Esconder medios marcados como sensibles"
|
respect: "Esconder medios marcados como sensibles"
|
||||||
ignore: "Mostrar medios marcados como sensibles"
|
ignore: "Mostrar medios marcados como sensibles"
|
||||||
|
@ -1677,6 +1741,7 @@ _channel:
|
||||||
notesCount: "{n} notas"
|
notesCount: "{n} notas"
|
||||||
nameAndDescription: "Nombre y descripción"
|
nameAndDescription: "Nombre y descripción"
|
||||||
nameOnly: "Sólo nombre"
|
nameOnly: "Sólo nombre"
|
||||||
|
allowRenoteToExternal: "Permitir renotas y menciones fuera del canal"
|
||||||
_menuDisplay:
|
_menuDisplay:
|
||||||
sideFull: "Horizontal"
|
sideFull: "Horizontal"
|
||||||
sideIcon: "Horizontal (ícono)"
|
sideIcon: "Horizontal (ícono)"
|
||||||
|
@ -1780,6 +1845,12 @@ _ago:
|
||||||
yearsAgo: "Hace {n} años"
|
yearsAgo: "Hace {n} años"
|
||||||
invalid: "No hay nada que ver aqui"
|
invalid: "No hay nada que ver aqui"
|
||||||
_timeIn:
|
_timeIn:
|
||||||
|
seconds: "En {n} segundos"
|
||||||
|
minutes: "En {n}m"
|
||||||
|
hours: "En {n}h"
|
||||||
|
days: "En {n}d"
|
||||||
|
weeks: "En {n}sem."
|
||||||
|
months: "En {n}M"
|
||||||
years: "En {n} años"
|
years: "En {n} años"
|
||||||
_time:
|
_time:
|
||||||
second: "Segundos"
|
second: "Segundos"
|
||||||
|
@ -1906,6 +1977,7 @@ _widgets:
|
||||||
_userList:
|
_userList:
|
||||||
chooseList: "Seleccione una lista"
|
chooseList: "Seleccione una lista"
|
||||||
clicker: "Cliqueador"
|
clicker: "Cliqueador"
|
||||||
|
birthdayFollowings: "Hoy cumplen años"
|
||||||
_cw:
|
_cw:
|
||||||
hide: "Ocultar"
|
hide: "Ocultar"
|
||||||
show: "Ver más"
|
show: "Ver más"
|
||||||
|
@ -1968,6 +2040,7 @@ _profile:
|
||||||
changeAvatar: "Cambiar avatar"
|
changeAvatar: "Cambiar avatar"
|
||||||
changeBanner: "Cambiar banner"
|
changeBanner: "Cambiar banner"
|
||||||
verifiedLinkDescription: "Introduciendo una URL que contiene un enlace a tu perfil, se puede mostrar un icono de verificación de propiedad al lado del campo."
|
verifiedLinkDescription: "Introduciendo una URL que contiene un enlace a tu perfil, se puede mostrar un icono de verificación de propiedad al lado del campo."
|
||||||
|
avatarDecorationMax: "Puedes añadir un máximo de {max} decoraciones de avatar."
|
||||||
_exportOrImport:
|
_exportOrImport:
|
||||||
allNotes: "Todas las notas"
|
allNotes: "Todas las notas"
|
||||||
favoritedNotes: "Notas favoritas"
|
favoritedNotes: "Notas favoritas"
|
||||||
|
@ -2089,6 +2162,7 @@ _notification:
|
||||||
pollEnded: "Estan disponibles los resultados de la encuesta"
|
pollEnded: "Estan disponibles los resultados de la encuesta"
|
||||||
newNote: "Nueva nota"
|
newNote: "Nueva nota"
|
||||||
unreadAntennaNote: "Antena {name}"
|
unreadAntennaNote: "Antena {name}"
|
||||||
|
roleAssigned: "Rol asignado"
|
||||||
emptyPushNotificationMessage: "Se han actualizado las notificaciones push"
|
emptyPushNotificationMessage: "Se han actualizado las notificaciones push"
|
||||||
achievementEarned: "Logro desbloqueado"
|
achievementEarned: "Logro desbloqueado"
|
||||||
testNotification: "Notificación de prueba"
|
testNotification: "Notificación de prueba"
|
||||||
|
@ -2110,6 +2184,7 @@ _notification:
|
||||||
pollEnded: "La encuesta terminó"
|
pollEnded: "La encuesta terminó"
|
||||||
receiveFollowRequest: "Recibió una solicitud de seguimiento"
|
receiveFollowRequest: "Recibió una solicitud de seguimiento"
|
||||||
followRequestAccepted: "El seguimiento fue aceptado"
|
followRequestAccepted: "El seguimiento fue aceptado"
|
||||||
|
roleAssigned: "Rol asignado"
|
||||||
achievementEarned: "Logro desbloqueado"
|
achievementEarned: "Logro desbloqueado"
|
||||||
app: "Notificaciones desde aplicaciones"
|
app: "Notificaciones desde aplicaciones"
|
||||||
_actions:
|
_actions:
|
||||||
|
@ -2255,3 +2330,6 @@ _externalResourceInstaller:
|
||||||
_themeInstallFailed:
|
_themeInstallFailed:
|
||||||
title: "Instalación de tema fallida"
|
title: "Instalación de tema fallida"
|
||||||
description: "Ha ocurrido un problema al instalar el tema. Por favor, inténtalo de nuevo. Se pueden ver más detalles del error en la consola de Javascript."
|
description: "Ha ocurrido un problema al instalar el tema. Por favor, inténtalo de nuevo. Se pueden ver más detalles del error en la consola de Javascript."
|
||||||
|
_dataSaver:
|
||||||
|
_media:
|
||||||
|
title: "Cargando Multimedia"
|
||||||
|
|
|
@ -114,7 +114,7 @@ quote: "인용"
|
||||||
inChannelRenote: "채널 내 리노트"
|
inChannelRenote: "채널 내 리노트"
|
||||||
inChannelQuote: "채널 내 인용"
|
inChannelQuote: "채널 내 인용"
|
||||||
pinnedNote: "고정된 노트"
|
pinnedNote: "고정된 노트"
|
||||||
pinned: "프로필에 고정"
|
pinned: "고정하기"
|
||||||
you: "나"
|
you: "나"
|
||||||
clickToShow: "클릭하여 보기"
|
clickToShow: "클릭하여 보기"
|
||||||
sensitive: "열람 주의"
|
sensitive: "열람 주의"
|
||||||
|
@ -1179,7 +1179,7 @@ code: "문자열"
|
||||||
reloadRequiredToApplySettings: "설정을 적용하려면 새로고침을 해야 합니다."
|
reloadRequiredToApplySettings: "설정을 적용하려면 새로고침을 해야 합니다."
|
||||||
remainingN: "나머지: {n}"
|
remainingN: "나머지: {n}"
|
||||||
overwriteContentConfirm: "현재 내용을 덮어쓰기 합니다. 계속 진행하시겠습니까?"
|
overwriteContentConfirm: "현재 내용을 덮어쓰기 합니다. 계속 진행하시겠습니까?"
|
||||||
seasonalScreenEffect: "철에 맞는 화면으로 꾸미기"
|
seasonalScreenEffect: "계절에 따른 효과 보이기"
|
||||||
decorate: "장식하기"
|
decorate: "장식하기"
|
||||||
_announcement:
|
_announcement:
|
||||||
forExistingUsers: "기존 유저에게만 알림"
|
forExistingUsers: "기존 유저에게만 알림"
|
||||||
|
@ -1641,6 +1641,7 @@ _emailUnavailable:
|
||||||
disposable: "임시 이메일 주소는 사용할 수 없습니다"
|
disposable: "임시 이메일 주소는 사용할 수 없습니다"
|
||||||
mx: "메일 서버가 올바르지 않습니다"
|
mx: "메일 서버가 올바르지 않습니다"
|
||||||
smtp: "메일 서버가 응답하지 않습니다"
|
smtp: "메일 서버가 응답하지 않습니다"
|
||||||
|
banned: "이 메일 주소는 사용할 수 없습니다"
|
||||||
_ffVisibility:
|
_ffVisibility:
|
||||||
public: "공개"
|
public: "공개"
|
||||||
followers: "팔로워에게만 공개"
|
followers: "팔로워에게만 공개"
|
||||||
|
|
|
@ -632,11 +632,11 @@ tokenRequested: "允許存取帳戶"
|
||||||
pluginTokenRequestedDescription: "此外掛將擁有在此設定的權限。"
|
pluginTokenRequestedDescription: "此外掛將擁有在此設定的權限。"
|
||||||
notificationType: "通知形式"
|
notificationType: "通知形式"
|
||||||
edit: "編輯"
|
edit: "編輯"
|
||||||
emailServer: "電郵伺服器"
|
emailServer: "電子郵件伺服器"
|
||||||
enableEmail: "啟用發送電郵功能"
|
enableEmail: "啟用發送電子郵件功能"
|
||||||
emailConfigInfo: "用於確認電郵地址及密碼重置"
|
emailConfigInfo: "用於確認電子郵件地址及密碼重置"
|
||||||
email: "電子郵件"
|
email: "電子郵件"
|
||||||
emailAddress: "電郵地址"
|
emailAddress: "電子郵件位址"
|
||||||
smtpConfig: "SMTP 伺服器設定"
|
smtpConfig: "SMTP 伺服器設定"
|
||||||
smtpHost: "主機"
|
smtpHost: "主機"
|
||||||
smtpPort: "埠"
|
smtpPort: "埠"
|
||||||
|
@ -731,7 +731,7 @@ disableShowingAnimatedImages: "不播放動態圖檔"
|
||||||
highlightSensitiveMedia: "強調敏感標記"
|
highlightSensitiveMedia: "強調敏感標記"
|
||||||
verificationEmailSent: "已發送驗證電子郵件。請點擊進入電子郵件中的鏈接完成驗證。"
|
verificationEmailSent: "已發送驗證電子郵件。請點擊進入電子郵件中的鏈接完成驗證。"
|
||||||
notSet: "未設定"
|
notSet: "未設定"
|
||||||
emailVerified: "已成功驗證您的電郵"
|
emailVerified: "已成功驗證您的電子郵件地址"
|
||||||
noteFavoritesCount: "我的最愛貼文的數目"
|
noteFavoritesCount: "我的最愛貼文的數目"
|
||||||
pageLikesCount: "頁面被按讚次數"
|
pageLikesCount: "頁面被按讚次數"
|
||||||
pageLikedCount: "頁面被按讚次數"
|
pageLikedCount: "頁面被按讚次數"
|
||||||
|
@ -783,7 +783,7 @@ capacity: "容量"
|
||||||
inUse: "已使用"
|
inUse: "已使用"
|
||||||
editCode: "編輯代碼"
|
editCode: "編輯代碼"
|
||||||
apply: "套用"
|
apply: "套用"
|
||||||
receiveAnnouncementFromInstance: "接收由本實例發出的電郵通知"
|
receiveAnnouncementFromInstance: "接收來自伺服器的通知"
|
||||||
emailNotification: "郵件通知"
|
emailNotification: "郵件通知"
|
||||||
publish: "發布"
|
publish: "發布"
|
||||||
inChannelSearch: "頻道内搜尋"
|
inChannelSearch: "頻道内搜尋"
|
||||||
|
@ -955,7 +955,7 @@ cannotUploadBecauseExceedsFileSizeLimit: "由於超過了檔案大小的限制
|
||||||
beta: "測試版"
|
beta: "測試版"
|
||||||
enableAutoSensitive: "自動 NSFW 判定"
|
enableAutoSensitive: "自動 NSFW 判定"
|
||||||
enableAutoSensitiveDescription: "如果可用,它將使用機器學習技術判斷檔案是否需要標記為敏感。即使關閉此功能,也可能會依實例規則而自動啟用。"
|
enableAutoSensitiveDescription: "如果可用,它將使用機器學習技術判斷檔案是否需要標記為敏感。即使關閉此功能,也可能會依實例規則而自動啟用。"
|
||||||
activeEmailValidationDescription: "積極驗證使用者的電郵地址,以判斷它是否可以通訊。關閉此選項代表只會檢查地址是否符合格式。"
|
activeEmailValidationDescription: "主動地驗證使用者的電子郵件地址,以確定是否是一次性地址以及是否可以真正與其進行通訊。關閉時,僅檢查格式是否正確。"
|
||||||
navbar: "導覽列"
|
navbar: "導覽列"
|
||||||
shuffle: "隨機"
|
shuffle: "隨機"
|
||||||
account: "帳戶"
|
account: "帳戶"
|
||||||
|
@ -1641,6 +1641,7 @@ _emailUnavailable:
|
||||||
disposable: "不是永久可用的地址"
|
disposable: "不是永久可用的地址"
|
||||||
mx: "郵件伺服器不正確"
|
mx: "郵件伺服器不正確"
|
||||||
smtp: "郵件伺服器沒有應答"
|
smtp: "郵件伺服器沒有應答"
|
||||||
|
banned: "無法使用此電子郵件地址註冊"
|
||||||
_ffVisibility:
|
_ffVisibility:
|
||||||
public: "公開"
|
public: "公開"
|
||||||
followers: "只有關注您的使用者能看到"
|
followers: "只有關注您的使用者能看到"
|
||||||
|
|
|
@ -24,9 +24,11 @@ export class ffVisibility1702718871541 {
|
||||||
async down(queryRunner) {
|
async down(queryRunner) {
|
||||||
await queryRunner.query(`CREATE TYPE "public"."user_profile_ffvisibility_enum" AS ENUM('public', 'followers', 'private')`);
|
await queryRunner.query(`CREATE TYPE "public"."user_profile_ffvisibility_enum" AS ENUM('public', 'followers', 'private')`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD "ffVisibility" "public"."user_profile_ffvisibility_enum" NOT NULL DEFAULT 'public'`);
|
await queryRunner.query(`ALTER TABLE "user_profile" ADD "ffVisibility" "public"."user_profile_ffvisibility_enum" NOT NULL DEFAULT 'public'`);
|
||||||
|
|
||||||
await queryRunner.query(`CREATE CAST ("public"."user_profile_followingvisibility_enum" AS "public"."user_profile_ffvisibility_enum") WITH INOUT AS ASSIGNMENT`);
|
await queryRunner.query(`CREATE CAST ("public"."user_profile_followingvisibility_enum" AS "public"."user_profile_ffvisibility_enum") WITH INOUT AS ASSIGNMENT`);
|
||||||
await queryRunner.query(`UPDATE "user_profile" SET ffVisibility = "user_profile"."followingVisibility"`);
|
await queryRunner.query(`UPDATE "user_profile" SET "ffVisibility" = "followingVisibility"`);
|
||||||
await queryRunner.query(`DROP CAST ("public"."user_profile_followingvisibility_enum" AS "public"."user_profile_ffvisibility_enum")`);
|
await queryRunner.query(`DROP CAST ("public"."user_profile_followingvisibility_enum" AS "public"."user_profile_ffvisibility_enum")`);
|
||||||
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "followersVisibility"`);
|
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "followersVisibility"`);
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "followingVisibility"`);
|
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "followingVisibility"`);
|
||||||
await queryRunner.query(`DROP TYPE "public"."user_profile_followersVisibility_enum"`);
|
await queryRunner.query(`DROP TYPE "public"."user_profile_followersVisibility_enum"`);
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { URLSearchParams } from 'node:url';
|
||||||
import * as nodemailer from 'nodemailer';
|
import * as nodemailer from 'nodemailer';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { validate as validateEmail } from 'deep-email-validator';
|
import { validate as validateEmail } from 'deep-email-validator';
|
||||||
import { SubOutputFormat } from 'deep-email-validator/dist/output/output.js';
|
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
@ -166,7 +165,10 @@ export class EmailService {
|
||||||
email: emailAddress,
|
email: emailAddress,
|
||||||
});
|
});
|
||||||
|
|
||||||
let validated;
|
let validated: {
|
||||||
|
valid: boolean,
|
||||||
|
reason?: string | null,
|
||||||
|
};
|
||||||
|
|
||||||
if (meta.enableActiveEmailValidation) {
|
if (meta.enableActiveEmailValidation) {
|
||||||
if (meta.enableVerifymailApi && meta.verifymailAuthKey != null) {
|
if (meta.enableVerifymailApi && meta.verifymailAuthKey != null) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { FeaturedService } from '@/core/FeaturedService.js';
|
import { FeaturedService } from '@/core/FeaturedService.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HashtagService {
|
export class HashtagService {
|
||||||
|
@ -29,6 +30,7 @@ export class HashtagService {
|
||||||
private featuredService: FeaturedService,
|
private featuredService: FeaturedService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private metaService: MetaService,
|
private metaService: MetaService,
|
||||||
|
private utilityService: UtilityService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +163,7 @@ export class HashtagService {
|
||||||
const instance = await this.metaService.fetch();
|
const instance = await this.metaService.fetch();
|
||||||
const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t));
|
const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t));
|
||||||
if (hiddenTags.includes(hashtag)) return;
|
if (hiddenTags.includes(hashtag)) return;
|
||||||
|
if (this.utilityService.isSensitiveWordIncluded(hashtag, instance.sensitiveWords)) return;
|
||||||
|
|
||||||
// YYYYMMDDHHmm (10分間隔)
|
// YYYYMMDDHHmm (10分間隔)
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
|
@ -222,7 +222,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
|
|
||||||
if (data.visibility === 'public' && data.channel == null) {
|
if (data.visibility === 'public' && data.channel == null) {
|
||||||
const sensitiveWords = meta.sensitiveWords;
|
const sensitiveWords = meta.sensitiveWords;
|
||||||
if (this.isSensitive(data, sensitiveWords)) {
|
if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
|
||||||
data.visibility = 'home';
|
data.visibility = 'home';
|
||||||
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
|
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
|
||||||
data.visibility = 'home';
|
data.visibility = 'home';
|
||||||
|
@ -672,31 +672,6 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
this.index(note);
|
this.index(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
|
||||||
private isSensitive(note: Option, sensitiveWord: string[]): boolean {
|
|
||||||
if (sensitiveWord.length > 0) {
|
|
||||||
const text = note.cw ?? note.text ?? '';
|
|
||||||
if (text === '') return false;
|
|
||||||
const matched = sensitiveWord.some(filter => {
|
|
||||||
// represents RegExp
|
|
||||||
const regexp = filter.match(/^\/(.+)\/(.*)$/);
|
|
||||||
// This should never happen due to input sanitisation.
|
|
||||||
if (!regexp) {
|
|
||||||
const words = filter.split(' ');
|
|
||||||
return words.every(keyword => text.includes(keyword));
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new RE2(regexp[1], regexp[2]).test(text);
|
|
||||||
} catch (err) {
|
|
||||||
// This should never happen due to input sanitisation.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (matched) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private isQuote(note: Option): note is Option & { renote: MiNote } {
|
private isQuote(note: Option): note is Option & { renote: MiNote } {
|
||||||
// sync with misc/is-quote.ts
|
// sync with misc/is-quote.ts
|
||||||
|
@ -880,6 +855,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
// ダイレクトのとき、そのリストが対象外のユーザーの場合
|
// ダイレクトのとき、そのリストが対象外のユーザーの場合
|
||||||
if (
|
if (
|
||||||
note.visibility === 'specified' &&
|
note.visibility === 'specified' &&
|
||||||
|
note.userId !== userListMembership.userListUserId &&
|
||||||
!note.visibleUserIds.some(v => v === userListMembership.userListUserId)
|
!note.visibleUserIds.some(v => v === userListMembership.userListUserId)
|
||||||
) continue;
|
) continue;
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,7 @@ export class ReactionService {
|
||||||
reaction = reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`;
|
reaction = reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`;
|
||||||
|
|
||||||
// センシティブ
|
// センシティブ
|
||||||
if ((note.reactionAcceptance === 'nonSensitiveOnly') && emoji.isSensitive) {
|
if ((note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && emoji.isSensitive) {
|
||||||
reaction = FALLBACK;
|
reaction = FALLBACK;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import { toASCII } from 'punycode';
|
import { toASCII } from 'punycode';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import RE2 from 're2';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@ -41,6 +42,33 @@ export class UtilityService {
|
||||||
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
|
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public isSensitiveWordIncluded(text: string, sensitiveWords: string[]): boolean {
|
||||||
|
if (sensitiveWords.length === 0) return false;
|
||||||
|
if (text === '') return false;
|
||||||
|
|
||||||
|
const regexpregexp = /^\/(.+)\/(.*)$/;
|
||||||
|
|
||||||
|
const matched = sensitiveWords.some(filter => {
|
||||||
|
// represents RegExp
|
||||||
|
const regexp = filter.match(regexpregexp);
|
||||||
|
// This should never happen due to input sanitisation.
|
||||||
|
if (!regexp) {
|
||||||
|
const words = filter.split(' ');
|
||||||
|
return words.every(keyword => text.includes(keyword));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// TODO: RE2インスタンスをキャッシュ
|
||||||
|
return new RE2(regexp[1], regexp[2]).test(text);
|
||||||
|
} catch (err) {
|
||||||
|
// This should never happen due to input sanitisation.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public extractDbHost(uri: string): string {
|
public extractDbHost(uri: string): string {
|
||||||
const url = new URL(uri);
|
const url = new URL(uri);
|
||||||
|
|
|
@ -381,6 +381,10 @@ export const meta = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
|
shortName: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
objectStorageS3ForcePathStyle: {
|
objectStorageS3ForcePathStyle: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
|
|
@ -29,37 +29,10 @@ export const meta = {
|
||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
items: {
|
items: {
|
||||||
properties: {
|
type: 'object',
|
||||||
id: { type: 'string' },
|
optional: false,
|
||||||
firstRetrievedAt: { type: 'string' },
|
nullable: false,
|
||||||
host: { type: 'string' },
|
ref: 'FederationInstance',
|
||||||
usersCount: { type: 'number' },
|
|
||||||
notesCount: { type: 'number' },
|
|
||||||
followingCount: { type: 'number' },
|
|
||||||
followersCount: { type: 'number' },
|
|
||||||
isNotResponding: { type: 'boolean' },
|
|
||||||
isSuspended: { type: 'boolean' },
|
|
||||||
isBlocked: { type: 'boolean' },
|
|
||||||
softwareName: { type: 'string' },
|
|
||||||
softwareVersion: { type: 'string' },
|
|
||||||
openRegistrations: { type: 'boolean' },
|
|
||||||
name: { type: 'string' },
|
|
||||||
description: { type: 'string' },
|
|
||||||
maintainerName: { type: 'string' },
|
|
||||||
maintainerEmail: { type: 'string' },
|
|
||||||
isSilenced: { type: 'boolean' },
|
|
||||||
iconUrl: { type: 'string' },
|
|
||||||
faviconUrl: { type: 'string' },
|
|
||||||
themeColor: { type: 'string' },
|
|
||||||
infoUpdatedAt: {
|
|
||||||
type: 'string',
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
latestRequestReceivedAt: {
|
|
||||||
type: 'string',
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
otherFollowersCount: { type: 'number' },
|
otherFollowersCount: { type: 'number' },
|
||||||
|
@ -68,42 +41,15 @@ export const meta = {
|
||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
items: {
|
items: {
|
||||||
properties: {
|
type: 'object',
|
||||||
id: { type: 'string' },
|
optional: false,
|
||||||
firstRetrievedAt: { type: 'string' },
|
nullable: false,
|
||||||
host: { type: 'string' },
|
ref: 'FederationInstance',
|
||||||
usersCount: { type: 'number' },
|
|
||||||
notesCount: { type: 'number' },
|
|
||||||
followingCount: { type: 'number' },
|
|
||||||
followersCount: { type: 'number' },
|
|
||||||
isNotResponding: { type: 'boolean' },
|
|
||||||
isSuspended: { type: 'boolean' },
|
|
||||||
isBlocked: { type: 'boolean' },
|
|
||||||
softwareName: { type: 'string' },
|
|
||||||
softwareVersion: { type: 'string' },
|
|
||||||
openRegistrations: { type: 'boolean' },
|
|
||||||
name: { type: 'string' },
|
|
||||||
description: { type: 'string' },
|
|
||||||
maintainerName: { type: 'string' },
|
|
||||||
maintainerEmail: { type: 'string' },
|
|
||||||
isSilenced: { type: 'boolean' },
|
|
||||||
iconUrl: { type: 'string' },
|
|
||||||
faviconUrl: { type: 'string' },
|
|
||||||
themeColor: { type: 'string' },
|
|
||||||
infoUpdatedAt: {
|
|
||||||
type: 'string',
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
latestRequestReceivedAt: {
|
|
||||||
type: 'string',
|
|
||||||
nullable: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
otherFollowingCount: { type: 'number' },
|
otherFollowingCount: { type: 'number' },
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -164,20 +164,34 @@ export const meta = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
properties: {
|
properties: {
|
||||||
place: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
example: 'xxxxxxxxxx',
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
format: 'url',
|
format: 'url',
|
||||||
},
|
},
|
||||||
|
place: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
ratio: {
|
||||||
|
type: 'number',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
imageUrl: {
|
imageUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
format: 'url',
|
format: 'url',
|
||||||
},
|
},
|
||||||
|
dayOfWeek: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,6 +21,10 @@ export const meta = {
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
properties: {
|
||||||
|
sourceLang: { type: 'string' },
|
||||||
|
text: { type: 'string' },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { parse } from 'acorn';
|
import { parse } from 'acorn';
|
||||||
import { generate } from 'astring';
|
import { generate } from 'astring';
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import { normalizeClass, unwindCssModuleClassName } from './rollup-plugin-unwind-css-module-class-name';
|
import { normalizeClass, unwindCssModuleClassName } from './rollup-plugin-unwind-css-module-class-name.js';
|
||||||
import type * as estree from 'estree';
|
import type * as estree from 'estree';
|
||||||
|
|
||||||
function parseExpression(code: string): estree.Expression {
|
function parseExpression(code: string): estree.Expression {
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { abuseUserReport } from '../../.storybook/fakes';
|
import { abuseUserReport } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAbuseReport from './MkAbuseReport.vue';
|
import MkAbuseReport from './MkAbuseReport.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
|
import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { userDetailed } from '../../.storybook/fakes';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import MkAccountMoved from './MkAccountMoved.vue';
|
import MkAccountMoved from './MkAccountMoved.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAchievements from './MkAchievements.vue';
|
import MkAchievements from './MkAchievements.vue';
|
||||||
import { ACHIEVEMENT_TYPES } from '@/scripts/achievements.js';
|
import { ACHIEVEMENT_TYPES } from '@/scripts/achievements.js';
|
||||||
export const Empty = {
|
export const Empty = {
|
||||||
|
|
|
@ -67,7 +67,7 @@ const props = withDefaults(defineProps<{
|
||||||
withDescription: true,
|
withDescription: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const achievements = ref();
|
const achievements = ref<Misskey.entities.UsersAchievementsResponse | null>(null);
|
||||||
const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x)));
|
const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x)));
|
||||||
|
|
||||||
function fetch() {
|
function fetch() {
|
||||||
|
|
|
@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }">{{ c.text }}</span>
|
<span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }">{{ c.text }}</span>
|
||||||
<Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }" :text="c.text"/>
|
<Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }" :text="c.text" @clickEv="c.onClickEv"/>
|
||||||
<MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :disabled="c.disabled" :small="size === 'small'" inline @click="c.onClick">{{ c.text }}</MkButton>
|
<MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :disabled="c.disabled" :small="size === 'small'" inline @click="c.onClick">{{ c.text }}</MkButton>
|
||||||
<div v-else-if="c.type === 'buttons'" class="_buttons" :style="{ justifyContent: align }">
|
<div v-else-if="c.type === 'buttons'" class="_buttons" :style="{ justifyContent: align }">
|
||||||
<MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :disabled="button.disabled" inline :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton>
|
<MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :disabled="button.disabled" inline :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton>
|
||||||
|
|
|
@ -9,8 +9,8 @@ import { expect } from '@storybook/jest';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAutocomplete from './MkAutocomplete.vue';
|
import MkAutocomplete from './MkAutocomplete.vue';
|
||||||
import MkInput from './MkInput.vue';
|
import MkInput from './MkInput.vue';
|
||||||
import { tick } from '@/scripts/test-utils.js';
|
import { tick } from '@/scripts/test-utils.js';
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAvatars from './MkAvatars.vue';
|
import MkAvatars from './MkAvatars.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ export type Captcha = {
|
||||||
getResponse(id: string): string;
|
getResponse(id: string): string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile';
|
export type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile';
|
||||||
|
|
||||||
type CaptchaContainer = {
|
type CaptchaContainer = {
|
||||||
readonly [_ in CaptchaProvider]?: Captcha;
|
readonly [_ in CaptchaProvider]?: Captcha;
|
||||||
|
|
|
@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onBeforeUnmount, shallowRef, ref } from 'vue';
|
import { onMounted, onBeforeUnmount, shallowRef, ref } from 'vue';
|
||||||
import MkMenu from './MkMenu.vue';
|
import MkMenu from './MkMenu.vue';
|
||||||
import { MenuItem } from './types/menu.vue';
|
import { MenuItem } from '@/types/menu.js';
|
||||||
import contains from '@/scripts/contains.js';
|
import contains from '@/scripts/contains.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
|
|
@ -6,11 +6,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, h, PropType, TransitionGroup, useCssModule } from 'vue';
|
import { defineComponent, h, PropType, TransitionGroup, useCssModule } from 'vue';
|
||||||
import MkAd from '@/components/global/MkAd.vue';
|
import MkAd from '@/components/global/MkAd.vue';
|
||||||
import { isDebuggerEnabled, stackTraceInstances } from '@/debug';
|
import { isDebuggerEnabled, stackTraceInstances } from '@/debug.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { MisskeyEntity } from '@/types/date-separated-list';
|
import { MisskeyEntity } from '@/types/date-separated-list.js';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -104,7 +104,7 @@ const props = defineProps<{
|
||||||
isRequest: boolean,
|
isRequest: boolean,
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let dialog = ref(null);
|
let dialog = ref<InstanceType<typeof MkModalWindow> | null>(null);
|
||||||
let name = ref(props.emoji ? props.emoji.name : '');
|
let name = ref(props.emoji ? props.emoji.name : '');
|
||||||
let category = ref(props.emoji ? props.emoji.category : '');
|
let category = ref(props.emoji ? props.emoji.category : '');
|
||||||
let aliases = ref(props.emoji ? props.emoji.aliases.join(' ') : '');
|
let aliases = ref(props.emoji ? props.emoji.aliases.join(' ') : '');
|
||||||
|
@ -112,7 +112,7 @@ let license = ref(props.emoji ? (props.emoji.license ?? '') : '');
|
||||||
let isSensitive = ref(props.emoji ? props.emoji.isSensitive : false);
|
let isSensitive = ref(props.emoji ? props.emoji.isSensitive : false);
|
||||||
let localOnly = ref(props.emoji ? props.emoji.localOnly : false);
|
let localOnly = ref(props.emoji ? props.emoji.localOnly : false);
|
||||||
let roleIdsThatCanBeUsedThisEmojiAsReaction = ref((props.emoji && props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction) ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
|
let roleIdsThatCanBeUsedThisEmojiAsReaction = ref((props.emoji && props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction) ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
|
||||||
let rolesThatCanBeUsedThisEmojiAsReaction = ref([]);
|
let rolesThatCanBeUsedThisEmojiAsReaction = ref<Misskey.entities.Role[]>([]);
|
||||||
let file = ref<Misskey.entities.DriveFile>();
|
let file = ref<Misskey.entities.DriveFile>();
|
||||||
let isRequest = ref(props.isRequest ?? false);
|
let isRequest = ref(props.isRequest ?? false);
|
||||||
watch((roleIdsThatCanBeUsedThisEmojiAsReaction), async () => {
|
watch((roleIdsThatCanBeUsedThisEmojiAsReaction), async () => {
|
||||||
|
|
|
@ -38,14 +38,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
||||||
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
|
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
|
||||||
import bytes from '@/filters/bytes.js';
|
import bytes from '@/filters/bytes.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { dateString } from '@/filters/date.js';
|
import { dateString } from '@/filters/date.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
pagination: any;
|
pagination: Paging;
|
||||||
viewMode: 'grid' | 'list';
|
viewMode: 'grid' | 'list';
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/jest';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { galleryPost } from '../../.storybook/fakes';
|
import { galleryPost } from '../../.storybook/fakes.js';
|
||||||
import MkGalleryPostPreview from './MkGalleryPostPreview.vue';
|
import MkGalleryPostPreview from './MkGalleryPostPreview.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { userDetailed, inviteCode } from '../../.storybook/fakes';
|
import { userDetailed, inviteCode } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkInviteCode from './MkInviteCode.vue';
|
import MkInviteCode from './MkInviteCode.vue';
|
||||||
|
|
||||||
export const Default = {
|
export const Default = {
|
||||||
|
|
|
@ -29,7 +29,7 @@ const self = props.url.startsWith(local);
|
||||||
const attr = self ? 'to' : 'href';
|
const attr = self ? 'to' : 'href';
|
||||||
const target = self ? null : '_blank';
|
const target = self ? null : '_blank';
|
||||||
|
|
||||||
const el = ref();
|
const el = ref<HTMLElement>();
|
||||||
|
|
||||||
useTooltip(el, (showing) => {
|
useTooltip(el, (showing) => {
|
||||||
os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
|
os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
|
||||||
|
|
|
@ -27,7 +27,7 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const contentEl = ref();
|
const contentEl = ref<HTMLElement>();
|
||||||
|
|
||||||
function calc() {
|
function calc() {
|
||||||
const eachLength = contentEl.value.offsetWidth / props.repeat;
|
const eachLength = contentEl.value.offsetWidth / props.repeat;
|
||||||
|
|
|
@ -37,7 +37,7 @@ import XBanner from '@/components/MkMediaBanner.vue';
|
||||||
import XImage from '@/components/MkMediaImage.vue';
|
import XImage from '@/components/MkMediaImage.vue';
|
||||||
import XVideo from '@/components/MkMediaVideo.vue';
|
import XVideo from '@/components/MkMediaVideo.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { FILE_TYPE_BROWSERSAFE } from '@/const';
|
import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
||||||
import { focusPrev, focusNext } from '@/scripts/focus.js';
|
import { focusPrev, focusNext } from '@/scripts/focus.js';
|
||||||
import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
|
import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { isTouchUsing } from '@/scripts/touch.js';
|
import { isTouchUsing } from '@/scripts/touch.js';
|
||||||
|
|
|
@ -257,7 +257,7 @@ const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
|
const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
|
||||||
const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords));
|
const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords));
|
||||||
const translation = ref<any>(null);
|
const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
|
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
|
||||||
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i.id));
|
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i.id));
|
||||||
|
|
|
@ -218,7 +218,7 @@ import { claimAchievement } from '@/scripts/achievements.js';
|
||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
||||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
||||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ const isMyRenote = $i && ($i.id === note.value.userId);
|
||||||
const showContent = ref(false);
|
const showContent = ref(false);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false);
|
const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false);
|
||||||
const translation = ref(null);
|
const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null;
|
const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null;
|
||||||
const urls = parsed ? extractUrlFromMfm(parsed) : null;
|
const urls = parsed ? extractUrlFromMfm(parsed) : null;
|
||||||
|
@ -325,7 +325,7 @@ provide('react', (reaction: string) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const tab = ref('replies');
|
const tab = ref('replies');
|
||||||
const reactionTabType = ref(null);
|
const reactionTabType = ref<string | null>(null);
|
||||||
|
|
||||||
const renotesPagination = computed(() => ({
|
const renotesPagination = computed(() => ({
|
||||||
endpoint: 'notes/renotes',
|
endpoint: 'notes/renotes',
|
||||||
|
@ -333,7 +333,7 @@ const renotesPagination = computed(() => ({
|
||||||
params: {
|
params: {
|
||||||
noteId: appearNote.value.id,
|
noteId: appearNote.value.id,
|
||||||
},
|
},
|
||||||
}));
|
} satisfies Paging));
|
||||||
|
|
||||||
const reactionsPagination = computed(() => ({
|
const reactionsPagination = computed(() => ({
|
||||||
endpoint: 'notes/reactions',
|
endpoint: 'notes/reactions',
|
||||||
|
@ -342,7 +342,7 @@ const reactionsPagination = computed(() => ({
|
||||||
noteId: appearNote.value.id,
|
noteId: appearNote.value.id,
|
||||||
type: reactionTabType.value,
|
type: reactionTabType.value,
|
||||||
},
|
},
|
||||||
}));
|
} satisfies Paging));
|
||||||
|
|
||||||
useNoteCapture({
|
useNoteCapture({
|
||||||
rootEl: el,
|
rootEl: el,
|
||||||
|
|
|
@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<i v-else-if="notification.type === 'quote'" class="ti ti-quote"></i>
|
<i v-else-if="notification.type === 'quote'" class="ti ti-quote"></i>
|
||||||
<i v-else-if="notification.type === 'pollEnded'" class="ti ti-chart-arrows"></i>
|
<i v-else-if="notification.type === 'pollEnded'" class="ti ti-chart-arrows"></i>
|
||||||
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
|
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
|
||||||
<img v-else-if="notification.type === 'roleAssigned'" :src="notification.role.iconUrl" alt=""/>
|
<img v-else-if="notification.type === 'roleAssigned'" style="height: 1.3em; vertical-align: -22%;" :src="notification.role.iconUrl" alt=""/>
|
||||||
<!-- notification.reaction が null になることはまずないが、ここでoptional chaining使うと一部ブラウザで刺さるので念の為 -->
|
<!-- notification.reaction が null になることはまずないが、ここでoptional chaining使うと一部ブラウザで刺さるので念の為 -->
|
||||||
<MkReactionIcon
|
<MkReactionIcon
|
||||||
v-else-if="notification.type === 'reaction'"
|
v-else-if="notification.type === 'reaction'"
|
||||||
|
|
|
@ -37,7 +37,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||||
import { url } from '@/config.js';
|
import { url } from '@/config.js';
|
||||||
import { mainRouter, routes, page } from '@/router.js';
|
import { mainRouter, routes, page } from '@/router.js';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/account.js';
|
||||||
import { Router, useScrollPositionManager } from '@/nirax';
|
import { Router, useScrollPositionManager } from '@/nirax.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
|
import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
|
||||||
import { openingWindowsCount } from '@/os.js';
|
import { openingWindowsCount } from '@/os.js';
|
||||||
|
|
|
@ -49,7 +49,7 @@ import * as os from '@/os.js';
|
||||||
import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js';
|
import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js';
|
||||||
import { useDocumentVisibility } from '@/scripts/use-document-visibility.js';
|
import { useDocumentVisibility } from '@/scripts/use-document-visibility.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { MisskeyEntity } from '@/types/date-separated-list';
|
import { MisskeyEntity } from '@/types/date-separated-list.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const SECOND_FETCH_LIMIT = 30;
|
const SECOND_FETCH_LIMIT = 30;
|
||||||
|
|
|
@ -52,7 +52,7 @@ const emit = defineEmits<{
|
||||||
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||||
const passwordInput = shallowRef<InstanceType<typeof MkInput>>();
|
const passwordInput = shallowRef<InstanceType<typeof MkInput>>();
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
const token = ref(null);
|
const token = ref<string | null>(null);
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
emit('cancelled');
|
emit('cancelled');
|
||||||
|
|
|
@ -203,14 +203,14 @@ watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
|
||||||
const cw = ref<string | null>(props.initialCw ?? null);
|
const cw = ref<string | null>(props.initialCw ?? null);
|
||||||
const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
|
const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
|
||||||
const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
|
const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
|
||||||
const visibleUsers = ref([]);
|
const visibleUsers = ref<Misskey.entities.UserDetailed[]>([]);
|
||||||
if (props.initialVisibleUsers) {
|
if (props.initialVisibleUsers) {
|
||||||
props.initialVisibleUsers.forEach(pushVisibleUser);
|
props.initialVisibleUsers.forEach(pushVisibleUser);
|
||||||
}
|
}
|
||||||
const reactionAcceptance = ref(defaultStore.state.reactionAcceptance);
|
const reactionAcceptance = ref(defaultStore.state.reactionAcceptance);
|
||||||
const autocomplete = ref(null);
|
const autocomplete = ref(null);
|
||||||
const draghover = ref(false);
|
const draghover = ref(false);
|
||||||
const quoteId = ref(null);
|
const quoteId = ref<string | null>(null);
|
||||||
const hasNotSpecifiedMentions = ref(false);
|
const hasNotSpecifiedMentions = ref(false);
|
||||||
const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
|
const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
|
||||||
const imeText = ref('');
|
const imeText = ref('');
|
||||||
|
|
|
@ -28,10 +28,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
role: any;
|
role: Misskey.entities.Role;
|
||||||
forModeration: boolean;
|
forModeration: boolean;
|
||||||
detailed: boolean;
|
detailed: boolean;
|
||||||
}>(), {
|
}>(), {
|
||||||
|
|
|
@ -65,10 +65,10 @@ const opening = ref(false);
|
||||||
const changed = ref(false);
|
const changed = ref(false);
|
||||||
const invalid = ref(false);
|
const invalid = ref(false);
|
||||||
const filled = computed(() => v.value !== '' && v.value != null);
|
const filled = computed(() => v.value !== '' && v.value != null);
|
||||||
const inputEl = ref(null);
|
const inputEl = ref<HTMLObjectElement | null>(null);
|
||||||
const prefixEl = ref(null);
|
const prefixEl = ref<HTMLElement | null>(null);
|
||||||
const suffixEl = ref(null);
|
const suffixEl = ref<HTMLElement | null>(null);
|
||||||
const container = ref(null);
|
const container = ref<HTMLElement | null>(null);
|
||||||
const height =
|
const height =
|
||||||
props.small ? 33 :
|
props.small ? 33 :
|
||||||
props.large ? 39 :
|
props.large ? 39 :
|
||||||
|
|
|
@ -71,8 +71,6 @@ const host = ref(toUnicode(configHost));
|
||||||
const totpLogin = ref(false);
|
const totpLogin = ref(false);
|
||||||
const queryingKey = ref(false);
|
const queryingKey = ref(false);
|
||||||
const credentialRequest = ref<CredentialRequestOptions | null>(null);
|
const credentialRequest = ref<CredentialRequestOptions | null>(null);
|
||||||
const hCaptchaResponse = ref(null);
|
|
||||||
const reCaptchaResponse = ref(null);
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'login', v: any): void;
|
(ev: 'login', v: any): void;
|
||||||
|
@ -126,8 +124,6 @@ async function queryKey(): Promise<void> {
|
||||||
username: username.value,
|
username: username.value,
|
||||||
password: password.value,
|
password: password.value,
|
||||||
credential: credential.toJSON(),
|
credential: credential.toJSON(),
|
||||||
'hcaptcha-response': hCaptchaResponse.value,
|
|
||||||
'g-recaptcha-response': reCaptchaResponse.value,
|
|
||||||
});
|
});
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
emit('login', res);
|
emit('login', res);
|
||||||
|
@ -149,8 +145,6 @@ function onSubmit(): void {
|
||||||
os.api('signin', {
|
os.api('signin', {
|
||||||
username: username.value,
|
username: username.value,
|
||||||
password: password.value,
|
password: password.value,
|
||||||
'hcaptcha-response': hCaptchaResponse.value,
|
|
||||||
'g-recaptcha-response': reCaptchaResponse.value,
|
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
totpLogin.value = true;
|
totpLogin.value = true;
|
||||||
signing.value = false;
|
signing.value = false;
|
||||||
|
@ -168,8 +162,6 @@ function onSubmit(): void {
|
||||||
os.api('signin', {
|
os.api('signin', {
|
||||||
username: username.value,
|
username: username.value,
|
||||||
password: password.value,
|
password: password.value,
|
||||||
'hcaptcha-response': hCaptchaResponse.value,
|
|
||||||
'g-recaptcha-response': reCaptchaResponse.value,
|
|
||||||
token: user.value?.twoFactorEnabled ? token.value : undefined,
|
token: user.value?.twoFactorEnabled ? token.value : undefined,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
emit('login', res);
|
emit('login', res);
|
||||||
|
|
|
@ -154,9 +154,9 @@ const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:
|
||||||
const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>('');
|
const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>('');
|
||||||
const passwordRetypeState = ref<null | 'match' | 'not-match'>(null);
|
const passwordRetypeState = ref<null | 'match' | 'not-match'>(null);
|
||||||
const submitting = ref<boolean>(false);
|
const submitting = ref<boolean>(false);
|
||||||
const hCaptchaResponse = ref(null);
|
const hCaptchaResponse = ref<string | null>(null);
|
||||||
const reCaptchaResponse = ref(null);
|
const reCaptchaResponse = ref<string | null>(null);
|
||||||
const turnstileResponse = ref(null);
|
const turnstileResponse = ref<string | null>(null);
|
||||||
const usernameAbortController = ref<null | AbortController>(null);
|
const usernameAbortController = ref<null | AbortController>(null);
|
||||||
const emailAbortController = ref<null | AbortController>(null);
|
const emailAbortController = ref<null | AbortController>(null);
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref, shallowRef } from 'vue';
|
import { onMounted, onUnmounted, ref, shallowRef } from 'vue';
|
||||||
|
|
||||||
const particles = ref([]);
|
const particles = ref<{
|
||||||
|
id: string,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
size: number,
|
||||||
|
dur: number,
|
||||||
|
color: string
|
||||||
|
}[]>([]);
|
||||||
const el = shallowRef<HTMLElement>();
|
const el = shallowRef<HTMLElement>();
|
||||||
const width = ref(0);
|
const width = ref(0);
|
||||||
const height = ref(0);
|
const height = ref(0);
|
||||||
|
|
|
@ -66,7 +66,7 @@ const props = defineProps<{
|
||||||
announcement?: any,
|
announcement?: any,
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = ref(null);
|
const dialog = ref<InstanceType<typeof MkModalWindow> | null>(null);
|
||||||
const title = ref<string>(props.announcement ? props.announcement.title : '');
|
const title = ref<string>(props.announcement ? props.announcement.title : '');
|
||||||
const text = ref<string>(props.announcement ? props.announcement.text : '');
|
const text = ref<string>(props.announcement ? props.announcement.text : '');
|
||||||
const icon = ref<string>(props.announcement ? props.announcement.icon : 'info');
|
const icon = ref<string>(props.announcement ? props.announcement.icon : 'info');
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { commonHandlers } from '../../.storybook/mocks';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import { userDetailed } from '../../.storybook/fakes';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue';
|
import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -37,15 +37,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import XUser from '@/components/MkUserSetupDialog.User.vue';
|
import XUser from '@/components/MkUserSetupDialog.User.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
||||||
|
|
||||||
const pinnedUsers = { endpoint: 'pinned-users', noPaging: true };
|
const pinnedUsers = { endpoint: 'pinned-users', noPaging: true } satisfies Paging;
|
||||||
|
|
||||||
const popularUsers = { endpoint: 'users', limit: 10, noPaging: true, params: {
|
const popularUsers = { endpoint: 'users', limit: 10, noPaging: true, params: {
|
||||||
state: 'alive',
|
state: 'alive',
|
||||||
origin: 'local',
|
origin: 'local',
|
||||||
sort: '+follower',
|
sort: '+follower',
|
||||||
} };
|
} } satisfies Paging;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { userDetailed } from '../../.storybook/fakes';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import MkUserSetupDialog_User from './MkUserSetupDialog.User.vue';
|
import MkUserSetupDialog_User from './MkUserSetupDialog.User.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { commonHandlers } from '../../.storybook/mocks';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import { userDetailed } from '../../.storybook/fakes';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import MkUserSetupDialog from './MkUserSetupDialog.vue';
|
import MkUserSetupDialog from './MkUserSetupDialog.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -54,7 +54,7 @@ import { defineAsyncComponent, ref } from 'vue';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { widgets as widgetDefs } from '@/widgets';
|
import { widgets as widgetDefs } from '@/widgets/index.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ const widgetRefs = {};
|
||||||
const configWidget = (id: string) => {
|
const configWidget = (id: string) => {
|
||||||
widgetRefs[id].configure();
|
widgetRefs[id].configure();
|
||||||
};
|
};
|
||||||
const widgetAdderSelected = ref(null);
|
const widgetAdderSelected = ref<string | null>(null);
|
||||||
const addWidget = () => {
|
const addWidget = () => {
|
||||||
if (widgetAdderSelected.value == null) return;
|
if (widgetAdderSelected.value == null) return;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ const props = defineProps<{
|
||||||
const pending = ref(true);
|
const pending = ref(true);
|
||||||
const resolved = ref(false);
|
const resolved = ref(false);
|
||||||
const rejected = ref(false);
|
const rejected = ref(false);
|
||||||
const result = ref(null);
|
const result = ref<any>(null);
|
||||||
|
|
||||||
const process = () => {
|
const process = () => {
|
||||||
if (props.p == null) {
|
if (props.p == null) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { userDetailed } from '../../../.storybook/fakes';
|
import { userDetailed } from '../../../.storybook/fakes.js';
|
||||||
import MkAcct from './MkAcct.vue';
|
import MkAcct from './MkAcct.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { userDetailed } from '../../../.storybook/fakes';
|
import { userDetailed } from '../../../.storybook/fakes.js';
|
||||||
import MkAvatar from './MkAvatar.vue';
|
import MkAvatar from './MkAvatar.vue';
|
||||||
const common = {
|
const common = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { within } from '@storybook/testing-library';
|
import { within } from '@storybook/testing-library';
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/jest';
|
||||||
import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.ts';
|
import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { VNode, h } from 'vue';
|
import { VNode, h, SetupContext } from 'vue';
|
||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkUrl from '@/components/global/MkUrl.vue';
|
import MkUrl from '@/components/global/MkUrl.vue';
|
||||||
|
@ -77,8 +77,12 @@ type MfmProps = {
|
||||||
enableEmojiMenuReaction?: boolean;
|
enableEmojiMenuReaction?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type MfmEvents = {
|
||||||
|
clickEv(id: string): void;
|
||||||
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default function(props: MfmProps) {
|
export default function(props: MfmProps, context: SetupContext<MfmEvents>) {
|
||||||
const isNote = props.isNote ?? true;
|
const isNote = props.isNote ?? true;
|
||||||
const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat : false : false;
|
const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat : false : false;
|
||||||
const shouldUhoize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isGorilla : false : false;
|
const shouldUhoize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isGorilla : false : false;
|
||||||
|
@ -354,6 +358,13 @@ export default function(props: MfmProps) {
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
case 'clickable': {
|
||||||
|
return h('span', { onClick(ev: MouseEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
context.emit('clickEv', token.props.args.ev ?? '');
|
||||||
|
} }, genEl(token.children, scale));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (style === undefined) {
|
if (style === undefined) {
|
||||||
return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children, scale), ']']);
|
return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children, scale), ']']);
|
||||||
|
|
|
@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, provide, inject, Ref, ref, watch, shallowRef } from 'vue';
|
import { onMounted, onUnmounted, provide, inject, Ref, ref, watch, shallowRef } from 'vue';
|
||||||
|
|
||||||
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const';
|
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const.js';
|
||||||
|
|
||||||
const rootEl = shallowRef<HTMLElement>();
|
const rootEl = shallowRef<HTMLElement>();
|
||||||
const headerEl = shallowRef<HTMLElement>();
|
const headerEl = shallowRef<HTMLElement>();
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { expect } from '@storybook/jest';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { commonHandlers } from '../../../.storybook/mocks';
|
import { commonHandlers } from '../../../.storybook/mocks.js';
|
||||||
import MkUrl from './MkUrl.vue';
|
import MkUrl from './MkUrl.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/jest';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { userDetailed } from '../../../.storybook/fakes';
|
import { userDetailed } from '../../../.storybook/fakes.js';
|
||||||
import MkUserName from './MkUserName.vue';
|
import MkUserName from './MkUserName.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, onBeforeUnmount, provide, shallowRef, ref } from 'vue';
|
import { inject, onBeforeUnmount, provide, shallowRef, ref } from 'vue';
|
||||||
import { Resolved, Router } from '@/nirax';
|
import { Resolved, Router } from '@/nirax.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -14,7 +14,7 @@ import XText from './page.text.vue';
|
||||||
import XSection from './page.section.vue';
|
import XSection from './page.section.vue';
|
||||||
import XImage from './page.image.vue';
|
import XImage from './page.image.vue';
|
||||||
import XNote from './page.note.vue';
|
import XNote from './page.note.vue';
|
||||||
import { Block } from './block.type';
|
import { Block } from './block.type.js';
|
||||||
|
|
||||||
function getComponent(type: string) {
|
function getComponent(type: string) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { ImageBlock } from './block.type';
|
import { ImageBlock } from './block.type.js';
|
||||||
import MediaImage from '@/components/MkMediaImage.vue';
|
import MediaImage from '@/components/MkMediaImage.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -11,9 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, Ref, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { NoteBlock } from './block.type';
|
import { NoteBlock } from './block.type.js';
|
||||||
import MkNote from '@/components/MkNote.vue';
|
import MkNote from '@/components/MkNote.vue';
|
||||||
import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
|
import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
@ -23,7 +23,7 @@ const props = defineProps<{
|
||||||
page: Misskey.entities.Page,
|
page: Misskey.entities.Page,
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const note: Ref<Misskey.entities.Note | null> = ref(null);
|
const note = ref<Misskey.entities.Note | null>(null);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
os.api('notes/show', { noteId: props.block.note })
|
os.api('notes/show', { noteId: props.block.note })
|
||||||
|
|
|
@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { SectionBlock } from './block.type';
|
import { SectionBlock } from './block.type.js';
|
||||||
|
|
||||||
const XBlock = defineAsyncComponent(() => import('./page.block.vue'));
|
const XBlock = defineAsyncComponent(() => import('./page.block.vue'));
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { TextBlock } from './block.type';
|
import { TextBlock } from './block.type.js';
|
||||||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
|
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
|
||||||
|
|
||||||
const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue'));
|
const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue'));
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Directive } from 'vue';
|
import { Directive } from 'vue';
|
||||||
import { makeHotkey } from '../scripts/hotkey';
|
import { makeHotkey } from '../scripts/hotkey.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mounted(el, binding) {
|
mounted(el, binding) {
|
||||||
|
|
|
@ -5,17 +5,17 @@
|
||||||
|
|
||||||
import { App } from 'vue';
|
import { App } from 'vue';
|
||||||
|
|
||||||
import userPreview from './user-preview';
|
import userPreview from './user-preview.js';
|
||||||
import getSize from './get-size';
|
import getSize from './get-size.js';
|
||||||
import ripple from './ripple';
|
import ripple from './ripple.js';
|
||||||
import tooltip from './tooltip';
|
import tooltip from './tooltip.js';
|
||||||
import hotkey from './hotkey';
|
import hotkey from './hotkey.js';
|
||||||
import appear from './appear';
|
import appear from './appear.js';
|
||||||
import anim from './anim';
|
import anim from './anim.js';
|
||||||
import clickAnime from './click-anime';
|
import clickAnime from './click-anime.js';
|
||||||
import panel from './panel';
|
import panel from './panel.js';
|
||||||
import adaptiveBorder from './adaptive-border';
|
import adaptiveBorder from './adaptive-border.js';
|
||||||
import adaptiveBg from './adaptive-bg';
|
import adaptiveBg from './adaptive-bg.js';
|
||||||
|
|
||||||
export default function(app: App) {
|
export default function(app: App) {
|
||||||
for (const [key, value] of Object.entries(directives)) {
|
for (const [key, value] of Object.entries(directives)) {
|
||||||
|
|
|
@ -313,8 +313,13 @@ const patrons = [
|
||||||
const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));
|
const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));
|
||||||
|
|
||||||
let easterEggReady = false;
|
let easterEggReady = false;
|
||||||
const easterEggEmojis = ref([]);
|
const easterEggEmojis = ref<{
|
||||||
const easterEggEngine = ref(null);
|
id: string,
|
||||||
|
top: number,
|
||||||
|
left: number,
|
||||||
|
emoji: string
|
||||||
|
}[]>([]);
|
||||||
|
const easterEggEngine = ref<{ stop: () => void } | null>(null);
|
||||||
const containerEl = shallowRef<HTMLElement>();
|
const containerEl = shallowRef<HTMLElement>();
|
||||||
|
|
||||||
function iconLoaded() {
|
function iconLoaded() {
|
||||||
|
|
|
@ -83,7 +83,7 @@ const pagination = {
|
||||||
state.value === 'notResponding' ? { notResponding: true } :
|
state.value === 'notResponding' ? { notResponding: true } :
|
||||||
{}),
|
{}),
|
||||||
})),
|
})),
|
||||||
} as Paging;
|
} satisfies Paging;
|
||||||
|
|
||||||
function getStatus(instance) {
|
function getStatus(instance) {
|
||||||
if (instance.isSuspended) return 'Suspended';
|
if (instance.isSuspended) return 'Suspended';
|
||||||
|
|
|
@ -101,6 +101,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {computed, ref, watch } from 'vue';
|
import {computed, ref, watch } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import XEmojis from './about.emojis.vue';
|
import XEmojis from './about.emojis.vue';
|
||||||
import XFederation from './about.federation.vue';
|
import XFederation from './about.federation.vue';
|
||||||
import { version, host } from '@/config.js';
|
import { version, host } from '@/config.js';
|
||||||
|
@ -125,7 +126,7 @@ const props = withDefaults(defineProps<{
|
||||||
initialTab: 'overview',
|
initialTab: 'overview',
|
||||||
});
|
});
|
||||||
|
|
||||||
const stats = ref(null);
|
const stats = ref<Misskey.entities.StatsResponse | null>(null);
|
||||||
const tab = ref(props.initialTab);
|
const tab = ref(props.initialTab);
|
||||||
|
|
||||||
watch(tab, () => {
|
watch(tab, () => {
|
||||||
|
|
|
@ -68,6 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkObjectView from '@/components/MkObjectView.vue';
|
import MkObjectView from '@/components/MkObjectView.vue';
|
||||||
|
@ -83,8 +84,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import { iAmAdmin, iAmModerator } from '@/account.js';
|
import { iAmAdmin, iAmModerator } from '@/account.js';
|
||||||
|
|
||||||
const tab = ref('overview');
|
const tab = ref('overview');
|
||||||
const file = ref<any>(null);
|
const file = ref<Misskey.entities.DriveFile | null>(null);
|
||||||
const info = ref<any>(null);
|
const info = ref<Misskey.entities.AdminDriveShowFileResponse | null>(null);
|
||||||
const isSensitive = ref<boolean>(false);
|
const isSensitive = ref<boolean>(false);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -225,7 +225,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { iAmAdmin, $i } from '@/account.js';
|
import { iAmAdmin, $i } from '@/account.js';
|
||||||
import MkRolePreview from '@/components/MkRolePreview.vue';
|
import MkRolePreview from '@/components/MkRolePreview.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
userId: string;
|
userId: string;
|
||||||
|
@ -238,9 +238,9 @@ const tab = ref(props.initialTab);
|
||||||
const chartSrc = ref('per-user-notes');
|
const chartSrc = ref('per-user-notes');
|
||||||
const user = ref<null | Misskey.entities.UserDetailed>();
|
const user = ref<null | Misskey.entities.UserDetailed>();
|
||||||
const init = ref<ReturnType<typeof createFetcher>>();
|
const init = ref<ReturnType<typeof createFetcher>>();
|
||||||
const info = ref();
|
const info = ref<any>();
|
||||||
const ips = ref(null);
|
const ips = ref<Misskey.entities.AdminGetUserIpsResponse | null>(null);
|
||||||
const ap = ref(null);
|
const ap = ref<any>(null);
|
||||||
const moderator = ref(false);
|
const moderator = ref(false);
|
||||||
const silenced = ref(false);
|
const silenced = ref(false);
|
||||||
const suspended = ref(false);
|
const suspended = ref(false);
|
||||||
|
@ -258,7 +258,7 @@ const announcementsPagination = {
|
||||||
params: computed(() => ({
|
params: computed(() => ({
|
||||||
userId: props.userId,
|
userId: props.userId,
|
||||||
})),
|
})),
|
||||||
};
|
} satisfies Paging;
|
||||||
const expandedRoles = ref([]);
|
const expandedRoles = ref([]);
|
||||||
|
|
||||||
function createFetcher() {
|
function createFetcher() {
|
||||||
|
|
|
@ -38,7 +38,7 @@ import tinycolor from 'tinycolor2';
|
||||||
import { popupMenu } from '@/os.js';
|
import { popupMenu } from '@/os.js';
|
||||||
import { scrollToTop } from '@/scripts/scroll.js';
|
import { scrollToTop } from '@/scripts/scroll.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { globalEvents } from '@/events';
|
import { globalEvents } from '@/events.js';
|
||||||
import { injectPageMetadata } from '@/scripts/page-metadata.js';
|
import { injectPageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
type Tab = {
|
type Tab = {
|
||||||
|
@ -70,7 +70,7 @@ const metadata = injectPageMetadata();
|
||||||
const el = shallowRef<HTMLElement>(null);
|
const el = shallowRef<HTMLElement>(null);
|
||||||
const tabRefs = {};
|
const tabRefs = {};
|
||||||
const tabHighlightEl = shallowRef<HTMLElement | null>(null);
|
const tabHighlightEl = shallowRef<HTMLElement | null>(null);
|
||||||
const bg = ref(null);
|
const bg = ref<string | null>(null);
|
||||||
const height = ref(0);
|
const height = ref(0);
|
||||||
const hasTabs = computed(() => {
|
const hasTabs = computed(() => {
|
||||||
return props.tabs && props.tabs.length > 0;
|
return props.tabs && props.tabs.length > 0;
|
||||||
|
|
|
@ -56,7 +56,7 @@ import { computed, shallowRef, ref } from 'vue';
|
||||||
|
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
||||||
import XAbuseReport from '@/components/MkAbuseReport.vue';
|
import XAbuseReport from '@/components/MkAbuseReport.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
@ -77,7 +77,7 @@ const pagination = {
|
||||||
reporterOrigin: reporterOrigin.value,
|
reporterOrigin: reporterOrigin.value,
|
||||||
targetUserOrigin: targetUserOrigin.value,
|
targetUserOrigin: targetUserOrigin.value,
|
||||||
})),
|
})),
|
||||||
};
|
} satisfies Paging;
|
||||||
|
|
||||||
function resolved(reportId) {
|
function resolved(reportId) {
|
||||||
reports.value.removeItem(reportId);
|
reports.value.removeItem(reportId);
|
||||||
|
|
|
@ -86,6 +86,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
|
@ -98,7 +99,7 @@ import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
const ads = ref<any[]>([]);
|
const ads = ref<Misskey.entities.Ad[]>([]);
|
||||||
|
|
||||||
// ISO形式はTZがUTCになってしまうので、TZ分ずらして時間を初期化
|
// ISO形式はTZがUTCになってしまうので、TZ分ずらして時間を初期化
|
||||||
const localTime = new Date();
|
const localTime = new Date();
|
||||||
|
|
|
@ -65,6 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, ref } from 'vue';
|
import { defineAsyncComponent, ref } from 'vue';
|
||||||
|
import type { CaptchaProvider } from '@/components/MkCaptcha.vue';
|
||||||
import MkRadios from '@/components/MkRadios.vue';
|
import MkRadios from '@/components/MkRadios.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
@ -76,7 +77,7 @@ import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
|
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
|
||||||
|
|
||||||
const provider = ref(null);
|
const provider = ref<CaptchaProvider | null>(null);
|
||||||
const hcaptchaSiteKey = ref<string | null>(null);
|
const hcaptchaSiteKey = ref<string | null>(null);
|
||||||
const hcaptchaSecretKey = ref<string | null>(null);
|
const hcaptchaSecretKey = ref<string | null>(null);
|
||||||
const recaptchaSiteKey = ref<string | null>(null);
|
const recaptchaSiteKey = ref<string | null>(null);
|
||||||
|
|
|
@ -113,9 +113,9 @@ const app192IconUrl = ref<string | null>(null);
|
||||||
const app512IconUrl = ref<string | null>(null);
|
const app512IconUrl = ref<string | null>(null);
|
||||||
const bannerUrl = ref<string | null>(null);
|
const bannerUrl = ref<string | null>(null);
|
||||||
const backgroundImageUrl = ref<string | null>(null);
|
const backgroundImageUrl = ref<string | null>(null);
|
||||||
const themeColor = ref<any>(null);
|
const themeColor = ref<string | null>(null);
|
||||||
const defaultLightTheme = ref<any>(null);
|
const defaultLightTheme = ref<string | null>(null);
|
||||||
const defaultDarkTheme = ref<any>(null);
|
const defaultDarkTheme = ref<string | null>(null);
|
||||||
const serverErrorImageUrl = ref<string | null>(null);
|
const serverErrorImageUrl = ref<string | null>(null);
|
||||||
const infoImageUrl = ref<string | null>(null);
|
const infoImageUrl = ref<string | null>(null);
|
||||||
const notFoundImageUrl = ref<string | null>(null);
|
const notFoundImageUrl = ref<string | null>(null);
|
||||||
|
|
|
@ -79,7 +79,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
|
||||||
const enableEmail = ref<boolean>(false);
|
const enableEmail = ref<boolean>(false);
|
||||||
const email = ref<any>(null);
|
const email = ref<string | null>(null);
|
||||||
const smtpSecure = ref<boolean>(false);
|
const smtpSecure = ref<boolean>(false);
|
||||||
const smtpHost = ref<string>('');
|
const smtpHost = ref<string>('');
|
||||||
const smtpPort = ref<number>(0);
|
const smtpPort = ref<number>(0);
|
||||||
|
|
|
@ -62,7 +62,7 @@ import { computed, ref } from 'vue';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
||||||
import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
|
import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
|
||||||
import FormSplit from '@/components/form/split.vue';
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
@ -88,7 +88,7 @@ const pagination = {
|
||||||
state.value === 'notResponding' ? { notResponding: true } :
|
state.value === 'notResponding' ? { notResponding: true } :
|
||||||
{}),
|
{}),
|
||||||
})),
|
})),
|
||||||
};
|
} satisfies Paging;
|
||||||
|
|
||||||
function getStatus(instance) {
|
function getStatus(instance) {
|
||||||
if (instance.isSuspended) return 'Suspended';
|
if (instance.isSuspended) return 'Suspended';
|
||||||
|
|
|
@ -46,7 +46,7 @@ import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
const origin = ref('local');
|
const origin = ref('local');
|
||||||
const type = ref(null);
|
const type = ref<string | null>(null);
|
||||||
const searchHost = ref('');
|
const searchHost = ref('');
|
||||||
const userId = ref('');
|
const userId = ref('');
|
||||||
const viewMode = ref('grid');
|
const viewMode = ref('grid');
|
||||||
|
|
|
@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {computed, onActivated, onMounted, onUnmounted, provide, ref, watch } from 'vue';
|
import { watch,ComputedRef, Ref, onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkSuperMenu from '@/components/MkSuperMenu.vue';
|
import MkSuperMenu from '@/components/MkSuperMenu.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
|
@ -36,7 +36,7 @@ import { instance } from '@/instance.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { lookupUser, lookupUserByEmail } from '@/scripts/lookup-user.js';
|
import { lookupUser, lookupUserByEmail } from '@/scripts/lookup-user.js';
|
||||||
import { useRouter } from '@/router.js';
|
import { useRouter } from '@/router.js';
|
||||||
import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
|
import { PageMetadata, definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
|
||||||
import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from "@/store.js";
|
import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from "@/store.js";
|
||||||
|
|
||||||
const isEmpty = (x: string | null) => x == null || x === '';
|
const isEmpty = (x: string | null) => x == null || x === '';
|
||||||
|
@ -52,10 +52,10 @@ const indexInfo = {
|
||||||
provide('shouldOmitHeaderTitle', false);
|
provide('shouldOmitHeaderTitle', false);
|
||||||
|
|
||||||
const INFO = ref(indexInfo);
|
const INFO = ref(indexInfo);
|
||||||
const childInfo = ref(null);
|
const childInfo: Ref<ComputedRef<PageMetadata> | null> = ref(null);
|
||||||
const narrow = ref(false);
|
const narrow = ref(false);
|
||||||
const view = ref(null);
|
const view = ref(null);
|
||||||
const el = ref(null);
|
const el = ref<HTMLDivElement | null>(null);
|
||||||
const pageProps = ref({});
|
const pageProps = ref({});
|
||||||
let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
|
let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
|
||||||
let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile;
|
let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile;
|
||||||
|
|
|
@ -73,7 +73,7 @@ const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
const type = ref('all');
|
const type = ref('all');
|
||||||
const sort = ref('+createdAt');
|
const sort = ref('+createdAt');
|
||||||
|
|
||||||
const pagination: Paging = {
|
const pagination = {
|
||||||
endpoint: 'admin/invite/list' as const,
|
endpoint: 'admin/invite/list' as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
params: computed(() => ({
|
params: computed(() => ({
|
||||||
|
@ -81,7 +81,7 @@ const pagination: Paging = {
|
||||||
sort: sort.value,
|
sort: sort.value,
|
||||||
})),
|
})),
|
||||||
offsetMode: true,
|
offsetMode: true,
|
||||||
};
|
} satisfies Paging;
|
||||||
|
|
||||||
const expiresAt = ref('');
|
const expiresAt = ref('');
|
||||||
const noExpirationDate = ref(true);
|
const noExpirationDate = ref(true);
|
||||||
|
@ -97,10 +97,10 @@ async function createWithOptions() {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
title: i18n.ts.inviteCodeCreated,
|
title: i18n.ts.inviteCodeCreated,
|
||||||
text: tickets?.map(x => x.code).join('\n'),
|
text: tickets.map(x => x.code).join('\n'),
|
||||||
});
|
});
|
||||||
|
|
||||||
tickets?.forEach(ticket => pagingComponent.value?.prepend(ticket));
|
tickets.forEach(ticket => pagingComponent.value?.prepend(ticket));
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleted(id: string) {
|
function deleted(id: string) {
|
||||||
|
|
|
@ -145,7 +145,7 @@ const props = defineProps<{
|
||||||
}
|
}
|
||||||
|
|
||||||
.logYellow {
|
.logYellow {
|
||||||
color: var(--warning);
|
color: var(--warn);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logRed {
|
.logRed {
|
||||||
|
|
|
@ -36,13 +36,13 @@ import XHeader from './_header_.vue';
|
||||||
import XModLog from './modlog.ModLog.vue';
|
import XModLog from './modlog.ModLog.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
const logs = shallowRef<InstanceType<typeof MkPagination>>();
|
const logs = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
const type = ref(null);
|
const type = ref<string | null>(null);
|
||||||
const moderatorId = ref('');
|
const moderatorId = ref('');
|
||||||
|
|
||||||
const pagination = {
|
const pagination = {
|
||||||
|
@ -52,7 +52,7 @@ const pagination = {
|
||||||
type: type.value,
|
type: type.value,
|
||||||
userId: moderatorId.value === '' ? null : moderatorId.value,
|
userId: moderatorId.value === '' ? null : moderatorId.value,
|
||||||
})),
|
})),
|
||||||
};
|
} satisfies Paging;
|
||||||
|
|
||||||
console.log(Misskey);
|
console.log(Misskey);
|
||||||
|
|
||||||
|
|
|
@ -47,15 +47,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import XPie from './overview.pie.vue';
|
import XPie, { type InstanceForPie } from './overview.pie.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import number from '@/filters/number.js';
|
import number from '@/filters/number.js';
|
||||||
import MkNumberDiff from '@/components/MkNumberDiff.vue';
|
import MkNumberDiff from '@/components/MkNumberDiff.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
|
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
|
||||||
|
|
||||||
const topSubInstancesForPie = ref<any>(null);
|
const topSubInstancesForPie = ref<InstanceForPie[] | null>(null);
|
||||||
const topPubInstancesForPie = ref<any>(null);
|
const topPubInstancesForPie = ref<InstanceForPie[] | null>(null);
|
||||||
const federationPubActive = ref<number | null>(null);
|
const federationPubActive = ref<number | null>(null);
|
||||||
const federationPubActiveDiff = ref<number | null>(null);
|
const federationPubActiveDiff = ref<number | null>(null);
|
||||||
const federationSubActive = ref<number | null>(null);
|
const federationSubActive = ref<number | null>(null);
|
||||||
|
@ -72,22 +72,28 @@ onMounted(async () => {
|
||||||
federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
|
federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
|
||||||
|
|
||||||
os.apiGet('federation/stats', { limit: 10 }).then(res => {
|
os.apiGet('federation/stats', { limit: 10 }).then(res => {
|
||||||
topSubInstancesForPie.value = res.topSubInstances.map(x => ({
|
topSubInstancesForPie.value = [
|
||||||
name: x.host,
|
...res.topSubInstances.map(x => ({
|
||||||
color: x.themeColor,
|
name: x.host,
|
||||||
value: x.followersCount,
|
color: x.themeColor,
|
||||||
onClick: () => {
|
value: x.followersCount,
|
||||||
os.pageWindow(`/instance-info/${x.host}`);
|
onClick: () => {
|
||||||
},
|
os.pageWindow(`/instance-info/${x.host}`);
|
||||||
})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]);
|
},
|
||||||
topPubInstancesForPie.value = res.topPubInstances.map(x => ({
|
})),
|
||||||
name: x.host,
|
{ name: '(other)', color: '#80808080', value: res.otherFollowersCount },
|
||||||
color: x.themeColor,
|
];
|
||||||
value: x.followingCount,
|
topPubInstancesForPie.value = [
|
||||||
onClick: () => {
|
...res.topPubInstances.map(x => ({
|
||||||
os.pageWindow(`/instance-info/${x.host}`);
|
name: x.host,
|
||||||
},
|
color: x.themeColor,
|
||||||
})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowingCount }]);
|
value: x.followingCount,
|
||||||
|
onClick: () => {
|
||||||
|
os.pageWindow(`/instance-info/${x.host}`);
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
{ name: '(other)', color: '#80808080', value: res.otherFollowingCount },
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
|
|
|
@ -18,12 +18,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { useInterval } from '@/scripts/use-interval.js';
|
import { useInterval } from '@/scripts/use-interval.js';
|
||||||
import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
|
import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
const instances = ref([]);
|
const instances = ref<Misskey.entities.FederationInstance[]>([]);
|
||||||
const fetching = ref(true);
|
const fetching = ref(true);
|
||||||
|
|
||||||
const fetch = async () => {
|
const fetch = async () => {
|
||||||
|
|
|
@ -18,10 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
const moderators = ref<any>(null);
|
const moderators = ref<Misskey.entities.UserDetailed[] | null>(null);
|
||||||
const fetching = ref(true);
|
const fetching = ref(true);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
|
@ -13,10 +13,17 @@ import { Chart } from 'chart.js';
|
||||||
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
|
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
|
||||||
import { initChart } from '@/scripts/init-chart.js';
|
import { initChart } from '@/scripts/init-chart.js';
|
||||||
|
|
||||||
|
export type InstanceForPie = {
|
||||||
|
name: string,
|
||||||
|
color: string | null,
|
||||||
|
value: number,
|
||||||
|
onClick?: () => void
|
||||||
|
};
|
||||||
|
|
||||||
initChart();
|
initChart();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: { name: string; value: number; color: string; onClick?: () => void }[];
|
data: InstanceForPie[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const chartEl = shallowRef<HTMLCanvasElement>(null);
|
const chartEl = shallowRef<HTMLCanvasElement>(null);
|
||||||
|
|
|
@ -62,6 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import MkNumberDiff from '@/components/MkNumberDiff.vue';
|
import MkNumberDiff from '@/components/MkNumberDiff.vue';
|
||||||
import MkNumber from '@/components/MkNumber.vue';
|
import MkNumber from '@/components/MkNumber.vue';
|
||||||
|
@ -69,7 +70,7 @@ import { i18n } from '@/i18n.js';
|
||||||
import { customEmojis } from '@/custom-emojis.js';
|
import { customEmojis } from '@/custom-emojis.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
const stats = ref<any>(null);
|
const stats = ref<Misskey.entities.StatsResponse | null>(null);
|
||||||
const usersComparedToThePrevDay = ref<number>();
|
const usersComparedToThePrevDay = ref<number>();
|
||||||
const notesComparedToThePrevDay = ref<number>();
|
const notesComparedToThePrevDay = ref<number>();
|
||||||
const onlineUsersCount = ref(0);
|
const onlineUsersCount = ref(0);
|
||||||
|
|
|
@ -18,12 +18,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { useInterval } from '@/scripts/use-interval.js';
|
import { useInterval } from '@/scripts/use-interval.js';
|
||||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
const newUsers = ref(null);
|
const newUsers = ref<Misskey.entities.UserDetailed[] | null>(null);
|
||||||
const fetching = ref(true);
|
const fetching = ref(true);
|
||||||
|
|
||||||
const fetch = async () => {
|
const fetch = async () => {
|
||||||
|
|
|
@ -66,6 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { markRaw, onMounted, onBeforeUnmount, nextTick, shallowRef, ref, computed } from 'vue';
|
import { markRaw, onMounted, onBeforeUnmount, nextTick, shallowRef, ref, computed } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import XFederation from './overview.federation.vue';
|
import XFederation from './overview.federation.vue';
|
||||||
import XInstances from './overview.instances.vue';
|
import XInstances from './overview.instances.vue';
|
||||||
import XQueue from './overview.queue.vue';
|
import XQueue from './overview.queue.vue';
|
||||||
|
@ -76,6 +77,7 @@ import XStats from './overview.stats.vue';
|
||||||
import XRetention from './overview.retention.vue';
|
import XRetention from './overview.retention.vue';
|
||||||
import XModerators from './overview.moderators.vue';
|
import XModerators from './overview.moderators.vue';
|
||||||
import XHeatmap from './overview.heatmap.vue';
|
import XHeatmap from './overview.heatmap.vue';
|
||||||
|
import type { InstanceForPie } from './overview.pie.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
@ -83,15 +85,15 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
||||||
|
|
||||||
const rootEl = shallowRef<HTMLElement>();
|
const rootEl = shallowRef<HTMLElement>();
|
||||||
const serverInfo = ref<any>(null);
|
const serverInfo = ref<Misskey.entities.ServerInfoResponse | null>(null);
|
||||||
const topSubInstancesForPie = ref<any>(null);
|
const topSubInstancesForPie = ref<InstanceForPie[] | null>(null);
|
||||||
const topPubInstancesForPie = ref<any>(null);
|
const topPubInstancesForPie = ref<InstanceForPie[] | null>(null);
|
||||||
const federationPubActive = ref<number | null>(null);
|
const federationPubActive = ref<number | null>(null);
|
||||||
const federationPubActiveDiff = ref<number | null>(null);
|
const federationPubActiveDiff = ref<number | null>(null);
|
||||||
const federationSubActive = ref<number | null>(null);
|
const federationSubActive = ref<number | null>(null);
|
||||||
const federationSubActiveDiff = ref<number | null>(null);
|
const federationSubActiveDiff = ref<number | null>(null);
|
||||||
const newUsers = ref(null);
|
const newUsers = ref<Misskey.entities.UserDetailed[] | null>(null);
|
||||||
const activeInstances = shallowRef(null);
|
const activeInstances = shallowRef<Misskey.entities.FederationInstance | null>(null);
|
||||||
const queueStatsConnection = markRaw(useStream().useChannel('queueStats'));
|
const queueStatsConnection = markRaw(useStream().useChannel('queueStats'));
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const filesPagination = {
|
const filesPagination = {
|
||||||
|
@ -123,22 +125,28 @@ onMounted(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
os.apiGet('federation/stats', { limit: 10 }).then(res => {
|
os.apiGet('federation/stats', { limit: 10 }).then(res => {
|
||||||
topSubInstancesForPie.value = res.topSubInstances.map(x => ({
|
topSubInstancesForPie.value = [
|
||||||
name: x.host,
|
...res.topSubInstances.map(x => ({
|
||||||
color: x.themeColor,
|
name: x.host,
|
||||||
value: x.followersCount,
|
color: x.themeColor,
|
||||||
onClick: () => {
|
value: x.followersCount,
|
||||||
os.pageWindow(`/instance-info/${x.host}`);
|
onClick: () => {
|
||||||
},
|
os.pageWindow(`/instance-info/${x.host}`);
|
||||||
})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]);
|
},
|
||||||
topPubInstancesForPie.value = res.topPubInstances.map(x => ({
|
})),
|
||||||
name: x.host,
|
{ name: '(other)', color: '#80808080', value: res.otherFollowersCount },
|
||||||
color: x.themeColor,
|
];
|
||||||
value: x.followingCount,
|
topPubInstancesForPie.value = [
|
||||||
onClick: () => {
|
...res.topPubInstances.map(x => ({
|
||||||
os.pageWindow(`/instance-info/${x.host}`);
|
name: x.host,
|
||||||
},
|
color: x.themeColor,
|
||||||
})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowingCount }]);
|
value: x.followingCount,
|
||||||
|
onClick: () => {
|
||||||
|
os.pageWindow(`/instance-info/${x.host}`);
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
{ name: '(other)', color: '#80808080', value: res.otherFollowingCount },
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
os.api('admin/server-info').then(serverInfoResponse => {
|
os.api('admin/server-info').then(serverInfoResponse => {
|
||||||
|
|
|
@ -22,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
|
@ -31,8 +32,8 @@ import { fetchInstance } from '@/instance.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
const proxyAccount = ref<any>(null);
|
const proxyAccount = ref<Misskey.entities.UserDetailed | null>(null);
|
||||||
const proxyAccountId = ref<any>(null);
|
const proxyAccountId = ref<string | null>(null);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api('admin/meta');
|
const meta = await os.api('admin/meta');
|
||||||
|
|
|
@ -62,7 +62,7 @@ const activeSincePrevTick = ref(0);
|
||||||
const active = ref(0);
|
const active = ref(0);
|
||||||
const delayed = ref(0);
|
const delayed = ref(0);
|
||||||
const waiting = ref(0);
|
const waiting = ref(0);
|
||||||
const jobs = ref([]);
|
const jobs = ref<(string | number)[][]>([]);
|
||||||
const chartProcess = shallowRef<InstanceType<typeof XChart>>();
|
const chartProcess = shallowRef<InstanceType<typeof XChart>>();
|
||||||
const chartActive = shallowRef<InstanceType<typeof XChart>>();
|
const chartActive = shallowRef<InstanceType<typeof XChart>>();
|
||||||
const chartDelayed = shallowRef<InstanceType<typeof XChart>>();
|
const chartDelayed = shallowRef<InstanceType<typeof XChart>>();
|
||||||
|
@ -104,9 +104,11 @@ const onStatsLog = (statsLog) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
os.api(props.domain === 'inbox' ? 'admin/queue/inbox-delayed' : props.domain === 'deliver' ? 'admin/queue/deliver-delayed' : null, {}).then(result => {
|
if (props.domain === 'inbox' || props.domain === 'deliver') {
|
||||||
jobs.value = result;
|
os.api(`admin/queue/${props.domain}-delayed`).then(result => {
|
||||||
});
|
jobs.value = result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
connection.on('stats', onStats);
|
connection.on('stats', onStats);
|
||||||
connection.on('statsLog', onStatsLog);
|
connection.on('statsLog', onStatsLog);
|
||||||
|
|
|
@ -25,13 +25,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
const relays = ref<any[]>([]);
|
const relays = ref<Misskey.entities.AdminRelaysListResponse>([]);
|
||||||
|
|
||||||
async function addRelay() {
|
async function addRelay() {
|
||||||
const { canceled, result: inbox } = await os.inputText({
|
const { canceled, result: inbox } = await os.inputText({
|
||||||
|
@ -66,7 +67,7 @@ function remove(inbox: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
os.api('admin/relays/list').then((relayList: any) => {
|
os.api('admin/relays/list').then(relayList => {
|
||||||
relays.value = relayList;
|
relays.value = relayList;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue