Merge branch 'develop' into hazelnoot/following-timeline

This commit is contained in:
Hazel K 2024-10-06 11:16:43 -04:00
commit 4ca27692ac
28 changed files with 129 additions and 36 deletions

View file

@ -144,8 +144,10 @@ markAsSensitive: "Mark as sensitive"
unmarkAsSensitive: "Unmark as sensitive" unmarkAsSensitive: "Unmark as sensitive"
enterFileName: "Enter filename" enterFileName: "Enter filename"
mute: "Mute" mute: "Mute"
muted: "Muted"
unmute: "Unmute" unmute: "Unmute"
renoteMute: "Mute Boosts" renoteMute: "Mute Boosts"
renoteMuted: "Boosts muted"
renoteUnmute: "Unmute Boosts" renoteUnmute: "Unmute Boosts"
block: "Block" block: "Block"
unblock: "Unblock" 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" flagAsCat: "Mark this account as a cat"
flagAsCatDescription: "Enable this option to mark this account as a cat." flagAsCatDescription: "Enable this option to mark this account as a cat."
flagSpeakAsCat: "Speak 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" flagShowTimelineReplies: "Show replies in timeline"
flagShowTimelineRepliesDescription: "Shows replies of users to notes of other users in the timeline if turned on." 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" 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" alwaysMarkSensitive: "Mark as sensitive by default"
loadRawImages: "Load original images instead of showing thumbnails" loadRawImages: "Load original images instead of showing thumbnails"
showTickerOnReplies: "Show instance ticker on replies" showTickerOnReplies: "Show instance ticker on replies"
disableCatSpeak: "Disable cat speak"
searchEngine: "Search Engine For Search MFM" searchEngine: "Search Engine For Search MFM"
searchEngineOther: "Other" 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\"." 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" detachAll: "Remove All"
angle: "Angle" angle: "Angle"
flip: "Flip" flip: "Flip"
showBelowAvatar: "Show Below Avatar"
showAvatarDecorations: "Show avatar decorations" showAvatarDecorations: "Show avatar decorations"
releaseToRefresh: "Release to refresh" releaseToRefresh: "Release to refresh"
refreshing: "Refreshing..." refreshing: "Refreshing..."

20
locales/index.d.ts vendored
View file

