-
+
@@ -74,18 +74,20 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.customEmojis }}
- {{ category || i18n.ts.other }}
+ {{ child.value || i18n.ts.other }}
- {{ category }}
+ {{ category }}
@@ -101,7 +103,14 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, shallowRef, computed, watch, onMounted } from 'vue';
import * as Misskey from 'cherrypick-js';
import XSection from '@/components/MkEmojiPicker.section.vue';
-import { emojilist, emojiCharByCategory, UnicodeEmojiDef, unicodeEmojiCategories as categories, getEmojiName } from '@/scripts/emojilist.js';
+import {
+ emojilist,
+ emojiCharByCategory,
+ UnicodeEmojiDef,
+ unicodeEmojiCategories as categories,
+ getEmojiName,
+ CustomEmojiFolderTree,
+} from '@/scripts/emojilist.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import * as os from '@/os.js';
import { isTouchUsing } from '@/scripts/touch.js';
@@ -145,6 +154,35 @@ const searchResultCustom = ref
([]);
const searchResultUnicode = ref([]);
const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index');
+const customEmojiFolderRoot: CustomEmojiFolderTree = { value: '', category: '', children: [] };
+
+function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): CustomEmojiFolderTree {
+ const parts = input.split('/').map(p => p.trim());
+ let currentNode: CustomEmojiFolderTree = root;
+
+ for (const part of parts) {
+ let existingNode = currentNode.children.find((node) => node.value === part);
+
+ if (!existingNode) {
+ const newNode: CustomEmojiFolderTree = { value: part, category: input, children: [] };
+ currentNode.children.push(newNode);
+ existingNode = newNode;
+ }
+
+ currentNode = existingNode;
+ }
+
+ return currentNode;
+}
+
+customEmojiCategories.value.forEach(ec => {
+ if (ec !== null) {
+ parseAndMergeCategories(ec, customEmojiFolderRoot);
+ }
+});
+
+parseAndMergeCategories('', customEmojiFolderRoot);
+
watch(q, () => {
if (emojisEl.value) emojisEl.value.scrollTop = 0;
@@ -573,8 +611,7 @@ defineExpose({
position: sticky;
top: 0;
left: 0;
- height: 32px;
- line-height: 32px;
+ line-height: 28px;
z-index: 1;
padding: 0 8px;
font-size: 12px;
diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue
index 8230acbc57..7301877731 100644
--- a/packages/frontend/src/components/MkFoldableSection.vue
+++ b/packages/frontend/src/components/MkFoldableSection.vue
@@ -96,6 +96,7 @@ onMounted(() => {
return getParentBg(el.parentElement);
}
}
+
const rawBg = getParentBg(el.value);
const _bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
_bg.setAlpha(0.85);
diff --git a/packages/frontend/src/components/MkGoogle.vue b/packages/frontend/src/components/MkGoogle.vue
index d0e52e9a04..04afbdbab2 100644
--- a/packages/frontend/src/components/MkGoogle.vue
+++ b/packages/frontend/src/components/MkGoogle.vue
@@ -21,7 +21,9 @@ const props = defineProps<{
const query = ref(props.q);
const search = () => {
- window.open(`https://www.google.com/search?q=${query.value}`, '_blank');
+ const sp = new URLSearchParams();
+ sp.append('q', query.value);
+ window.open(`https://www.google.com/search?${sp.toString()}`, '_blank');
};
diff --git a/packages/frontend/src/components/MkInfo.vue b/packages/frontend/src/components/MkInfo.vue
index 6d8ee78a21..0f0c1fa9e2 100644
--- a/packages/frontend/src/components/MkInfo.vue
+++ b/packages/frontend/src/components/MkInfo.vue
@@ -4,47 +4,42 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
-
+
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index d17286724a..cac400ea6f 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -20,6 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:placeholder="placeholder"
:pattern="pattern"
:autocomplete="autocomplete"
+ :autocapitalize="autocapitalize"
:spellcheck="spellcheck"
:step="step"
:list="id"
@@ -58,6 +59,7 @@ const props = defineProps<{
placeholder?: string;
autofocus?: boolean;
autocomplete?: string;
+ autocapitalize?: string;
spellcheck?: boolean;
step?: any;
datalist?: string[];
diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue
index e034274c1b..7e840fb840 100644
--- a/packages/frontend/src/components/MkLaunchPad.vue
+++ b/packages/frontend/src/components/MkLaunchPad.vue
@@ -7,17 +7,17 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
-
+{{ count - 10 }}
+
+{{ count - 10 }}
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 857d4a6b6d..9df15c95e2 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
diff --git a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
new file mode 100644
index 0000000000..d3e3620c39
--- /dev/null
+++ b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
@@ -0,0 +1,135 @@
+
+
+
+
+
{{ i18n.ts._initialTutorial._postNote.description1 }}
+
+
+ {{ i18n.ts.visibility }}
+
+
{{ i18n.ts._initialTutorial._postNote._visibility.description }}
+
{{ i18n.ts._visibility.public }} … {{ i18n.ts._initialTutorial._postNote._visibility.public }}
+
{{ i18n.ts._visibility.home }} … {{ i18n.ts._initialTutorial._postNote._visibility.home }}
+
{{ i18n.ts._visibility.followers }} … {{ i18n.ts._initialTutorial._postNote._visibility.followers }}
+
+
{{ i18n.ts._visibility.specified }} … {{ i18n.ts._initialTutorial._postNote._visibility.direct }}
+
+ {{ i18n.ts._initialTutorial._postNote._visibility.doNotSendConfidencialOnDirect1 }} {{ i18n.ts._initialTutorial._postNote._visibility.doNotSendConfidencialOnDirect2 }}
+
+
+
{{ i18n.ts._visibility.disableFederation }} … {{ i18n.ts._initialTutorial._postNote._visibility.localOnly }}
+
+
+
+ {{ i18n.ts._initialTutorial._postNote._cw.title }}
+
+
{{ i18n.ts._initialTutorial._postNote._cw.description }}
+
+
{{ i18n.ts._initialTutorial._postNote._cw.useCases }}
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
new file mode 100644
index 0000000000..c9f901963a
--- /dev/null
+++ b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
@@ -0,0 +1,145 @@
+
+
+
+
+
{{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.description }}
+
{{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.tryThisFile }}
+
{{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.method }}
+
+
{{ i18n.ts._initialTutorial.wellDone }} {{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.sensitiveSucceeded }}
+
+ {{ i18n.ts.previewNoteText }}
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
new file mode 100644
index 0000000000..fa689ac445
--- /dev/null
+++ b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
{{ i18n.ts._initialTutorial._timeline.description1 }}
+
+
{{ i18n.ts._timelines.home }} … {{ i18n.ts._initialTutorial._timeline.home }}
+
{{ i18n.ts._timelines.local }} … {{ i18n.ts._initialTutorial._timeline.local }}
+
{{ i18n.ts._timelines.social }} … {{ i18n.ts._initialTutorial._timeline.social }}
+
{{ i18n.ts._timelines.global }} … {{ i18n.ts._initialTutorial._timeline.global }}
+
+
+
{{ i18n.ts._initialTutorial._timeline.description2 }}
+
+
+
+
+
+ {{ i18n.ts.help }}
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/components/MkTutorialDialog.vue b/packages/frontend/src/components/MkTutorialDialog.vue
new file mode 100644
index 0000000000..88533deadc
--- /dev/null
+++ b/packages/frontend/src/components/MkTutorialDialog.vue
@@ -0,0 +1,260 @@
+
+
+
+
+ {{ i18n.ts._initialTutorial._note.title }}
+ {{ i18n.ts._initialTutorial._reaction.title }}
+ {{ i18n.ts._initialTutorial._timeline.title }}
+ {{ i18n.ts._initialTutorial._postNote.title }}
+ {{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.title }}
+ {{ i18n.ts._initialTutorial.title }}
+
+
+
+
+
+
+
+
+
+
{{ i18n.ts._initialTutorial._landing.title }}
+
{{ i18n.ts._initialTutorial._landing.description }}
+
{{ i18n.ts._initialTutorial.launchTutorial }}
+
{{ i18n.ts.close }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ i18n.ts.goBack }}
+ {{ i18n.ts.continue }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ i18n.ts._initialTutorial._reaction.reactToContinue }}
+
+
+
+
+ {{ i18n.ts.goBack }}
+ {{ i18n.ts.continue }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ i18n.ts.goBack }}
+ {{ i18n.ts.continue }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ i18n.ts.goBack }}
+ {{ i18n.ts.continue }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.doItToContinue }}
+
+
+
+
+ {{ i18n.ts.goBack }}
+ {{ i18n.ts.continue }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ i18n.ts._initialTutorial._done.title }}
+
+
+ {{ i18n.ts.help }}
+
+
+
{{ i18n.t('_initialAccountSetting.haveFun', { name: instance.name ?? host }) }}
+
+ {{ i18n.ts.goBack }}
+ {{ i18n.ts.close }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue
index 48a5e3f941..55d5032dcc 100644
--- a/packages/frontend/src/components/MkUserInfo.vue
+++ b/packages/frontend/src/components/MkUserInfo.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.followsYou }}
-
+
{{ i18n.ts.noAccountDescription }}
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index a32fc3671a..3bd09a8162 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
{{ i18n.ts.noAccountDescription }}
diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue
index 0f9e60b5a6..58d739122e 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.User.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
{{ i18n.ts.noAccountDescription }}
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
index 1ec0efac51..8da1503fe6 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -49,24 +49,32 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
-
-
-
-
@@ -138,16 +146,13 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._initialAccountSetting.initialAccountSettingCompleted }}
-
- {{ instance.name ?? host }}
-
- {{ i18n.ts.help }}
-
-
-
{{ i18n.t('_initialAccountSetting.haveFun', { name: instance.name ?? host }) }}
+
{{ i18n.t('_initialAccountSetting.youCanContinueTutorial', { name: instance.name ?? host }) }}
+ {{ i18n.ts._initialAccountSetting.startTutorial }}
+
+
{{ i18n.ts.goBack }}
- {{ i18n.ts.close }}
+ {{ i18n.ts.close }}
@@ -159,7 +164,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -256,10 +276,21 @@ async function later(later: boolean) {
box-sizing: border-box;
}
+.pageRoot {
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+}
+
+.pageMain {
+ flex-grow: 1;
+}
+
.pageFooter {
position: sticky;
bottom: 0;
left: 0;
+ flex-shrink: 0;
padding: 12px;
border-top: solid 0.5px var(--divider);
-webkit-backdrop-filter: blur(15px);
diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue
index bf9ed0087f..81cda75da5 100644
--- a/packages/frontend/src/components/MkVisibilityPicker.vue
+++ b/packages/frontend/src/components/MkVisibilityPicker.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.visibility }}
-
+
{{ i18n.ts._visibility.public }}
@@ -52,6 +52,7 @@ const modal = $shallowRef
>();
const props = withDefaults(defineProps<{
currentVisibility: typeof Misskey.noteVisibilities[number];
+ isSilenced: boolean;
localOnly: boolean;
src?: HTMLElement;
}>(), {
diff --git a/packages/frontend/src/components/global/CPAvatar-Friendly.vue b/packages/frontend/src/components/global/CPAvatar-Friendly.vue
index 762fb9c0fb..c62b152474 100644
--- a/packages/frontend/src/components/global/CPAvatar-Friendly.vue
+++ b/packages/frontend/src/components/global/CPAvatar-Friendly.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
+
@@ -35,16 +45,27 @@ const props = withDefaults(defineProps<{
target?: string | null;
link?: boolean;
preview?: boolean;
+ decoration?: {
+ url: string;
+ angle?: number;
+ flipH?: boolean;
+ flipV?: boolean;
+ };
+ forceShowDecoration?: boolean;
}>(), {
target: null,
link: false,
preview: false,
+ decoration: undefined,
+ forceShowDecoration: false,
});
const emit = defineEmits<{
(ev: 'click', v: MouseEvent): void;
}>();
+const showDecoration = props.forceShowDecoration || defaultStore.state.showAvatarDecorations;
+
const bound = $computed(() => props.link
? { to: userPage(props.user), target: props.target }
: {});
@@ -52,7 +73,7 @@ const bound = $computed(() => props.link
let playAnimation = $ref(true);
if (defaultStore.state.showingAnimatedImages === 'interaction') playAnimation = false;
let playAnimationTimer = setTimeout(() => playAnimation = false, 5000);
-const url = $computed(() => defaultStore.state.disableShowingAnimatedImages || (['interaction', 'inactive'].includes(defaultStore.state.showingAnimatedImages) && !playAnimation)
+const url = $computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.enableDataSaverMode) || (['interaction', 'inactive'].includes(defaultStore.state.showingAnimatedImages) && !playAnimation)
? getStaticImageUrl(props.user.avatarUrl)
: props.user.avatarUrl);
@@ -61,6 +82,30 @@ function onClick(ev: MouseEvent): void {
emit('click', ev);
}
+function getDecorationAngle() {
+ let angle;
+ if (props.decoration) {
+ angle = props.decoration.angle ?? 0;
+ } else if (props.user.avatarDecorations.length > 0) {
+ angle = props.user.avatarDecorations[0].angle ?? 0;
+ } else {
+ angle = 0;
+ }
+ return angle === 0 ? undefined : `${angle * 360}deg`;
+}
+
+function getDecorationScale() {
+ let scaleX;
+ if (props.decoration) {
+ scaleX = props.decoration.flipH ? -1 : 1;
+ } else if (props.user.avatarDecorations.length > 0) {
+ scaleX = props.user.avatarDecorations[0].flipH ? -1 : 1;
+ } else {
+ scaleX = 1;
+ }
+ return scaleX === 1 ? undefined : `${scaleX} 1`;
+}
+
function resetTimer() {
playAnimation = true;
clearTimeout(playAnimationTimer);
@@ -124,4 +169,13 @@ onUnmounted(() => {
-webkit-user-drag: none;
}
}
+
+.decoration {
+ position: absolute;
+ z-index: 1;
+ top: -50%;
+ left: -50%;
+ width: 200%;
+ pointer-events: none;
+}
diff --git a/packages/frontend/src/components/global/CPPageHeader.vue b/packages/frontend/src/components/global/CPPageHeader.vue
index b7539d56ee..0c0937c041 100644
--- a/packages/frontend/src/components/global/CPPageHeader.vue
+++ b/packages/frontend/src/components/global/CPPageHeader.vue
@@ -13,6 +13,10 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+
@@ -42,15 +46,14 @@ SPDX-License-Identifier: AGPL-3.0-only
-
@@ -103,7 +106,7 @@ const emit = defineEmits<{
const metadata = injectPageMetadata();
-const hideTitle = inject('shouldOmitHeaderTitle', false);
+const hideTitle = false;
const thin_ = props.thin || inject('shouldHeaderThin', false);
let el = $shallowRef(undefined);
diff --git a/packages/frontend/src/components/global/MkAcct.vue b/packages/frontend/src/components/global/MkAcct.vue
index 54eaa36f7f..782e2bda78 100644
--- a/packages/frontend/src/components/global/MkAcct.vue
+++ b/packages/frontend/src/components/global/MkAcct.vue
@@ -17,7 +17,6 @@ SPDX-License-Identifier: AGPL-3.0-only
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index 7530fe9fbc..90832f7b8d 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -18,7 +18,7 @@ import MkSparkle from '@/components/MkSparkle.vue';
import MkA from '@/components/global/MkA.vue';
import { host } from '@/config.js';
import { defaultStore } from '@/store.js';
-import { nyaize } from '@/scripts/nyaize.js';
+import { nyaize as doNyaize } from '@/scripts/nyaize.js';
const QUOTE_STYLE = `
display: block;
@@ -29,21 +29,29 @@ border-left: solid 3px var(--fg);
opacity: 0.7;
`.split('\n').join(' ');
-export default function(props: {
+type MfmProps = {
text: string;
plain?: boolean;
nowrap?: boolean;
author?: Misskey.entities.UserLite;
- i?: Misskey.entities.UserLite;
isNote?: boolean;
emojiUrls?: string[];
rootScale?: number;
-}) {
- const isNote = props.isNote !== undefined ? props.isNote : true;
+ nyaize: boolean | 'account';
+ parsedNodes?: mfm.MfmNode[] | null;
+ enableEmojiMenu?: boolean;
+ enableEmojiMenuReaction?: boolean;
+};
+// eslint-disable-next-line import/no-default-export
+export default function(props: MfmProps) {
+ const isNote = props.isNote ?? true;
+ const shouldNyaize = props.nyaize ? props.nyaize === 'account' ? props.author?.isCat : false : false;
+
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (props.text == null || props.text === '') return;
- const ast = (props.plain ? mfm.parseSimple : mfm.parse)(props.text);
+ const rootAst = props.parsedNodes ?? (props.plain ? mfm.parseSimple : mfm.parse)(props.text);
const validTime = (t: string | null | undefined) => {
if (t == null) return null;
@@ -56,13 +64,14 @@ export default function(props: {
* Gen Vue Elements from MFM AST
* @param ast MFM AST
* @param scale How times large the text is
+ * @param disableNyaize Whether nyaize is disabled or not
*/
const genEl = (ast: mfm.MfmNode[], scale: number, disableNyaize = false) => ast.map((token): VNode | string | (VNode | string)[] => {
switch (token.type) {
case 'text': {
let text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
- if (!disableNyaize && props.author?.isCat) {
- text = nyaize(text);
+ if (!disableNyaize && shouldNyaize) {
+ text = doNyaize(text);
}
if (!props.plain) {
@@ -345,6 +354,8 @@ export default function(props: {
normal: props.plain,
host: null,
useOriginalSize: scale >= 2.5,
+ menu: props.enableEmojiMenu,
+ menuReaction: props.enableEmojiMenuReaction,
})];
} else {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -368,6 +379,8 @@ export default function(props: {
return [h(MkEmoji, {
key: Math.random(),
emoji: token.props.emoji,
+ menu: props.enableEmojiMenu,
+ menuReaction: props.enableEmojiMenuReaction,
})];
}
@@ -406,5 +419,5 @@ export default function(props: {
return h('span', {
// https://codeday.me/jp/qa/20190424/690106.html
style: props.nowrap ? 'white-space: pre; word-wrap: normal; overflow: hidden; text-overflow: ellipsis;' : 'white-space: pre-wrap;',
- }, genEl(ast, props.rootScale ?? 1));
+ }, genEl(rootAst, props.rootScale ?? 1));
}
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
index 0946c29bfa..1bf322009f 100644
--- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
@@ -134,9 +134,11 @@ async function enter(el: HTMLElement) {
setTimeout(renderTab, 170);
}
+
function afterEnter(el: HTMLElement) {
//el.style.width = '';
}
+
async function leave(el: HTMLElement) {
const elementWidth = el.getBoundingClientRect().width;
el.style.width = elementWidth + 'px';
@@ -145,6 +147,7 @@ async function leave(el: HTMLElement) {
el.style.width = '0';
el.style.paddingLeft = '0';
}
+
function afterLeave(el: HTMLElement) {
el.style.width = '';
}
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index b7dcb84658..1fd80f81a7 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -13,7 +13,19 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
+
+
+
@@ -33,15 +45,14 @@ SPDX-License-Identifier: AGPL-3.0-only
emit('update:tab', key)" @tabClick="onTabClick"/>
-
@@ -51,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index 89c781e050..796cfb0f8c 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -24,6 +24,10 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.sensitive }}
+
+ {{ i18n.ts._channel.allowRenoteToExternal }}
+
+
{{ i18n.ts._channel.setBanner }}
@@ -93,6 +97,7 @@ let bannerUrl = $ref
(null);
let bannerId = $ref(null);
let color = $ref('#000');
let isSensitive = $ref(false);
+let allowRenoteToExternal = $ref(true);
const pinnedNotes = ref([]);
watch(() => bannerId, async () => {
@@ -121,6 +126,7 @@ async function fetchChannel() {
id,
}));
color = channel.color;
+ allowRenoteToExternal = channel.allowRenoteToExternal;
}
fetchChannel();
@@ -150,16 +156,14 @@ function save() {
pinnedNoteIds: pinnedNotes.value.map(x => x.id),
color: color,
isSensitive: isSensitive,
+ allowRenoteToExternal: allowRenoteToExternal,
};
if (props.channelId) {
params.channelId = props.channelId;
- os.api('channels/update', params).then(() => {
- os.success();
- });
+ os.apiWithDialog('channels/update', params);
} else {
- os.api('channels/create', params).then(created => {
- os.success();
+ os.apiWithDialog('channels/create', params).then(created => {
router.push(`/channels/${created.id}`);
});
}
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index 2d8ef02217..62fe69f380 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 2944a4f3b4..a3533e5ff2 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
{{ clip.favoritedCount }}
{{ clip.favoritedCount }}
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index e768b59349..4e8a1d563c 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
{{ i18n.ts.search }}
@@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
{{ i18n.ts.search }}
diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue
index ac8527e43c..053bf0b2a1 100644
--- a/packages/frontend/src/pages/drive.file.info.vue
+++ b/packages/frontend/src/pages/drive.file.info.vue
@@ -56,6 +56,10 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._fileViewer.size }}
{{ bytes(file.size) }}
+
+ URL
+ {{ file.url }}
+
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index c83f99313d..e82ba48a6d 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -31,13 +31,13 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.selectFile }}
-
+
{{ i18n.ts.name }}
{{ i18n.ts.category }}
-
+
{{ i18n.ts.tags }}
{{ i18n.ts.setMultipleBySeparatingWithSpace }}
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 53e129a3ed..c9601ec085 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._play.viewSource }}
-
+
diff --git a/packages/frontend/src/pages/install-extentions.vue b/packages/frontend/src/pages/install-extentions.vue
index 0a661be50b..35dab47ca6 100644
--- a/packages/frontend/src/pages/install-extentions.vue
+++ b/packages/frontend/src/pages/install-extentions.vue
@@ -95,7 +95,7 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index f0c73cc357..a8268c5c73 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
{{ i18n.ts._profile.changeAvatar }}
{{ i18n.ts._profile.changeBanner }}
@@ -83,6 +83,24 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._profile.metadataDescription }}
+
+
+ {{ i18n.ts.avatarDecorations }}
+
+
+
+
{{ avatarDecoration.name }}
+
+
+
+
+
+
{{ i18n.ts.advancedSettings }}
@@ -121,11 +139,13 @@ import { langmap } from '@/scripts/langmap.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { defaultStore } from '@/store.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
import MkInfo from '@/components/MkInfo.vue';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
const reactionAcceptance = computed(defaultStore.makeGetterSetter('reactionAcceptance'));
+let avatarDecorations: any[] = $ref([]);
const profile = reactive({
name: $i.name,
@@ -146,6 +166,10 @@ watch(() => profile, () => {
const fields = ref($i?.fields.map(field => ({ id: Math.random().toString(), name: field.name, value: field.value })) ?? []);
const fieldEditMode = ref(false);
+os.api('get-avatar-decorations').then(_avatarDecorations => {
+ avatarDecorations = _avatarDecorations;
+});
+
function addField() {
fields.value.push({
id: Math.random().toString(),
@@ -188,8 +212,16 @@ function save() {
if (profile.name === 'syuilo' || profile.name === 'しゅいろ') {
claimAchievement('setNameToSyuilo');
}
- if (profile.isCat) {
+ if (profile.isCat && defaultStore.state.renameTheButtonInPostFormToNya) {
claimAchievement('markedAsCat');
+ } else if (profile.isCat && !defaultStore.state.renameTheButtonInPostFormToNya) {
+ claimAchievement('markedAsCat');
+ defaultStore.set('renameTheButtonInPostFormToNya', true);
+ defaultStore.set('renameTheButtonInPostFormToNyaManualSet', false);
+ reloadAsk();
+ } else if (!profile.isCat && !defaultStore.state.renameTheButtonInPostFormToNyaManualSet) {
+ defaultStore.set('renameTheButtonInPostFormToNya', false);
+ reloadAsk();
}
}
@@ -244,6 +276,22 @@ function changeBanner(ev) {
});
}
+function openDecoration(avatarDecoration) {
+ os.popup(defineAsyncComponent(() => import('./profile.avatar-decoration-dialog.vue')), {
+ decoration: avatarDecoration,
+ }, {}, 'closed');
+}
+
+async function reloadAsk() {
+ const { canceled } = await os.confirm({
+ type: 'info',
+ text: i18n.ts.reloadToApplySetting,
+ });
+ if (canceled) return;
+
+ unisonReload();
+}
+
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
@@ -338,4 +386,33 @@ definePageMetadata({
.dragItemForm {
flex-grow: 1;
}
+
+.avatarDecoration {
+ cursor: pointer;
+ padding: 16px 16px 28px 16px;
+ border: solid 2px var(--divider);
+ border-radius: 8px;
+ text-align: center;
+ font-size: 90%;
+ overflow: clip;
+ contain: content;
+}
+
+.avatarDecorationActive {
+ background-color: var(--accentedBg);
+ border-color: var(--accent);
+}
+
+.avatarDecorationName {
+ position: relative;
+ z-index: 10;
+ font-weight: bold;
+ margin-bottom: 20px;
+}
+
+.avatarDecorationLock {
+ position: absolute;
+ bottom: 12px;
+ right: 12px;
+}
diff --git a/packages/frontend/src/pages/settings/sounds-and-vibrations.vue b/packages/frontend/src/pages/settings/sounds-and-vibrations.vue
index c64740aea9..f25486abd8 100644
--- a/packages/frontend/src/pages/settings/sounds-and-vibrations.vue
+++ b/packages/frontend/src/pages/settings/sounds-and-vibrations.vue
@@ -46,25 +46,24 @@ import MkButton from '@/components/MkButton.vue';
import FormSection from '@/components/form/section.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
-import { soundConfigStore } from '@/scripts/sound.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { ColdDeviceStorage } from '@/store.js';
+import { ColdDeviceStorage, defaultStore } from '@/store.js';
import { unisonReload } from '@/scripts/unison-reload.js';
-const masterVolume = computed(soundConfigStore.makeGetterSetter('sound_masterVolume'));
+const masterVolume = computed(defaultStore.makeGetterSetter('sound_masterVolume'));
const soundsKeys = ['note', 'noteMy', 'noteEdited', 'notification', 'chat', 'chatBg', 'antenna', 'channel'] as const;
const sounds = ref>>({
- note: soundConfigStore.reactiveState.sound_note,
- noteMy: soundConfigStore.reactiveState.sound_noteMy,
- noteEdited: soundConfigStore.reactiveState.sound_noteEdited,
- notification: soundConfigStore.reactiveState.sound_notification,
- chat: soundConfigStore.reactiveState.sound_chat,
- chatBg: soundConfigStore.reactiveState.sound_chatBg,
- antenna: soundConfigStore.reactiveState.sound_antenna,
- channel: soundConfigStore.reactiveState.sound_channel,
+ note: defaultStore.reactiveState.sound_note,
+ noteMy: defaultStore.reactiveState.sound_noteMy,
+ noteEdited: defaultStore.reactiveState.sound_noteEdited,
+ notification: defaultStore.reactiveState.sound_notification,
+ chat: defaultStore.reactiveState.sound_chat,
+ chatBg: defaultStore.reactiveState.sound_chatBg,
+ antenna: defaultStore.reactiveState.sound_antenna,
+ channel: defaultStore.reactiveState.sound_channel,
});
const vibrate = computed(ColdDeviceStorage.makeGetterSetter('vibrate'));
@@ -90,14 +89,14 @@ async function updated(type: keyof typeof sounds.value, sound) {
volume: sound.volume,
};
- soundConfigStore.set(`sound_${type}`, v);
+ defaultStore.set(`sound_${type}`, v);
sounds.value[type] = v;
}
function reset() {
for (const sound of Object.keys(sounds.value) as Array) {
- const v = soundConfigStore.def[`sound_${sound}`].default;
- soundConfigStore.set(`sound_${sound}`, v);
+ const v = defaultStore.def[`sound_${sound}`].default;
+ defaultStore.set(`sound_${sound}`, v);
sounds.value[sound] = v;
}
}
diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue
index 05117896c5..0ee2865e72 100644
--- a/packages/frontend/src/pages/settings/webhook.edit.vue
+++ b/packages/frontend/src/pages/settings/webhook.edit.vue
@@ -108,6 +108,7 @@ async function del(): Promise {
router.push('/settings/webhook');
}
+
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/pages/signup-complete.vue b/packages/frontend/src/pages/signup-complete.vue
index 86c000c8b5..7e85ac2673 100644
--- a/packages/frontend/src/pages/signup-complete.vue
+++ b/packages/frontend/src/pages/signup-complete.vue
@@ -51,7 +51,8 @@ function submit() {
os.alert({
type: 'error',
- text: i18n.ts.somethingHappened,
+ title: i18n.ts.somethingHappened,
+ text: i18n.ts.signupPendingError,
});
});
}
diff --git a/packages/frontend/src/pages/timeline.tutorial.vue b/packages/frontend/src/pages/timeline.tutorial.vue
deleted file mode 100644
index d17ad9cd38..0000000000
--- a/packages/frontend/src/pages/timeline.tutorial.vue
+++ /dev/null
@@ -1,123 +0,0 @@
-
-
-
-
-
-
{{ i18n.ts._timelineTutorial.title }}
-
-
-
-
- {{ tutorial + 1 }} / {{ tutorialsNumber }}
-
-
-
-
-
-
-
-
{{ i18n.t('_timelineTutorial.step1_1', { name: instance.name ?? host }) }}
-
{{ i18n.t('_timelineTutorial.step1_2', { name: instance.name ?? host }) }}
-
-
-
{{ i18n.ts._timelineTutorial.step2_1 }}
-
{{ i18n.t('_timelineTutorial.step2_2', { name: instance.name ?? host }) }}
-
-
-
{{ i18n.ts._timelineTutorial.step3_1 }}
-
{{ i18n.ts._timelineTutorial.step3_2 }}
-
-
-
{{ i18n.ts._timelineTutorial.step4_1 }}
-
{{ i18n.ts._timelineTutorial.step4_2 }}
-
-
-
-
- {{ i18n.ts.done }}
-
-
- {{ i18n.ts.next }}
-
-
-
-
-
-
-
-
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 18320e4575..bf394640f7 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -11,7 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
+ {{ i18n.ts._timelineDescription[src] }}
+
diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue
index 4943432cff..d94709d1f6 100644
--- a/packages/frontend/src/ui/_common_/stream-indicator.vue
+++ b/packages/frontend/src/ui/_common_/stream-indicator.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only