diff --git a/locales/en-US.yml b/locales/en-US.yml index 46451ba7a1..88316786df 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -144,8 +144,10 @@ markAsSensitive: "Mark as sensitive" unmarkAsSensitive: "Unmark as sensitive" enterFileName: "Enter filename" mute: "Mute" +muted: "Muted" unmute: "Unmute" renoteMute: "Mute Boosts" +renoteMuted: "Boosts muted" renoteUnmute: "Unmute Boosts" block: "Block" unblock: "Unblock" @@ -185,7 +187,7 @@ flagAsBotDescription: "Enable this option if this account is controlled by a pro flagAsCat: "Mark this account as a cat" flagAsCatDescription: "Enable this option to mark this account as a cat." flagSpeakAsCat: "Speak as a cat" -flagSpeakAsCatDescription: "Your posts will get nyanified when in cat mode." +flagSpeakAsCatDescription: "Your posts will get nyanified when in cat mode. If this isn't working, then please check that you dont have 'Disable cat speak' on under General/Note Display" flagShowTimelineReplies: "Show replies in timeline" flagShowTimelineRepliesDescription: "Shows replies of users to notes of other users in the timeline if turned on." autoAcceptFollowed: "Automatically approve follow requests from users you're following" @@ -780,6 +782,7 @@ lockedAccountInfo: "Unless you set your note visiblity to \"Followers only\", yo alwaysMarkSensitive: "Mark as sensitive by default" loadRawImages: "Load original images instead of showing thumbnails" showTickerOnReplies: "Show instance ticker on replies" +disableCatSpeak: "Disable cat speak" searchEngine: "Search Engine For Search MFM" searchEngineOther: "Other" searchEngineCustomURIDescription: "The custom URI must be input in the format like \"https://www.google.com/search?q=\\{query}\" or \"https://www.google.com/search?q=%s\"." @@ -1280,6 +1283,7 @@ detach: "Remove" detachAll: "Remove All" angle: "Angle" flip: "Flip" +showBelowAvatar: "Show Below Avatar" showAvatarDecorations: "Show avatar decorations" releaseToRefresh: "Release to refresh" refreshing: "Refreshing..." diff --git a/locales/index.d.ts b/locales/index.d.ts index a985192e07..ebc2e63110 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -592,6 +592,10 @@ export interface Locale extends ILocale { * ミュート */ "mute": string; + /** + * Muted + */ + "muted": string; /** * ミュート解除 */ @@ -600,6 +604,10 @@ export interface Locale extends ILocale { * ブーストをミュート */ "renoteMute": string; + /** + * Boosts muted + */ + "renoteMuted": string; /** * ブーストのミュートを解除 */ @@ -3136,6 +3144,10 @@ export interface Locale extends ILocale { * 返信にサーバー情報を表示する */ "showTickerOnReplies": string; + /** + * 猫の話し方を無効にする + */ + "disableCatSpeak": string; /** * 検索MFMの検索エンジン */ @@ -5137,6 +5149,10 @@ export interface Locale extends ILocale { * 反転 */ "flip": string; + /** + * アイコンの後ろに表示 + */ + "showBelowAvatar": string; /** * アイコンのデコレーションを表示 */ @@ -5777,7 +5793,7 @@ export interface Locale extends ILocale { */ "social": string; /** - * バッッブルタイムラインでは、管理者が選択した接続サーバーからのメモを表示できます。 + * バブルタイムラインでは、管理者が選択した接続サーバーからの投稿を表示できます。 */ "bubble": string; /** @@ -9139,7 +9155,7 @@ export interface Locale extends ILocale { */ "global": string; /** - * バッッブル + * バブル */ "bubble": string; }; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index c1b7ff75c4..304a1d886c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -144,8 +144,10 @@ markAsSensitive: "センシティブとして設定" unmarkAsSensitive: "センシティブを解除する" enterFileName: "ファイル名を入力" mute: "ミュート" +muted: "Muted" unmute: "ミュート解除" renoteMute: "ブーストをミュート" +renoteMuted: "Boosts muted" renoteUnmute: "ブーストのミュートを解除" block: "ブロック" unblock: "ブロック解除" @@ -780,6 +782,7 @@ lockedAccountInfo: "フォローを承認制にしても、ノートの公開範 alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする" loadRawImages: "添付画像のサムネイルをオリジナル画質にする" showTickerOnReplies: "返信にサーバー情報を表示する" +disableCatSpeak: "猫の話し方を無効にする" searchEngine: "検索MFMの検索エンジン" searchEngineOther: "カスタム" searchEngineCustomURIDescription: "カスタム検索エンジンのURIは、\"https://www.google.com/search?q=\\{query}\" や \"https://www.google.com/search?q=%s\" のような形式で入力する必要があります。" @@ -1280,6 +1283,7 @@ detach: "外す" detachAll: "全て外す" angle: "角度" flip: "反転" +showBelowAvatar: "アイコンの後ろに表示" showAvatarDecorations: "アイコンのデコレーションを表示" releaseToRefresh: "離してリロード" refreshing: "リロード中" diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 55d1054de9..499a163d6c 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -526,6 +526,7 @@ export class ApRendererService { publicKey: this.renderKey(user, keypair, '#main-key'), isCat: user.isCat, noindex: user.noindex, + indexable: !user.noindex, speakAsCat: user.speakAsCat, attachment: attachment.length ? attachment : undefined, }; diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts index 815b20b910..86a665732a 100644 --- a/packages/backend/src/core/activitypub/misc/contexts.ts +++ b/packages/backend/src/core/activitypub/misc/contexts.ts @@ -545,6 +545,7 @@ const extension_context_definition = { Emoji: 'toot:Emoji', featured: 'toot:featured', discoverable: 'toot:discoverable', + indexable: 'toot:indexable', // schema schema: 'http://schema.org#', PropertyValue: 'schema:PropertyValue', diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 7b7a7921fb..382cda301f 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -587,7 +587,7 @@ export class ApNoteService { // ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにノートが生成されるが // 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。 const createFrom = options.sentFrom?.origin === new URL(uri).origin ? value : uri; - return await this.createNote(createFrom, options.resolver, true); + return await this.createNote(createFrom, options.resolver, false); } finally { unlock(); } diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 2b1c4d5c63..40830f86b4 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -525,6 +525,7 @@ export class UserEntityService implements OnModuleInit { flipH: ud.flipH || undefined, offsetX: ud.offsetX || undefined, offsetY: ud.offsetY || undefined, + showBelow: ud.showBelow || undefined, url: decorations.find(d => d.id === ud.id)!.url, }))) : [], isBot: user.isBot, diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index b0910133c9..cbebd0102d 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -170,6 +170,7 @@ export class MiUser { flipH?: boolean; offsetX?: number; offsetY?: number; + showBelow?: boolean; }[]; @Index() diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 33a3efd453..249b9bba38 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -104,6 +104,10 @@ export const packedUserLiteSchema = { type: 'number', nullable: false, optional: true, }, + showBelow: { + type: 'boolean', + nullable: false, optional: true, + }, }, }, }, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 6cc22e7994..f9b8061249 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -159,6 +159,7 @@ export const paramDef = { flipH: { type: 'boolean', nullable: true }, offsetX: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 }, offsetY: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 }, + showBelow: { type: 'boolean', nullable: true }, }, required: ['id'], } }, @@ -417,6 +418,7 @@ export default class extends Endpoint { // eslint- flipH: d.flipH ?? false, offsetX: d.offsetX ?? 0, offsetY: d.offsetY ?? 0, + showBelow: d.showBelow ?? false, })); } diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 0af90a844b..e20707977f 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -193,9 +193,9 @@ export class ClientServerService { icon: meta.iconUrl, appleTouchIcon: meta.app512IconUrl, themeColor: meta.themeColor, - serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://launcher.moe/error.png', - infoImageUrl: meta.infoImageUrl ?? 'https://launcher.moe/nothinghere.png', - notFoundImageUrl: meta.notFoundImageUrl ?? 'https://launcher.moe/missingpage.webp', + serverErrorImageUrl: meta.serverErrorImageUrl ?? '/status/error.png', + infoImageUrl: meta.infoImageUrl ?? '/status/nothinghere.png', + notFoundImageUrl: meta.notFoundImageUrl ?? '/status/missingpage.webp', instanceUrl: this.config.url, randomMOTD: this.config.customMOTD ? this.config.customMOTD[Math.floor(Math.random() * this.config.customMOTD.length)] : undefined, metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)), diff --git a/packages/frontend/assets/status/error.png b/packages/frontend/assets/status/error.png new file mode 100644 index 0000000000..9f21236c39 Binary files /dev/null and b/packages/frontend/assets/status/error.png differ diff --git a/packages/frontend/assets/status/missingpage.webp b/packages/frontend/assets/status/missingpage.webp new file mode 100644 index 0000000000..3ac83b3110 Binary files /dev/null and b/packages/frontend/assets/status/missingpage.webp differ diff --git a/packages/frontend/assets/status/nothinghere.png b/packages/frontend/assets/status/nothinghere.png new file mode 100644 index 0000000000..5ebe210acd Binary files /dev/null and b/packages/frontend/assets/status/nothinghere.png differ diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index c10930a038..5ff998fac4 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -216,19 +216,25 @@ export async function mainBoot() { claimAchievement('collectAchievements30'); } - window.setInterval(() => { - if (Math.floor(Math.random() * 20000) === 0) { - claimAchievement('justPlainLucky'); - } - }, 1000 * 10); + if (!claimedAchievements.includes('justPlainLucky')) { + window.setInterval(() => { + if (Math.floor(Math.random() * 20000) === 0) { + claimAchievement('justPlainLucky'); + } + }, 1000 * 10); + } - window.setTimeout(() => { - claimAchievement('client30min'); - }, 1000 * 60 * 30); + if (!claimedAchievements.includes('client30min')) { + window.setTimeout(() => { + claimAchievement('client30min'); + }, 1000 * 60 * 30); + } - window.setTimeout(() => { - claimAchievement('client60min'); - }, 1000 * 60 * 60); + if (!claimedAchievements.includes('client60min')) { + window.setTimeout(() => { + claimAchievement('client60min'); + }, 1000 * 60 * 60); + } // 邪魔 //const lastUsed = miLocalStorage.getItem('lastUsed'); diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index dc3f3aa94c..add5296f0a 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -630,11 +630,22 @@ async function onPaste(ev: ClipboardEvent) { if (paste.length > 1000) { ev.preventDefault(); - os.confirm({ - type: 'info', + os.actions({ + type: 'question', text: i18n.ts.attachAsFileQuestion, - }).then(({ canceled }) => { - if (canceled) { + actions: [ + { + value: 'yes', + text: i18n.ts.yes, + primary: true, + }, + { + value: 'no', + text: i18n.ts.no, + }, + ], + }).then(({ result }) => { + if (result !== 'yes') { insertTextAtCursor(textareaEl.value, paste); return; } diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index de62fe12a9..4a01d3f32d 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -32,6 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only rotate: getDecorationAngle(decoration), scale: getDecorationScale(decoration), translate: getDecorationOffset(decoration), + zIndex: getDecorationZIndex(decoration), }" alt="" > @@ -113,6 +114,10 @@ function getDecorationOffset(decoration: Omit) { + return decoration.showBelow ? '-1' : undefined; +} + const color = ref(); watch(() => props.user.avatarBlurhash, () => { @@ -159,6 +164,7 @@ watch(() => props.user.avatarBlurhash, () => { flex-shrink: 0; border-radius: 100%; // sharkey: controlled by square avatars setting! line-height: 16px; + z-index: 0; // sharkey: starts stacking context to help with showing decorations behind the avatar } .inner { diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index a3a2b9f319..5046f17357 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -58,8 +58,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext = { diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html index 733116b75f..fdeb642c70 100644 --- a/packages/frontend/src/index.html +++ b/packages/frontend/src/index.html @@ -20,7 +20,7 @@ worker-src 'self'; script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com https://esm.sh; style-src 'self' 'unsafe-inline'; - img-src 'self' data: blob: www.google.com xn--931a.moe launcher.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 activitypub.software secure.gravatar.com avatars.githubusercontent.com; + img-src 'self' data: blob: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 activitypub.software secure.gravatar.com avatars.githubusercontent.com; media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000; connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 https://newassets.hcaptcha.com https://api.listenbrainz.org; frame-src *;" diff --git a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue index 0767fa7864..9f7852a71d 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only @click="emit('click')" >
{{ decoration.name }}
- + @@ -32,6 +32,7 @@ const props = defineProps<{ flipH?: boolean; offsetX?: number; offsetY?: number; + showBelow?: boolean; }>(); const emit = defineEmits<{ diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue index ce1d4e48d8..400b365ca6 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue @@ -29,6 +29,9 @@ SPDX-License-Identifier: AGPL-3.0-only + + + @@ -71,12 +74,14 @@ const emit = defineEmits<{ flipH: boolean; offsetX: number; offsetY: number; + showBelow: boolean; }): void; (ev: 'update', payload: { angle: number; flipH: boolean; offsetX: number; offsetY: number; + showBelow: boolean; }): void; (ev: 'detach'): void; }>(); @@ -87,6 +92,7 @@ const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIn const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false); const offsetX = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetX : null) ?? 0); const offsetY = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetY : null) ?? 0); +const showBelow = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].showBelow : null) ?? false); const decorationsForPreview = computed(() => { const decoration = { @@ -96,6 +102,7 @@ const decorationsForPreview = computed(() => { flipH: flipH.value, offsetX: offsetX.value, offsetY: offsetY.value, + showBelow: showBelow.value, }; const decorations = [...$i.avatarDecorations]; if (props.usingIndex != null) { @@ -116,6 +123,7 @@ async function update() { flipH: flipH.value, offsetX: offsetX.value, offsetY: offsetY.value, + showBelow: showBelow.value, }); dialog.value.close(); } @@ -126,6 +134,7 @@ async function attach() { flipH: flipH.value, offsetX: offsetX.value, offsetY: offsetY.value, + showBelow: showBelow.value, }); dialog.value.close(); } diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue index 77229d3349..5324a6b7f7 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.vue @@ -21,6 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only :flipH="avatarDecoration.flipH" :offsetX="avatarDecoration.offsetX" :offsetY="avatarDecoration.offsetY" + :showBelow="avatarDecoration.showBelow" :active="true" @click="openDecoration(avatarDecoration, i)" /> @@ -78,6 +79,7 @@ function openDecoration(avatarDecoration, index?: number) { flipH: payload.flipH, offsetX: payload.offsetX, offsetY: payload.offsetY, + showBelow: payload.showBelow, }; const update = [...$i.avatarDecorations, decoration]; await os.apiWithDialog('i/update', { @@ -92,6 +94,7 @@ function openDecoration(avatarDecoration, index?: number) { flipH: payload.flipH, offsetX: payload.offsetX, offsetY: payload.offsetY, + showBelow: payload.showBelow, }; const update = [...$i.avatarDecorations]; update[index] = decoration; diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 637c1b24b9..8cd1caf6bd 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -68,6 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.showGapBetweenNotesInTimeline }} {{ i18n.ts.loadRawImages }} {{ i18n.ts.showTickerOnReplies }} + {{ i18n.ts.disableCatSpeak }}