@ -592,6 +592,10 @@ export interface Locale extends ILocale {
* *
*/ */
"mute": string; "mute": string;
/**
* Muted
*/
"muted": string;
/** /**
* *
*/ */
@ -600,6 +604,10 @@ export interface Locale extends ILocale {
* *
*/ */
"renoteMute": string; "renoteMute": string;
/**
* Boosts muted
*/
"renoteMuted": string;
/** /**
* *
*/ */
@ -3136,6 +3144,10 @@ export interface Locale extends ILocale {
* *
*/ */
"showTickerOnReplies": string; "showTickerOnReplies": string;
/**
*
*/
"disableCatSpeak": string;
/** /**
* MFMの検索エンジン * MFMの検索エンジン
*/ */
@ -5137,6 +5149,10 @@ export interface Locale extends ILocale {
* *
*/ */
"flip": string; "flip": string;
/**
*
*/
"showBelowAvatar": string;
/** /**
* *
*/ */
@ -5777,7 +5793,7 @@ export interface Locale extends ILocale {
*/ */
"social": string; "social": string;
/** /**
* * 稿
*/ */
"bubble": string; "bubble": string;
/** /**
@ -9139,7 +9155,7 @@ export interface Locale extends ILocale {
*/ */
"global": string; "global": string;
/** /**
* *
*/ */
"bubble": string; "bubble": string;
}; };

View file

@ -144,8 +144,10 @@ markAsSensitive: "センシティブとして設定"
unmarkAsSensitive: "センシティブを解除する" unmarkAsSensitive: "センシティブを解除する"
enterFileName: "ファイル名を入力" enterFileName: "ファイル名を入力"
mute: "ミュート" mute: "ミュート"
muted: "Muted"
unmute: "ミュート解除" unmute: "ミュート解除"
renoteMute: "ブーストをミュート" renoteMute: "ブーストをミュート"
renoteMuted: "Boosts muted"
renoteUnmute: "ブーストのミュートを解除" renoteUnmute: "ブーストのミュートを解除"
block: "ブロック" block: "ブロック"
unblock: "ブロック解除" unblock: "ブロック解除"
@ -780,6 +782,7 @@ lockedAccountInfo: "フォローを承認制にしても、ノートの公開範
alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする" alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする"
loadRawImages: "添付画像のサムネイルをオリジナル画質にする" loadRawImages: "添付画像のサムネイルをオリジナル画質にする"
showTickerOnReplies: "返信にサーバー情報を表示する" showTickerOnReplies: "返信にサーバー情報を表示する"
disableCatSpeak: "猫の話し方を無効にする"
searchEngine: "検索MFMの検索エンジン" searchEngine: "検索MFMの検索エンジン"
searchEngineOther: "カスタム" searchEngineOther: "カスタム"
searchEngineCustomURIDescription: "カスタム検索エンジンのURIは、\"https://www.google.com/search?q=\\{query}\" や \"https://www.google.com/search?q=%s\" のような形式で入力する必要があります。" searchEngineCustomURIDescription: "カスタム検索エンジンのURIは、\"https://www.google.com/search?q=\\{query}\" や \"https://www.google.com/search?q=%s\" のような形式で入力する必要があります。"
@ -1280,6 +1283,7 @@ detach: "外す"
detachAll: "全て外す" detachAll: "全て外す"
angle: "角度" angle: "角度"
flip: "反転" flip: "反転"
showBelowAvatar: "アイコンの後ろに表示"
showAvatarDecorations: "アイコンのデコレーションを表示" showAvatarDecorations: "アイコンのデコレーションを表示"
releaseToRefresh: "離してリロード" releaseToRefresh: "離してリロード"
refreshing: "リロード中" refreshing: "リロード中"

View file

@ -526,6 +526,7 @@ export class ApRendererService {
publicKey: this.renderKey(user, keypair, '#main-key'), publicKey: this.renderKey(user, keypair, '#main-key'),
isCat: user.isCat, isCat: user.isCat,
noindex: user.noindex, noindex: user.noindex,
indexable: !user.noindex,
speakAsCat: user.speakAsCat, speakAsCat: user.speakAsCat,
attachment: attachment.length ? attachment : undefined, attachment: attachment.length ? attachment : undefined,
}; };

View file

@ -545,6 +545,7 @@ const extension_context_definition = {
Emoji: 'toot:Emoji', Emoji: 'toot:Emoji',
featured: 'toot:featured', featured: 'toot:featured',
discoverable: 'toot:discoverable', discoverable: 'toot:discoverable',
indexable: 'toot:indexable',
// schema // schema
schema: 'http://schema.org#', schema: 'http://schema.org#',
PropertyValue: 'schema:PropertyValue', PropertyValue: 'schema:PropertyValue',

View file

@ -587,7 +587,7 @@ export class ApNoteService {
// ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにートが生成されるが // ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにートが生成されるが
// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。 // 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
const createFrom = options.sentFrom?.origin === new URL(uri).origin ? value : 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 { } finally {
unlock(); unlock();
} }

View file

@ -525,6 +525,7 @@ export class UserEntityService implements OnModuleInit {
flipH: ud.flipH || undefined, flipH: ud.flipH || undefined,
offsetX: ud.offsetX || undefined, offsetX: ud.offsetX || undefined,
offsetY: ud.offsetY || undefined, offsetY: ud.offsetY || undefined,
showBelow: ud.showBelow || undefined,
url: decorations.find(d => d.id === ud.id)!.url, url: decorations.find(d => d.id === ud.id)!.url,
}))) : [], }))) : [],
isBot: user.isBot, isBot: user.isBot,

View file

@ -170,6 +170,7 @@ export class MiUser {
flipH?: boolean; flipH?: boolean;
offsetX?: number; offsetX?: number;
offsetY?: number; offsetY?: number;
showBelow?: boolean;
}[]; }[];
@Index() @Index()

View file

@ -104,6 +104,10 @@ export const packedUserLiteSchema = {
type: 'number', type: 'number',
nullable: false, optional: true, nullable: false, optional: true,
}, },
showBelow: {
type: 'boolean',
nullable: false, optional: true,
},
}, },
}, },
}, },

View file

@ -159,6 +159,7 @@ export const paramDef = {
flipH: { type: 'boolean', nullable: true }, flipH: { type: 'boolean', nullable: true },
offsetX: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 }, offsetX: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 },
offsetY: { 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'], required: ['id'],
} }, } },
@ -417,6 +418,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
flipH: d.flipH ?? false, flipH: d.flipH ?? false,
offsetX: d.offsetX ?? 0, offsetX: d.offsetX ?? 0,
offsetY: d.offsetY ?? 0, offsetY: d.offsetY ?? 0,
showBelow: d.showBelow ?? false,
})); }));
} }

View file

@ -193,9 +193,9 @@ export class ClientServerService {
icon: meta.iconUrl, icon: meta.iconUrl,
appleTouchIcon: meta.app512IconUrl, appleTouchIcon: meta.app512IconUrl,
themeColor: meta.themeColor, themeColor: meta.themeColor,
serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://launcher.moe/error.png', serverErrorImageUrl: meta.serverErrorImageUrl ?? '/status/error.png',
infoImageUrl: meta.infoImageUrl ?? 'https://launcher.moe/nothinghere.png', infoImageUrl: meta.infoImageUrl ?? '/status/nothinghere.png',
notFoundImageUrl: meta.notFoundImageUrl ?? 'https://launcher.moe/missingpage.webp', notFoundImageUrl: meta.notFoundImageUrl ?? '/status/missingpage.webp',
instanceUrl: this.config.url, instanceUrl: this.config.url,
randomMOTD: this.config.customMOTD ? this.config.customMOTD[Math.floor(Math.random() * this.config.customMOTD.length)] : undefined, randomMOTD: this.config.customMOTD ? this.config.customMOTD[Math.floor(Math.random() * this.config.customMOTD.length)] : undefined,
metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)), metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)),

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -216,19 +216,25 @@ export async function mainBoot() {
claimAchievement('collectAchievements30'); claimAchievement('collectAchievements30');
} }
window.setInterval(() => { if (!claimedAchievements.includes('justPlainLucky')) {
if (Math.floor(Math.random() * 20000) === 0) { window.setInterval(() => {
claimAchievement('justPlainLucky'); if (Math.floor(Math.random() * 20000) === 0) {
} claimAchievement('justPlainLucky');
}, 1000 * 10); }
}, 1000 * 10);
}
window.setTimeout(() => { if (!claimedAchievements.includes('client30min')) {
claimAchievement('client30min'); window.setTimeout(() => {
}, 1000 * 60 * 30); claimAchievement('client30min');
}, 1000 * 60 * 30);
}
window.setTimeout(() => { if (!claimedAchievements.includes('client60min')) {
claimAchievement('client60min'); window.setTimeout(() => {
}, 1000 * 60 * 60); claimAchievement('client60min');
}, 1000 * 60 * 60);
}
// 邪魔 // 邪魔
//const lastUsed = miLocalStorage.getItem('lastUsed'); //const lastUsed = miLocalStorage.getItem('lastUsed');

View file

@ -630,11 +630,22 @@ async function onPaste(ev: ClipboardEvent) {
if (paste.length > 1000) { if (paste.length > 1000) {
ev.preventDefault(); ev.preventDefault();
os.confirm({ os.actions({
type: 'info', type: 'question',
text: i18n.ts.attachAsFileQuestion, text: i18n.ts.attachAsFileQuestion,
}).then(({ canceled }) => { actions: [
if (canceled) { {
value: 'yes',
text: i18n.ts.yes,
primary: true,
},
{
value: 'no',
text: i18n.ts.no,
},
],
}).then(({ result }) => {
if (result !== 'yes') {
insertTextAtCursor(textareaEl.value, paste); insertTextAtCursor(textareaEl.value, paste);
return; return;
} }

View file

@ -32,6 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
rotate: getDecorationAngle(decoration), rotate: getDecorationAngle(decoration),
scale: getDecorationScale(decoration), scale: getDecorationScale(decoration),
translate: getDecorationOffset(decoration), translate: getDecorationOffset(decoration),
zIndex: getDecorationZIndex(decoration),
}" }"
alt="" alt=""
> >
@ -113,6 +114,10 @@ function getDecorationOffset(decoration: Omit<Misskey.entities.UserDetailed['ava
return offsetX === 0 && offsetY === 0 ? undefined : `${offsetX * 100}% ${offsetY * 100}%`; return offsetX === 0 && offsetY === 0 ? undefined : `${offsetX * 100}% ${offsetY * 100}%`;
} }
function getDecorationZIndex(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
return decoration.showBelow ? '-1' : undefined;
}
const color = ref<string | undefined>(); const color = ref<string | undefined>();
watch(() => props.user.avatarBlurhash, () => { watch(() => props.user.avatarBlurhash, () => {
@ -159,6 +164,7 @@ watch(() => props.user.avatarBlurhash, () => {
flex-shrink: 0; flex-shrink: 0;
border-radius: 100%; // sharkey: controlled by square avatars setting! border-radius: 100%; // sharkey: controlled by square avatars setting!
line-height: 16px; line-height: 16px;
z-index: 0; // sharkey: starts stacking context to help with showing decorations behind the avatar
} }
.inner { .inner {

View file

@ -58,8 +58,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
provide('linkNavigationBehavior', props.linkNavigationBehavior); provide('linkNavigationBehavior', props.linkNavigationBehavior);
const isNote = props.isNote ?? true; const isNote = props.isNote ?? true;
const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat ? props.author.speakAsCat : false : false : false; const shouldNyaize = props.nyaize === 'respect' && props.author?.isCat && props.author?.speakAsCat && !defaultStore.state.disableCatSpeak;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (props.text == null || props.text === '') return; if (props.text == null || props.text === '') return;

View file

@ -160,9 +160,9 @@ export const ROLE_POLICIES = [
export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP'; export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP';
export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM'; export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM';
export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://launcher.moe/error.png'; export const DEFAULT_SERVER_ERROR_IMAGE_URL = '/status/error.png';
export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://launcher.moe/missingpage.webp'; export const DEFAULT_NOT_FOUND_IMAGE_URL = '/status/missingpage.webp';
export const DEFAULT_INFO_IMAGE_URL = 'https://launcher.moe/nothinghere.png'; export const DEFAULT_INFO_IMAGE_URL = '/status/nothinghere.png';
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime', 'crop', 'fade', 'followmouse']; export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime', 'crop', 'fade', 'followmouse'];
export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = {

View file

@ -20,7 +20,7 @@
worker-src 'self'; worker-src 'self';
script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com https://esm.sh; script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com https://esm.sh;
style-src 'self' 'unsafe-inline'; 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; 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; 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 *;" frame-src *;"

View file

@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@click="emit('click')" @click="emit('click')"
> >
<div :class="$style.name"><MkCondensedLine :minScale="0.5">{{ decoration.name }}</MkCondensedLine></div> <div :class="$style.name"><MkCondensedLine :minScale="0.5">{{ decoration.name }}</MkCondensedLine></div>
<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: decoration.url, angle, flipH, offsetX, offsetY }]" forceShowDecoration/> <MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: decoration.url, angle, flipH, offsetX, offsetY, showBelow }]" forceShowDecoration/>
<i v-if="decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.lock" class="ti ti-lock"></i> <i v-if="decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.lock" class="ti ti-lock"></i>
</div> </div>
</template> </template>
@ -32,6 +32,7 @@ const props = defineProps<{
flipH?: boolean; flipH?: boolean;
offsetX?: number; offsetX?: number;
offsetY?: number; offsetY?: number;
showBelow?: boolean;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{

View file

@ -29,6 +29,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkRange v-model="offsetY" continuousUpdate :min="-0.25" :max="0.25" :step="0.025" :textConverter="(v) => `${Math.floor(v * 100)}%`"> <MkRange v-model="offsetY" continuousUpdate :min="-0.25" :max="0.25" :step="0.025" :textConverter="(v) => `${Math.floor(v * 100)}%`">
<template #label>Y {{ i18n.ts.position }}</template> <template #label>Y {{ i18n.ts.position }}</template>
</MkRange> </MkRange>
<MkSwitch v-model="showBelow">
<template #label>{{ i18n.ts.showBelowAvatar }}</template>
</MkSwitch>
<MkSwitch v-model="flipH"> <MkSwitch v-model="flipH">
<template #label>{{ i18n.ts.flip }}</template> <template #label>{{ i18n.ts.flip }}</template>
</MkSwitch> </MkSwitch>
@ -71,12 +74,14 @@ const emit = defineEmits<{
flipH: boolean; flipH: boolean;
offsetX: number; offsetX: number;
offsetY: number; offsetY: number;
showBelow: boolean;
}): void; }): void;
(ev: 'update', payload: { (ev: 'update', payload: {
angle: number; angle: number;
flipH: boolean; flipH: boolean;
offsetX: number; offsetX: number;
offsetY: number; offsetY: number;
showBelow: boolean;
}): void; }): void;
(ev: 'detach'): 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 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 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 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 decorationsForPreview = computed(() => {
const decoration = { const decoration = {
@ -96,6 +102,7 @@ const decorationsForPreview = computed(() => {
flipH: flipH.value, flipH: flipH.value,
offsetX: offsetX.value, offsetX: offsetX.value,
offsetY: offsetY.value, offsetY: offsetY.value,
showBelow: showBelow.value,
}; };
const decorations = [...$i.avatarDecorations]; const decorations = [...$i.avatarDecorations];
if (props.usingIndex != null) { if (props.usingIndex != null) {
@ -116,6 +123,7 @@ async function update() {
flipH: flipH.value, flipH: flipH.value,
offsetX: offsetX.value, offsetX: offsetX.value,
offsetY: offsetY.value, offsetY: offsetY.value,
showBelow: showBelow.value,
}); });
dialog.value.close(); dialog.value.close();
} }
@ -126,6 +134,7 @@ async function attach() {
flipH: flipH.value, flipH: flipH.value,
offsetX: offsetX.value, offsetX: offsetX.value,
offsetY: offsetY.value, offsetY: offsetY.value,
showBelow: showBelow.value,
}); });
dialog.value.close(); dialog.value.close();
} }

View file

@ -21,6 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:flipH="avatarDecoration.flipH" :flipH="avatarDecoration.flipH"
:offsetX="avatarDecoration.offsetX" :offsetX="avatarDecoration.offsetX"
:offsetY="avatarDecoration.offsetY" :offsetY="avatarDecoration.offsetY"
:showBelow="avatarDecoration.showBelow"
:active="true" :active="true"
@click="openDecoration(avatarDecoration, i)" @click="openDecoration(avatarDecoration, i)"
/> />
@ -78,6 +79,7 @@ function openDecoration(avatarDecoration, index?: number) {
flipH: payload.flipH, flipH: payload.flipH,
offsetX: payload.offsetX, offsetX: payload.offsetX,
offsetY: payload.offsetY, offsetY: payload.offsetY,
showBelow: payload.showBelow,
}; };
const update = [...$i.avatarDecorations, decoration]; const update = [...$i.avatarDecorations, decoration];
await os.apiWithDialog('i/update', { await os.apiWithDialog('i/update', {
@ -92,6 +94,7 @@ function openDecoration(avatarDecoration, index?: number) {
flipH: payload.flipH, flipH: payload.flipH,
offsetX: payload.offsetX, offsetX: payload.offsetX,
offsetY: payload.offsetY, offsetY: payload.offsetY,
showBelow: payload.showBelow,
}; };
const update = [...$i.avatarDecorations]; const update = [...$i.avatarDecorations];
update[index] = decoration; update[index] = decoration;

View file

@ -68,6 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch> <MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
<MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch> <MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
<MkSwitch v-model="showTickerOnReplies">{{ i18n.ts.showTickerOnReplies }}</MkSwitch> <MkSwitch v-model="showTickerOnReplies">{{ i18n.ts.showTickerOnReplies }}</MkSwitch>
<MkSwitch v-model="disableCatSpeak">{{ i18n.ts.disableCatSpeak }}</MkSwitch>
<MkSelect v-model="searchEngine" placeholder="Other"> <MkSelect v-model="searchEngine" placeholder="Other">
<template #label>{{ i18n.ts.searchEngine }}</template> <template #label>{{ i18n.ts.searchEngine }}</template>
<option <option
@ -400,6 +401,7 @@ const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('dis
const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds')); const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds'));
const oneko = computed(defaultStore.makeGetterSetter('oneko')); const oneko = computed(defaultStore.makeGetterSetter('oneko'));
const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages'));
const disableCatSpeak = computed(defaultStore.makeGetterSetter('disableCatSpeak'));
const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia')); const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia'));
const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab'));
const enableFaviconNotificationDot = computed(defaultStore.makeGetterSetter('enableFaviconNotificationDot')); const enableFaviconNotificationDot = computed(defaultStore.makeGetterSetter('enableFaviconNotificationDot'));

View file

@ -77,6 +77,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
'enableFaviconNotificationDot', 'enableFaviconNotificationDot',
'imageNewTab', 'imageNewTab',
'dataSaver', 'dataSaver',
'disableCatSpeak',
'disableShowingAnimatedImages', 'disableShowingAnimatedImages',
'emojiStyle', 'emojiStyle',
'disableDrawer', 'disableDrawer',

View file

@ -30,7 +30,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</button> </button>
</div> </div>
</div> </div>
<span v-if="$i && $i.id != user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span> <div v-if="$i && $i.id != user.id" class="info-badges">
<span v-if="user.isFollowed">{{ i18n.ts.followsYou }}</span>
<span v-if="user.isMuted">{{ i18n.ts.muted }}</span>
<span v-if="user.isRenoteMuted">{{ i18n.ts.renoteMuted }}</span>
<span v-if="user.isBlocking">{{ i18n.ts.blocked }}</span>
</div>
<div class="actions"> <div class="actions">
<button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button> <button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button>
<MkFollowButton v-if="$i?.id != user.id" v-model:user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/> <MkFollowButton v-if="$i?.id != user.id" v-model:user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
@ -445,15 +450,25 @@ onUnmounted(() => {
background: linear-gradient(transparent, rgba(#000, 0.7)); background: linear-gradient(transparent, rgba(#000, 0.7));
} }
> .followed { > .info-badges {
position: absolute; position: absolute;
top: 12px; top: 12px;
left: 12px; left: 12px;
padding: 4px 8px;
color: #fff; display: flex;
background: rgba(0, 0, 0, 0.7); flex-direction: row;
font-size: 0.7em;
border-radius: var(--radius-sm); > * {
padding: 4px 8px;
color: #fff;
background: rgba(0, 0, 0, 0.7);
font-size: 0.7em;
border-radius: var(--radius-sm);
}
> :not(:first-child) {
margin-left: 8px;
}
} }
> .actions { > .actions {

View file

@ -288,6 +288,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device', where: 'device',
default: window.matchMedia('(prefers-reduced-motion)').matches, default: window.matchMedia('(prefers-reduced-motion)').matches,
}, },
disableCatSpeak: {
where: 'account',
default: false,
},
emojiStyle: { emojiStyle: {
where: 'device', where: 'device',
default: 'twemoji', // twemoji / fluentEmoji / native default: 'twemoji', // twemoji / fluentEmoji / native

View file

@ -3830,6 +3830,7 @@ export type components = {
url: string; url: string;
offsetX?: number; offsetX?: number;
offsetY?: number; offsetY?: number;
showBelow?: boolean;
}[]; }[];
/** @default false */ /** @default false */
isAdmin?: boolean; isAdmin?: boolean;
@ -20243,6 +20244,7 @@ export type operations = {
flipH?: boolean | null; flipH?: boolean | null;
offsetX?: number | null; offsetX?: number | null;
offsetY?: number | null; offsetY?: number | null;
showBelow?: boolean | null;
})[]; })[];
/** Format: misskey:id */ /** Format: misskey:id */
bannerId?: string | null; bannerId?: string | null;