mirror of
https://codeberg.org/yeentown/barkey.git
synced 2025-07-07 04:26:58 +00:00
copy sharkey settings into new frontend preferences model
This commit is contained in:
parent
59ce4d6c28
commit
c371af34e8
50 changed files with 468 additions and 425 deletions
|
@ -17,7 +17,7 @@ import EmNoteDetailed from '@/components/EmNoteDetailed.vue';
|
||||||
import XNotFound from '@/pages/not-found.vue';
|
import XNotFound from '@/pages/not-found.vue';
|
||||||
import { DI } from '@/di.js';
|
import { DI } from '@/di.js';
|
||||||
import { misskeyApi } from '@/misskey-api.js';
|
import { misskeyApi } from '@/misskey-api.js';
|
||||||
import { assertServerContext } from '@/server-context';
|
import { assertServerContext } from '@/server-context.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
noteId: string;
|
noteId: string;
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { deckStore } from '@/ui/deck/deck-store.js';
|
||||||
import { emojiPicker } from '@/utility/emoji-picker.js';
|
import { emojiPicker } from '@/utility/emoji-picker.js';
|
||||||
import { mainRouter } from '@/router.js';
|
import { mainRouter } from '@/router.js';
|
||||||
import { setFavIconDot } from '@/utility/favicon-dot.js';
|
import { setFavIconDot } from '@/utility/favicon-dot.js';
|
||||||
import { type Keymap, makeHotkey } from '@/utility/hotkey.js';
|
import { makeHotkey } from '@/utility/hotkey.js';
|
||||||
import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js';
|
import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
import { launchPlugins } from '@/plugin.js';
|
import { launchPlugins } from '@/plugin.js';
|
||||||
|
@ -85,9 +85,7 @@ export async function mainBoot() {
|
||||||
|
|
||||||
let reloadDialogShowing = false;
|
let reloadDialogShowing = false;
|
||||||
stream.on('_disconnected_', async () => {
|
stream.on('_disconnected_', async () => {
|
||||||
if (prefer.s.serverDisconnectedBehavior === 'reload') {
|
if (prefer.s.serverDisconnectedBehavior === 'dialog') {
|
||||||
window.location.reload();
|
|
||||||
} else if (prefer.s.serverDisconnectedBehavior === 'dialog') {
|
|
||||||
if (reloadDialogShowing) return;
|
if (reloadDialogShowing) return;
|
||||||
reloadDialogShowing = true;
|
reloadDialogShowing = true;
|
||||||
const { canceled } = await confirm({
|
const { canceled } = await confirm({
|
||||||
|
@ -351,7 +349,7 @@ export async function mainBoot() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function attemptShowNotificationDot() {
|
function attemptShowNotificationDot() {
|
||||||
if (defaultStore.state.enableFaviconNotificationDot) {
|
if (store.s.enableFaviconNotificationDot) {
|
||||||
setFavIconDot(true);
|
setFavIconDot(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { computed, defineAsyncComponent, shallowRef } from 'vue';
|
||||||
import type { ComponentExposed } from 'vue-component-type-helpers';
|
import type { ComponentExposed } from 'vue-component-type-helpers';
|
||||||
import type MkNote from '@/components/MkNote.vue';
|
import type MkNote from '@/components/MkNote.vue';
|
||||||
import type SkNote from '@/components/SkNote.vue';
|
import type SkNote from '@/components/SkNote.vue';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
const XNote = computed(() =>
|
const XNote = computed(() =>
|
||||||
defineAsyncComponent(() =>
|
defineAsyncComponent(() =>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/i.js';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -116,7 +116,7 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderChildren = () => {
|
const renderChildren = () => {
|
||||||
const shouldHideAds = !defaultStore.state.forceShowAds && $i && $i.policies.canHideAds;
|
const shouldHideAds = (!prefer.s.forceShowAds && $i && $i.policies.canHideAds) ?? false;
|
||||||
|
|
||||||
const children = renderChildrenImpl(shouldHideAds);
|
const children = renderChildrenImpl(shouldHideAds);
|
||||||
if (isDebuggerEnabled(6864)) {
|
if (isDebuggerEnabled(6864)) {
|
||||||
|
|
|
@ -13,7 +13,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 { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { defaultStore } from '@/store';
|
import { prefer } from '@/preferences.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
q: string;
|
q: string;
|
||||||
|
@ -23,7 +23,7 @@ const query = ref(props.q);
|
||||||
|
|
||||||
const search = () => {
|
const search = () => {
|
||||||
const searchQuery = encodeURIComponent(query.value);
|
const searchQuery = encodeURIComponent(query.value);
|
||||||
const searchUrl = defaultStore.state.searchEngine.replace(/{query}|%s\b/g, searchQuery);
|
const searchUrl = prefer.s.searchEngine.replace(/{query}|%s\b/g, searchQuery);
|
||||||
|
|
||||||
window.open(searchUrl, '_blank', 'noopener');
|
window.open(searchUrl, '_blank', 'noopener');
|
||||||
};
|
};
|
||||||
|
|
|
@ -95,7 +95,7 @@ import { useTemplateRef, watch, computed, ref, onDeactivated, onActivated, onMou
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import type { Keymap } from '@/utility/hotkey.js';
|
import type { Keymap } from '@/utility/hotkey.js';
|
||||||
import { copyToClipboard } from '@/utility/copy-to-clipboard';
|
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import bytes from '@/filters/bytes.js';
|
import bytes from '@/filters/bytes.js';
|
||||||
|
|
|
@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { watch, ref, computed } from 'vue';
|
import { watch, ref, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import { copyToClipboard } from '@/utility/copy-to-clipboard';
|
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
|
||||||
import { getStaticImageUrl } from '@/utility/media-proxy.js';
|
import { getStaticImageUrl } from '@/utility/media-proxy.js';
|
||||||
import bytes from '@/filters/bytes.js';
|
import bytes from '@/filters/bytes.js';
|
||||||
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
||||||
|
|
|
@ -116,7 +116,7 @@ import { ref, useTemplateRef, computed, watch, onDeactivated, onActivated, onMou
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import type { Keymap } from '@/utility/hotkey.js';
|
import type { Keymap } from '@/utility/hotkey.js';
|
||||||
import { copyToClipboard } from '@/utility/copy-to-clipboard';
|
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
|
||||||
import bytes from '@/filters/bytes.js';
|
import bytes from '@/filters/bytes.js';
|
||||||
import { hms } from '@/filters/hms.js';
|
import { hms } from '@/filters/hms.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
|
@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
|
<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
|
||||||
<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
|
<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
|
||||||
<MkAvatar :class="[$style.avatar, prefer.s.useStickyIcons ? $style.useSticky : null]" :user="appearNote.user" :link="!mock" :preview="!mock"/>
|
<MkAvatar :class="[$style.avatar, prefer.s.useStickyIcons ? $style.useSticky : null]" :user="appearNote.user" :link="!mock" :preview="!mock"/>
|
||||||
<div :class="[$style.main, { [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click.stop="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
|
<div :class="[$style.main, { [$style.clickToOpen]: prefer.s.clickToOpen }]" @click.stop="prefer.s.clickToOpen ? noteclick(appearNote.id) : undefined">
|
||||||
<MkNoteHeader :note="appearNote" :mini="true" @click.stop/>
|
<MkNoteHeader :note="appearNote" :mini="true" @click.stop/>
|
||||||
<MkInstanceTicker v-if="showTicker" :host="appearNote.user.host" :instance="appearNote.user.instance"/>
|
<MkInstanceTicker v-if="showTicker" :host="appearNote.user.host" :instance="appearNote.user.instance"/>
|
||||||
<div style="container-type: inline-size;">
|
<div style="container-type: inline-size;">
|
||||||
|
@ -94,7 +94,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
||||||
<MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
<MkButton v-else-if="!prefer.s.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="appearNote.files && appearNote.files.length > 0">
|
<div v-if="appearNote.files && appearNote.files.length > 0">
|
||||||
<MkMediaList ref="galleryEl" :mediaList="appearNote.files" @click.stop/>
|
<MkMediaList ref="galleryEl" :mediaList="appearNote.files" @click.stop/>
|
||||||
|
@ -211,11 +211,12 @@ import * as Misskey from 'misskey-js';
|
||||||
import { isLink } from '@@/js/is-link.js';
|
import { isLink } from '@@/js/is-link.js';
|
||||||
import { shouldCollapsed } from '@@/js/collapsed.js';
|
import { shouldCollapsed } from '@@/js/collapsed.js';
|
||||||
import { host } from '@@/js/config.js';
|
import { host } from '@@/js/config.js';
|
||||||
import type { Ref } from 'vue';
|
|
||||||
import { computeMergedCw } from '@@/js/compute-merged-cw.js';
|
import { computeMergedCw } from '@@/js/compute-merged-cw.js';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
|
import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
|
||||||
import type { Keymap } from '@/utility/hotkey.js';
|
import type { Keymap } from '@/utility/hotkey.js';
|
||||||
|
import type { Visibility } from '@/utility/boost-quote.js';
|
||||||
import MkNoteSub from '@/components/MkNoteSub.vue';
|
import MkNoteSub from '@/components/MkNoteSub.vue';
|
||||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||||
|
@ -250,14 +251,14 @@ import { claimAchievement } from '@/utility/achievements.js';
|
||||||
import { getNoteSummary } from '@/utility/get-note-summary.js';
|
import { getNoteSummary } from '@/utility/get-note-summary.js';
|
||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
||||||
import { useRouter } from '@/router/supplier.js';
|
import { boostMenuItems, computeRenoteTooltip } from '@/utility/boost-quote.js';
|
||||||
import { boostMenuItems, type Visibility, computeRenoteTooltip } from '@/utility/boost-quote.js';
|
|
||||||
import { isEnabledUrlPreview } from '@/instance.js';
|
import { isEnabledUrlPreview } from '@/instance.js';
|
||||||
import { focusPrev, focusNext } from '@/utility/focus.js';
|
import { focusPrev, focusNext } from '@/utility/focus.js';
|
||||||
import { getAppearNote } from '@/utility/get-appear-note.js';
|
import { getAppearNote } from '@/utility/get-appear-note.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
import { getPluginHandlers } from '@/plugin.js';
|
import { getPluginHandlers } from '@/plugin.js';
|
||||||
import { DI } from '@/di.js';
|
import { DI } from '@/di.js';
|
||||||
|
import { useRouter } from '@/router.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -285,7 +286,7 @@ const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', nul
|
||||||
const note = ref(deepClone(props.note));
|
const note = ref(deepClone(props.note));
|
||||||
|
|
||||||
function noteclick(id: string) {
|
function noteclick(id: string) {
|
||||||
const selection = document.getSelection();
|
const selection = window.document.getSelection();
|
||||||
if (selection?.toString().length === 0) {
|
if (selection?.toString().length === 0) {
|
||||||
router.push(`/notes/${id}`);
|
router.push(`/notes/${id}`);
|
||||||
}
|
}
|
||||||
|
@ -325,11 +326,11 @@ const likeButton = useTemplateRef('likeButton');
|
||||||
const appearNote = computed(() => getAppearNote(note.value));
|
const appearNote = computed(() => getAppearNote(note.value));
|
||||||
const galleryEl = useTemplateRef('galleryEl');
|
const galleryEl = useTemplateRef('galleryEl');
|
||||||
const isMyRenote = $i && ($i.id === note.value.userId);
|
const isMyRenote = $i && ($i.id === note.value.userId);
|
||||||
const showContent = ref(defaultStore.state.uncollapseCW);
|
const showContent = ref(prefer.s.uncollapseCW);
|
||||||
const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
|
const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
|
||||||
const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null);
|
const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null);
|
||||||
const isLong = shouldCollapsed(appearNote.value, urls.value ?? []);
|
const isLong = shouldCollapsed(appearNote.value, urls.value ?? []);
|
||||||
const collapsed = ref(defaultStore.state.expandLongNote && appearNote.value.cw == null && isLong ? false : appearNote.value.cw == null && isLong);
|
const collapsed = ref(prefer.s.expandLongNote && appearNote.value.cw == null && isLong ? false : appearNote.value.cw == null && isLong);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const renoted = ref(false);
|
const renoted = ref(false);
|
||||||
const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
|
const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
|
||||||
|
@ -345,10 +346,10 @@ const renoteCollapsed = ref(
|
||||||
(appearNote.value.myReaction != null)
|
(appearNote.value.myReaction != null)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const inReplyToCollapsed = ref(defaultStore.state.collapseNotesRepliedTo);
|
const inReplyToCollapsed = ref(prefer.s.collapseNotesRepliedTo);
|
||||||
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
const defaultLike = computed(() => prefer.s.like ? prefer.s.like : null);
|
||||||
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
||||||
const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
|
const allowAnim = ref(prefer.s.advancedMfm && prefer.s.animatedMfm);
|
||||||
|
|
||||||
const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
|
const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
|
||||||
type: 'lookup',
|
type: 'lookup',
|
||||||
|
@ -397,7 +398,7 @@ const keymap = {
|
||||||
},
|
},
|
||||||
'q': () => {
|
'q': () => {
|
||||||
if (renoteCollapsed.value) return;
|
if (renoteCollapsed.value) return;
|
||||||
if (canRenote.value && !renoted.value && !renoting) renote(defaultStore.state.visibilityOnBoost);
|
if (canRenote.value && !renoted.value && !renoting) renote(prefer.s.visibilityOnBoost);
|
||||||
},
|
},
|
||||||
'm': () => {
|
'm': () => {
|
||||||
if (renoteCollapsed.value) return;
|
if (renoteCollapsed.value) return;
|
||||||
|
@ -458,6 +459,8 @@ if (props.mock) {
|
||||||
|
|
||||||
if (!props.mock) {
|
if (!props.mock) {
|
||||||
useTooltip(renoteButton, async (showing) => {
|
useTooltip(renoteButton, async (showing) => {
|
||||||
|
if (!renoteButton.value) return;
|
||||||
|
|
||||||
const renotes = await misskeyApi('notes/renotes', {
|
const renotes = await misskeyApi('notes/renotes', {
|
||||||
noteId: appearNote.value.id,
|
noteId: appearNote.value.id,
|
||||||
limit: 11,
|
limit: 11,
|
||||||
|
@ -478,6 +481,8 @@ if (!props.mock) {
|
||||||
});
|
});
|
||||||
|
|
||||||
useTooltip(quoteButton, async (showing) => {
|
useTooltip(quoteButton, async (showing) => {
|
||||||
|
if (!quoteButton.value) return;
|
||||||
|
|
||||||
const renotes = await misskeyApi('notes/renotes', {
|
const renotes = await misskeyApi('notes/renotes', {
|
||||||
noteId: appearNote.value.id,
|
noteId: appearNote.value.id,
|
||||||
limit: 11,
|
limit: 11,
|
||||||
|
@ -536,8 +541,8 @@ if (!props.mock) {
|
||||||
function boostVisibility(forceMenu: boolean = false) {
|
function boostVisibility(forceMenu: boolean = false) {
|
||||||
if (renoting) return;
|
if (renoting) return;
|
||||||
|
|
||||||
if (!defaultStore.state.showVisibilitySelectorOnBoost && !forceMenu) {
|
if (!prefer.s.showVisibilitySelectorOnBoost && !forceMenu) {
|
||||||
renote(defaultStore.state.visibilityOnBoost);
|
renote(prefer.s.visibilityOnBoost);
|
||||||
} else {
|
} else {
|
||||||
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
||||||
}
|
}
|
||||||
|
@ -799,8 +804,8 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
react();
|
react();
|
||||||
} else {
|
} else {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
|
const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
|
||||||
os.contextMenu(menu, ev).then(focus).finally(cleanup);
|
os.contextMenu(popupMenu, ev).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -809,15 +814,13 @@ function showMenu(): void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
|
const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
|
||||||
os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup);
|
os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function menuVersions(viaKeyboard = false): Promise<void> {
|
async function menuVersions(): Promise<void> {
|
||||||
const { menu, cleanup } = await getNoteVersionsMenu({ note: note.value, menuVersionsButton });
|
const { menu, cleanup } = await getNoteVersionsMenu({ note: note.value, menuButton: menuVersionsButton });
|
||||||
os.popupMenu(menu, menuVersionsButton.value, {
|
os.popupMenu(menu, menuVersionsButton.value).then(focus).finally(cleanup);
|
||||||
viaKeyboard,
|
|
||||||
}).then(focus).finally(cleanup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clip(): Promise<void> {
|
async function clip(): Promise<void> {
|
||||||
|
|
|
@ -112,7 +112,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
||||||
<MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
<MkButton v-else-if="!prefer.s.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
||||||
<div v-if="appearNote.files && appearNote.files.length > 0">
|
<div v-if="appearNote.files && appearNote.files.length > 0">
|
||||||
<MkMediaList ref="galleryEl" :mediaList="appearNote.files"/>
|
<MkMediaList ref="galleryEl" :mediaList="appearNote.files"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -241,7 +241,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject, onMounted, provide, ref, useTemplateRef } from 'vue';
|
import { computed, inject, onMounted, provide, ref, useTemplateRef, watch } from 'vue';
|
||||||
import * as mfm from '@transfem-org/sfm-js';
|
import * as mfm from '@transfem-org/sfm-js';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { isLink } from '@@/js/is-link.js';
|
import { isLink } from '@@/js/is-link.js';
|
||||||
|
@ -285,7 +285,8 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination 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';
|
||||||
import { boostMenuItems, type Visibility, computeRenoteTooltip } from '@/utility/boost-quote.js';
|
import { boostMenuItems, computeRenoteTooltip } from '@/utility/boost-quote.js';
|
||||||
|
import type { Visibility } from '@/utility/boost-quote.js';
|
||||||
import { isEnabledUrlPreview } from '@/instance.js';
|
import { isEnabledUrlPreview } from '@/instance.js';
|
||||||
import { getAppearNote } from '@/utility/get-appear-note.js';
|
import { getAppearNote } from '@/utility/get-appear-note.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
|
@ -338,7 +339,7 @@ const likeButton = useTemplateRef('likeButton');
|
||||||
const appearNote = computed(() => getAppearNote(note.value));
|
const appearNote = computed(() => getAppearNote(note.value));
|
||||||
const galleryEl = useTemplateRef('galleryEl');
|
const galleryEl = useTemplateRef('galleryEl');
|
||||||
const isMyRenote = $i && ($i.id === note.value.userId);
|
const isMyRenote = $i && ($i.id === note.value.userId);
|
||||||
const showContent = ref(defaultStore.state.uncollapseCW);
|
const showContent = ref(prefer.s.uncollapseCW);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const renoted = ref(false);
|
const renoted = ref(false);
|
||||||
const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false);
|
const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false);
|
||||||
|
@ -353,7 +354,7 @@ const conversation = ref<Misskey.entities.Note[]>([]);
|
||||||
const replies = ref<Misskey.entities.Note[]>([]);
|
const replies = ref<Misskey.entities.Note[]>([]);
|
||||||
const quotes = ref<Misskey.entities.Note[]>([]);
|
const quotes = ref<Misskey.entities.Note[]>([]);
|
||||||
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));
|
||||||
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
const defaultLike = computed(() => prefer.s.like ? prefer.s.like : null);
|
||||||
|
|
||||||
const mergedCW = computed(() => computeMergedCw(appearNote.value));
|
const mergedCW = computed(() => computeMergedCw(appearNote.value));
|
||||||
|
|
||||||
|
@ -383,7 +384,7 @@ const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
|
||||||
const keymap = {
|
const keymap = {
|
||||||
'r': () => reply(),
|
'r': () => reply(),
|
||||||
'e|a|plus': () => react(),
|
'e|a|plus': () => react(),
|
||||||
'q': () => { if (canRenote.value && !renoted.value && !renoting) renote(defaultStore.state.visibilityOnBoost); },
|
'q': () => { if (canRenote.value && !renoted.value && !renoting) renote(prefer.s.visibilityOnBoost); },
|
||||||
'm': () => showMenu(),
|
'm': () => showMenu(),
|
||||||
'c': () => {
|
'c': () => {
|
||||||
if (!prefer.s.showClipButtonInNoteFooter) return;
|
if (!prefer.s.showClipButtonInNoteFooter) return;
|
||||||
|
@ -494,8 +495,8 @@ useTooltip(quoteButton, async (showing) => {
|
||||||
function boostVisibility(forceMenu: boolean = false) {
|
function boostVisibility(forceMenu: boolean = false) {
|
||||||
if (renoting) return;
|
if (renoting) return;
|
||||||
|
|
||||||
if (!defaultStore.state.showVisibilitySelectorOnBoost && !forceMenu) {
|
if (!prefer.s.showVisibilitySelectorOnBoost && !forceMenu) {
|
||||||
renote(defaultStore.state.visibilityOnBoost);
|
renote(prefer.s.visibilityOnBoost);
|
||||||
} else {
|
} else {
|
||||||
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
||||||
}
|
}
|
||||||
|
@ -531,7 +532,7 @@ function renote(visibility: Visibility, localOnly: boolean = false) {
|
||||||
|
|
||||||
renoting = true;
|
renoting = true;
|
||||||
|
|
||||||
if (appearNote.value.channel) {
|
if (appearNote.value.channel && !appearNote.value.channel.allowRenoteToExternal) {
|
||||||
const el = renoteButton.value as HTMLElement | null | undefined;
|
const el = renoteButton.value as HTMLElement | null | undefined;
|
||||||
if (el) {
|
if (el) {
|
||||||
const rect = el.getBoundingClientRect();
|
const rect = el.getBoundingClientRect();
|
||||||
|
@ -549,7 +550,7 @@ function renote(visibility: Visibility, localOnly: boolean = false) {
|
||||||
os.toast(i18n.ts.renoted);
|
os.toast(i18n.ts.renoted);
|
||||||
renoted.value = true;
|
renoted.value = true;
|
||||||
}).finally(() => { renoting = false; });
|
}).finally(() => { renoting = false; });
|
||||||
} else if (!appearNote.value.channel || appearNote.value.channel.allowRenoteToExternal) {
|
} else {
|
||||||
const el = renoteButton.value as HTMLElement | null | undefined;
|
const el = renoteButton.value as HTMLElement | null | undefined;
|
||||||
if (el) {
|
if (el) {
|
||||||
const rect = el.getBoundingClientRect();
|
const rect = el.getBoundingClientRect();
|
||||||
|
@ -748,18 +749,18 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
react();
|
react();
|
||||||
} else {
|
} else {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted });
|
const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted });
|
||||||
os.contextMenu(menu, ev).then(focus).finally(cleanup);
|
os.contextMenu(popupMenu, ev).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMenu(): void {
|
function showMenu(): void {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted });
|
const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted });
|
||||||
os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup);
|
os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function menuVersions(): Promise<void> {
|
async function menuVersions(): Promise<void> {
|
||||||
const { menu, cleanup } = await getNoteVersionsMenu({ note: note.value, menuVersionsButton });
|
const { menu, cleanup } = await getNoteVersionsMenu({ note: note.value, menuButton: menuVersionsButton });
|
||||||
os.popupMenu(menu, menuVersionsButton.value).then(focus).finally(cleanup);
|
os.popupMenu(menu, menuVersionsButton.value).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -833,7 +834,7 @@ function loadConversation() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appearNote.value.reply && appearNote.value.reply.replyId && defaultStore.state.autoloadConversation) loadConversation();
|
if (appearNote.value.reply && appearNote.value.reply.replyId && prefer.s.autoloadConversation) loadConversation();
|
||||||
|
|
||||||
function animatedMFM() {
|
function animatedMFM() {
|
||||||
if (allowAnim.value) {
|
if (allowAnim.value) {
|
||||||
|
|
|
@ -34,8 +34,8 @@ import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||||
import MkCwButton from '@/components/MkCwButton.vue';
|
import MkCwButton from '@/components/MkCwButton.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { defaultStore } from '@/store.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { prefer } from '@/preferences.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: Misskey.entities.Note & {
|
note: Misskey.entities.Note & {
|
||||||
|
@ -46,7 +46,7 @@ const props = defineProps<{
|
||||||
hideFiles?: boolean;
|
hideFiles?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let showContent = ref(defaultStore.state.uncollapseCW);
|
let showContent = ref(prefer.s.uncollapseCW);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
|
|
||||||
const mergedCW = computed(() => computeMergedCw(props.note));
|
const mergedCW = computed(() => computeMergedCw(props.note));
|
||||||
|
|
|
@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="depth < numberOfReplies">
|
<template v-if="depth < store.s.numberOfReplies">
|
||||||
<MkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="$style.reply" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply"/>
|
<MkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="$style.reply" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply"/>
|
||||||
</template>
|
</template>
|
||||||
<div v-else :class="$style.more">
|
<div v-else :class="$style.more">
|
||||||
|
@ -87,6 +87,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { computed, ref, shallowRef, watch } from 'vue';
|
import { computed, ref, shallowRef, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { computeMergedCw } from '@@/js/compute-merged-cw.js';
|
import { computeMergedCw } from '@@/js/compute-merged-cw.js';
|
||||||
|
import { host } from '@@/js/config.js';
|
||||||
|
import type { Visibility } from '@/utility/boost-quote.js';
|
||||||
|
import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
|
||||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||||
import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
|
import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
|
||||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||||
|
@ -99,16 +102,16 @@ import { i18n } from '@/i18n.js';
|
||||||
import { $i } from '@/i.js';
|
import { $i } from '@/i.js';
|
||||||
import { userPage } from '@/filters/user.js';
|
import { userPage } from '@/filters/user.js';
|
||||||
import { checkWordMute } from '@/utility/check-word-mute.js';
|
import { checkWordMute } from '@/utility/check-word-mute.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { pleaseLogin } from '@/utility/please-login.js';
|
||||||
import { host } from '@@/js/config.js';
|
|
||||||
import { pleaseLogin, type OpenOnRemoteOptions } from '@/utility/please-login.js';
|
|
||||||
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
import { reactionPicker } from '@/utility/reaction-picker.js';
|
import { reactionPicker } from '@/utility/reaction-picker.js';
|
||||||
import { claimAchievement } from '@/utility/achievements.js';
|
import { claimAchievement } from '@/utility/achievements.js';
|
||||||
import { getNoteMenu } from '@/utility/get-note-menu.js';
|
import { getNoteMenu } from '@/utility/get-note-menu.js';
|
||||||
import { useNoteCapture } from '@/utility/use-note-capture.js';
|
import { boostMenuItems, computeRenoteTooltip } from '@/utility/boost-quote.js';
|
||||||
import { boostMenuItems, type Visibility, computeRenoteTooltip } from '@/utility/boost-quote.js';
|
import { prefer } from '@/preferences.js';
|
||||||
|
import { useNoteCapture } from '@/use/use-note-capture.js';
|
||||||
|
import { store } from '@/store.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -120,6 +123,7 @@ const props = withDefaults(defineProps<{
|
||||||
depth?: number;
|
depth?: number;
|
||||||
}>(), {
|
}>(), {
|
||||||
depth: 1,
|
depth: 1,
|
||||||
|
onDeleteCallback: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
|
const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
|
||||||
|
@ -130,17 +134,16 @@ const translation = ref<any>(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const renoted = ref(false);
|
const renoted = ref(false);
|
||||||
const numberOfReplies = ref(defaultStore.state.numberOfReplies);
|
|
||||||
const reactButton = shallowRef<HTMLElement>();
|
const reactButton = shallowRef<HTMLElement>();
|
||||||
const renoteButton = shallowRef<HTMLElement>();
|
const renoteButton = shallowRef<HTMLElement>();
|
||||||
const quoteButton = shallowRef<HTMLElement>();
|
const quoteButton = shallowRef<HTMLElement>();
|
||||||
const menuButton = shallowRef<HTMLElement>();
|
const menuButton = shallowRef<HTMLElement>();
|
||||||
const likeButton = shallowRef<HTMLElement>();
|
const likeButton = shallowRef<HTMLElement>();
|
||||||
|
|
||||||
const renoteTooltip = computeRenoteTooltip(computed);
|
const renoteTooltip = computeRenoteTooltip(renoted);
|
||||||
|
|
||||||
let appearNote = computed(() => isRenote ? props.note.renote as Misskey.entities.Note : props.note);
|
let appearNote = computed(() => isRenote ? props.note.renote as Misskey.entities.Note : props.note);
|
||||||
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
const defaultLike = computed(() => prefer.s.like ? prefer.s.like : null);
|
||||||
const replies = ref<Misskey.entities.Note[]>([]);
|
const replies = ref<Misskey.entities.Note[]>([]);
|
||||||
|
|
||||||
const mergedCW = computed(() => computeMergedCw(appearNote.value));
|
const mergedCW = computed(() => computeMergedCw(appearNote.value));
|
||||||
|
@ -175,8 +178,8 @@ useNoteCapture({
|
||||||
note: appearNote,
|
note: appearNote,
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
// only update replies if we are, in fact, showing replies
|
// only update replies if we are, in fact, showing replies
|
||||||
onReplyCallback: props.detail && props.depth < numberOfReplies.value ? addReplyTo : undefined,
|
onReplyCallback: props.detail && props.depth < store.s.numberOfReplies ? addReplyTo : undefined,
|
||||||
onDeleteCallback: props.detail && props.depth < numberOfReplies.value ? props.onDeleteCallback : undefined,
|
onDeleteCallback: props.detail && props.depth < store.s.numberOfReplies ? props.onDeleteCallback : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($i) {
|
if ($i) {
|
||||||
|
@ -190,22 +193,21 @@ if ($i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function focus() {
|
function focus() {
|
||||||
el.value.focus();
|
el.value?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function reply(viaKeyboard = false): void {
|
async function reply(viaKeyboard = false): Promise<void> {
|
||||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
os.post({
|
await os.post({
|
||||||
reply: props.note,
|
reply: props.note,
|
||||||
channel: props.note.channel,
|
channel: props.note.channel ?? undefined,
|
||||||
animation: !viaKeyboard,
|
animation: !viaKeyboard,
|
||||||
}, () => {
|
|
||||||
focus();
|
|
||||||
});
|
});
|
||||||
|
focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function react(viaKeyboard = false): void {
|
function react(): void {
|
||||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
sound.playMisskeySfx('reaction');
|
sound.playMisskeySfx('reaction');
|
||||||
|
@ -285,15 +287,15 @@ function undoRenote() : void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let showContent = ref(defaultStore.state.uncollapseCW);
|
let showContent = ref(prefer.s.uncollapseCW);
|
||||||
|
|
||||||
watch(() => props.expandAllCws, (expandAllCws) => {
|
watch(() => props.expandAllCws, (expandAllCws) => {
|
||||||
if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
|
if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
|
||||||
});
|
});
|
||||||
|
|
||||||
function boostVisibility(forceMenu: boolean = false) {
|
function boostVisibility(forceMenu: boolean = false) {
|
||||||
if (!defaultStore.state.showVisibilitySelectorOnBoost && !forceMenu) {
|
if (!prefer.s.showVisibilitySelectorOnBoost && !forceMenu) {
|
||||||
renote(defaultStore.state.visibilityOnBoost);
|
renote(prefer.s.visibilityOnBoost);
|
||||||
} else {
|
} else {
|
||||||
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
||||||
}
|
}
|
||||||
|
@ -347,49 +349,23 @@ function quote() {
|
||||||
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
|
|
||||||
if (appearNote.value.channel) {
|
|
||||||
os.post({
|
os.post({
|
||||||
renote: appearNote.value,
|
renote: appearNote.value,
|
||||||
channel: appearNote.value.channel,
|
channel: appearNote.value.channel ?? undefined,
|
||||||
}).then((cancelled) => {
|
}).then((cancelled) => {
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
misskeyApi('notes/renotes', {
|
misskeyApi('notes/renotes', {
|
||||||
noteId: props.note.id,
|
noteId: props.note.id,
|
||||||
userId: $i.id,
|
userId: $i?.id,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
quote: true,
|
quote: true,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (!(res.length > 0)) return;
|
if (!(res.length > 0)) return;
|
||||||
const el = quoteButton.value as HTMLElement | null | undefined;
|
const popupEl = quoteButton.value as HTMLElement | null | undefined;
|
||||||
if (el && res.length > 0) {
|
if (popupEl && res.length > 0) {
|
||||||
const rect = el.getBoundingClientRect();
|
const rect = popupEl.getBoundingClientRect();
|
||||||
const x = rect.left + (el.offsetWidth / 2);
|
const x = rect.left + (popupEl.offsetWidth / 2);
|
||||||
const y = rect.top + (el.offsetHeight / 2);
|
const y = rect.top + (popupEl.offsetHeight / 2);
|
||||||
const { dispose } = os.popup(MkRippleEffect, { x, y }, {
|
|
||||||
end: () => dispose(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
os.toast(i18n.ts.quoted);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
os.post({
|
|
||||||
renote: appearNote.value,
|
|
||||||
}).then((cancelled) => {
|
|
||||||
if (cancelled) return;
|
|
||||||
misskeyApi('notes/renotes', {
|
|
||||||
noteId: props.note.id,
|
|
||||||
userId: $i.id,
|
|
||||||
limit: 1,
|
|
||||||
quote: true,
|
|
||||||
}).then((res) => {
|
|
||||||
if (!(res.length > 0)) return;
|
|
||||||
const el = quoteButton.value as HTMLElement | null | undefined;
|
|
||||||
if (el && res.length > 0) {
|
|
||||||
const rect = el.getBoundingClientRect();
|
|
||||||
const x = rect.left + (el.offsetWidth / 2);
|
|
||||||
const y = rect.top + (el.offsetHeight / 2);
|
|
||||||
const { dispose } = os.popup(MkRippleEffect, { x, y }, {
|
const { dispose } = os.popup(MkRippleEffect, { x, y }, {
|
||||||
end: () => dispose(),
|
end: () => dispose(),
|
||||||
});
|
});
|
||||||
|
@ -399,13 +375,10 @@ function quote() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function menu(viaKeyboard = false): void {
|
function menu(): void {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: props.note, translating, translation, menuButton, isDeleted });
|
const { popupMenu, cleanup } = getNoteMenu({ note: props.note, translating, translation, isDeleted });
|
||||||
os.popupMenu(menu, menuButton.value, {
|
os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup);
|
||||||
viaKeyboard,
|
|
||||||
}).then(focus).finally(cleanup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.detail) {
|
if (props.detail) {
|
||||||
|
|
|
@ -884,7 +884,7 @@ async function post(ev?: MouseEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defaultStore.state.warnMissingAltText) {
|
if (prefer.s.warnMissingAltText) {
|
||||||
const filesData = toRaw(files.value);
|
const filesData = toRaw(files.value);
|
||||||
|
|
||||||
const isMissingAltText = filesData.filter(
|
const isMissingAltText = filesData.filter(
|
||||||
|
@ -952,7 +952,7 @@ async function post(ev?: MouseEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let token: string | undefined = undefined;
|
let token: string | null | undefined = undefined;
|
||||||
|
|
||||||
if (postAccount.value) {
|
if (postAccount.value) {
|
||||||
const storedAccounts = await getAccounts();
|
const storedAccounts = await getAccounts();
|
||||||
|
|
|
@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { defineAsyncComponent, inject } from 'vue';
|
import { defineAsyncComponent, inject } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import type { MenuItem } from '@/types/menu';
|
import type { MenuItem } from '@/types/menu';
|
||||||
import { copyToClipboard } from '@/utility/copy-to-clipboard';
|
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
|
||||||
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
|
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
|
|
@ -5,13 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="[$style.root, { [$style.collapsed]: collapsed }]">
|
<div :class="[$style.root, { [$style.collapsed]: collapsed }]">
|
||||||
<div :class="{ [$style.clickToOpen]: defaultStore.state.clickToOpen }" @click.stop="defaultStore.state.clickToOpen ? noteclick(note.id) : undefined">
|
<div :class="{ [$style.clickToOpen]: prefer.s.clickToOpen }" @click.stop="prefer.s.clickToOpen ? noteclick(note.id) : undefined">
|
||||||
<span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
<span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
||||||
<span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deletedNote }})</span>
|
<span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deletedNote }})</span>
|
||||||
<MkA v-if="note.replyId" :class="$style.reply" :to="`/notes/${note.replyId}`" @click.stop><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA>
|
<MkA v-if="note.replyId" :class="$style.reply" :to="`/notes/${note.replyId}`" @click.stop><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA>
|
||||||
<Mfm v-if="note.text" :text="note.text" :isBlock="true" :author="note.user" :nyaize="'respect'" :isAnim="allowAnim" :emojiUrls="note.emojis"/>
|
<Mfm v-if="note.text" :text="note.text" :isBlock="true" :author="note.user" :nyaize="'respect'" :isAnim="allowAnim" :emojiUrls="note.emojis"/>
|
||||||
<MkButton v-if="!allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
<MkButton v-if="!allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
||||||
<MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
<MkButton v-else-if="!prefer.s.animatedMfm && allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
||||||
<div v-if="note.text && translating || note.text && translation" :class="$style.translation">
|
<div v-if="note.text && translating || note.text && translation" :class="$style.translation">
|
||||||
<MkLoading v-if="translating" mini/>
|
<MkLoading v-if="translating" mini/>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
<MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`" @click.stop>RN: ...</MkA>
|
<MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`" @click.stop>RN: ...</MkA>
|
||||||
</div>
|
</div>
|
||||||
<details v-if="note.files && note.files.length > 0" :open="!defaultStore.state.collapseFiles && !hideFiles">
|
<details v-if="note.files && note.files.length > 0" :open="!prefer.s.collapseFiles && !hideFiles">
|
||||||
<summary>({{ i18n.tsx.withNFiles({ n: note.files.length }) }})</summary>
|
<summary>({{ i18n.tsx.withNFiles({ n: note.files.length }) }})</summary>
|
||||||
<MkMediaList :mediaList="note.files"/>
|
<MkMediaList :mediaList="note.files"/>
|
||||||
</details>
|
</details>
|
||||||
|
@ -47,10 +47,10 @@ import MkMediaList from '@/components/MkMediaList.vue';
|
||||||
import MkPoll from '@/components/MkPoll.vue';
|
import MkPoll from '@/components/MkPoll.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { defaultStore } from '@/store.js';
|
|
||||||
import { useRouter } from '@/router/supplier.js';
|
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { checkAnimationFromMfm } from '@/utility/check-animated-mfm.js';
|
import { checkAnimationFromMfm } from '@/utility/check-animated-mfm.js';
|
||||||
|
import { useRouter } from '@/router';
|
||||||
|
import { prefer } from '@/preferences.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -63,7 +63,7 @@ const props = defineProps<{
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
function noteclick(id: string) {
|
function noteclick(id: string) {
|
||||||
const selection = document.getSelection();
|
const selection = window.document.getSelection();
|
||||||
if (selection?.toString().length === 0) {
|
if (selection?.toString().length === 0) {
|
||||||
router.push(`/notes/${id}`);
|
router.push(`/notes/${id}`);
|
||||||
}
|
}
|
||||||
|
@ -71,9 +71,9 @@ function noteclick(id: string) {
|
||||||
|
|
||||||
const parsed = computed(() => props.note.text ? mfm.parse(props.note.text) : null);
|
const parsed = computed(() => props.note.text ? mfm.parse(props.note.text) : null);
|
||||||
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
||||||
let allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
|
let allowAnim = ref(prefer.s.advancedMfm && prefer.s.animatedMfm);
|
||||||
|
|
||||||
const isLong = defaultStore.state.expandLongNote && !props.hideFiles ? false : shouldCollapsed(props.note, []);
|
const isLong = prefer.s.expandLongNote && !props.hideFiles ? false : shouldCollapsed(props.note, []);
|
||||||
|
|
||||||
function animatedMFM() {
|
function animatedMFM() {
|
||||||
if (allowAnim.value) {
|
if (allowAnim.value) {
|
||||||
|
|
|
@ -106,8 +106,9 @@ import { prefer } from '@/preferences.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { warningExternalWebsite } from '@/utility/warning-external-website.js';
|
import { warningExternalWebsite } from '@/utility/warning-external-website.js';
|
||||||
|
|
||||||
|
// TODO DynamicNoteSimple
|
||||||
const XNoteSimple = defineAsyncComponent<typeof MkNoteSimple | typeof SkNoteSimple>(() =>
|
const XNoteSimple = defineAsyncComponent<typeof MkNoteSimple | typeof SkNoteSimple>(() =>
|
||||||
defaultStore.state.noteDesign === 'misskey'
|
prefer.s.noteDesign === 'misskey'
|
||||||
? import('@/components/MkNoteSimple.vue')
|
? import('@/components/MkNoteSimple.vue')
|
||||||
: import('@/components/SkNoteSimple.vue'),
|
: import('@/components/SkNoteSimple.vue'),
|
||||||
);
|
);
|
||||||
|
|
|
@ -34,7 +34,7 @@ import MkModal from '@/components/MkModal.vue';
|
||||||
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 { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { prefer } from '@/preferences.js';
|
||||||
|
|
||||||
type Result = string | number | true | null;
|
type Result = string | number | true | null;
|
||||||
|
|
||||||
|
@ -62,8 +62,8 @@ function done(canceled: boolean, result?: Result): void { // eslint-disable-line
|
||||||
|
|
||||||
async function ok() {
|
async function ok() {
|
||||||
const result = true;
|
const result = true;
|
||||||
if (!defaultStore.state.trustedDomains.includes(domain.value) && trustThisDomain.value) {
|
if (!prefer.s.trustedDomains.includes(domain.value) && trustThisDomain.value) {
|
||||||
await defaultStore.set('trustedDomains', defaultStore.state.trustedDomains.concat(domain.value));
|
prefer.r.trustedDomains.value = prefer.s.trustedDomains.concat(domain.value);
|
||||||
}
|
}
|
||||||
done(false, result);
|
done(false, result);
|
||||||
}
|
}
|
||||||
|
@ -77,11 +77,11 @@ function onKeydown(evt: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener('keydown', onKeydown);
|
window.document.addEventListener('keydown', onKeydown);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
document.removeEventListener('keydown', onKeydown);
|
window.document.removeEventListener('keydown', onKeydown);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -53,9 +53,10 @@ const props = withDefaults(defineProps<{
|
||||||
currentVisibility: typeof Misskey.noteVisibilities[number];
|
currentVisibility: typeof Misskey.noteVisibilities[number];
|
||||||
isSilenced: boolean;
|
isSilenced: boolean;
|
||||||
localOnly: boolean;
|
localOnly: boolean;
|
||||||
src?: HTMLElement;
|
src?: HTMLElement | null;
|
||||||
isReplyVisibilitySpecified?: boolean;
|
isReplyVisibilitySpecified?: boolean;
|
||||||
}>(), {
|
}>(), {
|
||||||
|
src: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import DynamicNote from '@/components/DynamicNote.vue';
|
import DynamicNote from '@/components/DynamicNote.vue';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
|
|
|
@ -59,12 +59,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onDeactivated } from 'vue';
|
import { ref, onDeactivated } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import type { PublicAPI, PublicAPILike } from '@/types/ruffle/setup';
|
||||||
|
import type { PlayerElement } from '@/types/ruffle/player.js';
|
||||||
import MkEllipsis from '@/components/global/MkEllipsis.vue';
|
import MkEllipsis from '@/components/global/MkEllipsis.vue';
|
||||||
import MkLoading from '@/components/global/MkLoading.vue';
|
import MkLoading from '@/components/global/MkLoading.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { prefer } from '@/preferences.js';
|
||||||
import { PublicAPI, PublicAPILike } from '@/types/ruffle/setup'; // This gives us the types for window.RufflePlayer, etc via side effects
|
|
||||||
import { PlayerElement } from '@/types/ruffle/player';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
flashFile: Misskey.entities.DriveFile
|
flashFile: Misskey.entities.DriveFile
|
||||||
|
@ -73,7 +73,7 @@ const props = defineProps<{
|
||||||
const isSensitive = props.flashFile.isSensitive;
|
const isSensitive = props.flashFile.isSensitive;
|
||||||
const url = props.flashFile.url;
|
const url = props.flashFile.url;
|
||||||
const comment = props.flashFile.comment ?? '';
|
const comment = props.flashFile.comment ?? '';
|
||||||
let hide = ref((defaultStore.state.nsfw === 'force') || isSensitive && (defaultStore.state.nsfw !== 'ignore'));
|
let hide = ref((prefer.s.nsfw === 'force') || isSensitive && (prefer.s.nsfw !== 'ignore'));
|
||||||
let playerHide = ref(true);
|
let playerHide = ref(true);
|
||||||
let ruffleContainer = ref<HTMLDivElement>();
|
let ruffleContainer = ref<HTMLDivElement>();
|
||||||
let playPauseButtonKey = ref<number>(0);
|
let playPauseButtonKey = ref<number>(0);
|
||||||
|
@ -126,7 +126,7 @@ async function loadRuffle() {
|
||||||
'maxExecutionDuration': 15,
|
'maxExecutionDuration': 15,
|
||||||
'logLevel': 'error',
|
'logLevel': 'error',
|
||||||
'base': null,
|
'base': null,
|
||||||
'menu': true,
|
'popupMenu': true,
|
||||||
'salign': '',
|
'salign': '',
|
||||||
'forceAlign': false,
|
'forceAlign': false,
|
||||||
'scale': 'showAll',
|
'scale': 'showAll',
|
||||||
|
|
|
@ -44,9 +44,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { ref, nextTick, watch, onDeactivated, onMounted } from 'vue';
|
import { ref, nextTick, watch, onDeactivated, onMounted } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { defaultStore } from '@/store.js';
|
|
||||||
import { ChiptuneJsPlayer, ChiptuneJsConfig } from '@/utility/chiptune2.js';
|
import { ChiptuneJsPlayer, ChiptuneJsConfig } from '@/utility/chiptune2.js';
|
||||||
import { isTouchUsing } from '@/utility/touch.js';
|
import { isTouchUsing } from '@/utility/touch.js';
|
||||||
|
import { prefer } from '@/preferences.js';
|
||||||
|
|
||||||
const colours = {
|
const colours = {
|
||||||
background: '#000000',
|
background: '#000000',
|
||||||
|
@ -72,7 +72,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
const isSensitive = props.module.isSensitive;
|
const isSensitive = props.module.isSensitive;
|
||||||
const url = props.module.url;
|
const url = props.module.url;
|
||||||
let hide = ref((defaultStore.state.nsfw === 'force') ? true : isSensitive && (defaultStore.state.nsfw !== 'ignore'));
|
let hide = ref((prefer.s.nsfw === 'force') ? true : isSensitive && (prefer.s.nsfw !== 'ignore'));
|
||||||
let patternHide = ref(false);
|
let patternHide = ref(false);
|
||||||
let playing = ref(false);
|
let playing = ref(false);
|
||||||
let displayCanvas = ref<HTMLCanvasElement>();
|
let displayCanvas = ref<HTMLCanvasElement>();
|
||||||
|
|
|
@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
v-show="!isDeleted"
|
v-show="!isDeleted"
|
||||||
ref="rootEl"
|
ref="rootEl"
|
||||||
v-hotkey="keymap"
|
v-hotkey="keymap"
|
||||||
:class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover, [$style.skipRender]: defaultStore.state.skipNoteRender }]"
|
:class="[$style.root, { [$style.showActionsOnlyHover]: prefer.s.showNoteActionsOnlyHover, [$style.skipRender]: prefer.s.skipNoteRender }]"
|
||||||
:tabindex="isDeleted ? '-1' : '0'"
|
:tabindex="isDeleted ? '-1' : '0'"
|
||||||
>
|
>
|
||||||
<SkNoteSub v-if="appearNote.reply" v-show="!renoteCollapsed && !inReplyToCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
<SkNoteSub v-if="appearNote.reply" v-show="!renoteCollapsed && !inReplyToCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
||||||
|
@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<SkNoteHeader :note="appearNote" :mini="true"/>
|
<SkNoteHeader :note="appearNote" :mini="true"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :class="[{ [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click.stop="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
|
<div :class="[{ [$style.clickToOpen]: prefer.s.clickToOpen }]" @click.stop="prefer.s.clickToOpen ? noteclick(appearNote.id) : undefined">
|
||||||
<div style="container-type: inline-size;">
|
<div style="container-type: inline-size;">
|
||||||
<p v-if="mergedCW != null" :class="$style.cw">
|
<p v-if="mergedCW != null" :class="$style.cw">
|
||||||
<Mfm
|
<Mfm
|
||||||
|
@ -97,7 +97,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
||||||
<MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
<MkButton v-else-if="!prefer.s.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="appearNote.files && appearNote.files.length > 0">
|
<div v-if="appearNote.files && appearNote.files.length > 0">
|
||||||
<MkMediaList ref="galleryEl" :mediaList="appearNote.files" @click.stop/>
|
<MkMediaList ref="galleryEl" :mediaList="appearNote.files" @click.stop/>
|
||||||
|
@ -160,9 +160,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i>
|
<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i>
|
||||||
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
|
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
|
||||||
<i v-else class="ph-smiley ph-bold ph-lg"></i>
|
<i v-else class="ph-smiley ph-bold ph-lg"></i>
|
||||||
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
|
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || prefer.s.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown.prevent="clip()">
|
<button v-if="prefer.s.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown.prevent="clip()">
|
||||||
<i class="ti ti-paperclip"></i>
|
<i class="ti ti-paperclip"></i>
|
||||||
</button>
|
</button>
|
||||||
<button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown.prevent="showMenu()">
|
<button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown.prevent="showMenu()">
|
||||||
|
@ -215,6 +215,8 @@ import { shouldCollapsed } from '@@/js/collapsed.js';
|
||||||
import { host } from '@@/js/config.js';
|
import { host } from '@@/js/config.js';
|
||||||
import { computeMergedCw } from '@@/js/compute-merged-cw.js';
|
import { computeMergedCw } from '@@/js/compute-merged-cw.js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
|
import type { Keymap } from '@/utility/hotkey.js';
|
||||||
|
import type { Visibility } from '@/utility/boost-quote.js';
|
||||||
import SkNoteSub from '@/components/SkNoteSub.vue';
|
import SkNoteSub from '@/components/SkNoteSub.vue';
|
||||||
import SkNoteHeader from '@/components/SkNoteHeader.vue';
|
import SkNoteHeader from '@/components/SkNoteHeader.vue';
|
||||||
import SkNoteSimple from '@/components/SkNoteSimple.vue';
|
import SkNoteSimple from '@/components/SkNoteSimple.vue';
|
||||||
|
@ -234,27 +236,27 @@ import number from '@/filters/number.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js';
|
import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js';
|
||||||
import * as sound from '@/utility/sound.js';
|
import * as sound from '@/utility/sound.js';
|
||||||
import { defaultStore, noteViewInterruptors } from '@/store.js';
|
|
||||||
import { reactionPicker } from '@/utility/reaction-picker.js';
|
import { reactionPicker } from '@/utility/reaction-picker.js';
|
||||||
import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
|
import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
|
||||||
import { checkAnimationFromMfm } from '@/utility/check-animated-mfm.js';
|
import { checkAnimationFromMfm } from '@/utility/check-animated-mfm.js';
|
||||||
import { $i } from '@/account.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu } from '@/utility/get-note-menu.js';
|
import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu } from '@/utility/get-note-menu.js';
|
||||||
import { getNoteVersionsMenu } from '@/utility/get-note-versions-menu.js';
|
import { getNoteVersionsMenu } from '@/utility/get-note-versions-menu.js';
|
||||||
import { useNoteCapture } from '@/utility/use-note-capture.js';
|
import { useNoteCapture } from '@/use/use-note-capture.js';
|
||||||
import { deepClone } from '@/utility/clone.js';
|
import { deepClone } from '@/utility/clone.js';
|
||||||
import { useTooltip } from '@/utility/use-tooltip.js';
|
import { useTooltip } from '@/use/use-tooltip.js';
|
||||||
import { claimAchievement } from '@/utility/achievements.js';
|
import { claimAchievement } from '@/utility/achievements.js';
|
||||||
import { getNoteSummary } from '@/utility/get-note-summary.js';
|
import { getNoteSummary } from '@/utility/get-note-summary.js';
|
||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
||||||
import { useRouter } from '@/router/supplier.js';
|
import { boostMenuItems, computeRenoteTooltip } from '@/utility/boost-quote.js';
|
||||||
import { boostMenuItems, type Visibility, computeRenoteTooltip } from '@/utility/boost-quote.js';
|
|
||||||
import { isEnabledUrlPreview } from '@/instance.js';
|
import { isEnabledUrlPreview } from '@/instance.js';
|
||||||
import { type Keymap } from '@/utility/hotkey.js';
|
|
||||||
import { focusPrev, focusNext } from '@/utility/focus.js';
|
import { focusPrev, focusNext } from '@/utility/focus.js';
|
||||||
import { getAppearNote } from '@/utility/get-appear-note.js';
|
import { getAppearNote } from '@/utility/get-appear-note.js';
|
||||||
|
import { $i } from '@/i';
|
||||||
|
import { prefer } from '@/preferences';
|
||||||
|
import { useRouter } from '@/router';
|
||||||
|
import { getPluginHandlers } from '@/plugin.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -282,13 +284,14 @@ const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', nul
|
||||||
const note = ref(deepClone(props.note));
|
const note = ref(deepClone(props.note));
|
||||||
|
|
||||||
function noteclick(id: string) {
|
function noteclick(id: string) {
|
||||||
const selection = document.getSelection();
|
const selection = window.document.getSelection();
|
||||||
if (selection?.toString().length === 0) {
|
if (selection?.toString().length === 0) {
|
||||||
router.push(`/notes/${id}`);
|
router.push(`/notes/${id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// plugin
|
// plugin
|
||||||
|
const noteViewInterruptors = getPluginHandlers('note_view_interruptor');
|
||||||
if (noteViewInterruptors.length > 0) {
|
if (noteViewInterruptors.length > 0) {
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
let result: Misskey.entities.Note | null = deepClone(note.value);
|
let result: Misskey.entities.Note | null = deepClone(note.value);
|
||||||
|
@ -321,30 +324,30 @@ const likeButton = shallowRef<HTMLElement>();
|
||||||
const appearNote = computed(() => getAppearNote(note.value));
|
const appearNote = computed(() => getAppearNote(note.value));
|
||||||
const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
|
const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
|
||||||
const isMyRenote = $i && ($i.id === note.value.userId);
|
const isMyRenote = $i && ($i.id === note.value.userId);
|
||||||
const showContent = ref(defaultStore.state.uncollapseCW);
|
const showContent = ref(prefer.s.uncollapseCW);
|
||||||
const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
|
const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
|
||||||
const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null);
|
const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null);
|
||||||
const isLong = shouldCollapsed(appearNote.value, urls.value ?? []);
|
const isLong = shouldCollapsed(appearNote.value, urls.value ?? []);
|
||||||
const collapsed = ref(defaultStore.state.expandLongNote && appearNote.value.cw == null && isLong ? false : appearNote.value.cw == null && isLong);
|
const collapsed = ref(prefer.s.expandLongNote && appearNote.value.cw == null && isLong ? false : appearNote.value.cw == null && isLong);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const renoted = ref(false);
|
const renoted = 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, true));
|
const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords, true));
|
||||||
const showSoftWordMutedWord = computed(() => defaultStore.state.showSoftWordMutedWord);
|
const showSoftWordMutedWord = computed(() => prefer.s.showSoftWordMutedWord);
|
||||||
const translation = ref<Misskey.entities.NotesTranslateResponse | null>(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 = (prefer.s.instanceTicker === 'always') || (prefer.s.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));
|
||||||
const renoteCollapsed = ref(
|
const renoteCollapsed = ref(
|
||||||
defaultStore.state.collapseRenotes && isRenote && (
|
prefer.s.collapseRenotes && isRenote && (
|
||||||
($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131
|
($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131
|
||||||
(appearNote.value.myReaction != null)
|
(appearNote.value.myReaction != null)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const inReplyToCollapsed = ref(defaultStore.state.collapseNotesRepliedTo);
|
const inReplyToCollapsed = ref(prefer.s.collapseNotesRepliedTo);
|
||||||
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
const defaultLike = computed(() => prefer.s.like ? prefer.s.like : null);
|
||||||
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
||||||
const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
|
const allowAnim = ref(prefer.s.advancedMfm && prefer.s.animatedMfm ? true : false);
|
||||||
|
|
||||||
const mergedCW = computed(() => computeMergedCw(appearNote.value));
|
const mergedCW = computed(() => computeMergedCw(appearNote.value));
|
||||||
|
|
||||||
|
@ -393,7 +396,7 @@ const keymap = {
|
||||||
},
|
},
|
||||||
'q': () => {
|
'q': () => {
|
||||||
if (renoteCollapsed.value) return;
|
if (renoteCollapsed.value) return;
|
||||||
if (canRenote.value && !renoted.value && !renoting) renote(defaultStore.state.visibilityOnBoost);
|
if (canRenote.value && !renoted.value && !renoting) renote(prefer.s.visibilityOnBoost);
|
||||||
},
|
},
|
||||||
'm': () => {
|
'm': () => {
|
||||||
if (renoteCollapsed.value) return;
|
if (renoteCollapsed.value) return;
|
||||||
|
@ -401,7 +404,7 @@ const keymap = {
|
||||||
},
|
},
|
||||||
'c': () => {
|
'c': () => {
|
||||||
if (renoteCollapsed.value) return;
|
if (renoteCollapsed.value) return;
|
||||||
if (!defaultStore.state.showClipButtonInNoteFooter) return;
|
if (!prefer.s.showClipButtonInNoteFooter) return;
|
||||||
clip();
|
clip();
|
||||||
},
|
},
|
||||||
'o': () => {
|
'o': () => {
|
||||||
|
@ -531,8 +534,8 @@ if (!props.mock) {
|
||||||
function boostVisibility(forceMenu: boolean = false) {
|
function boostVisibility(forceMenu: boolean = false) {
|
||||||
if (renoting) return;
|
if (renoting) return;
|
||||||
|
|
||||||
if (!defaultStore.state.showVisibilitySelectorOnBoost && !forceMenu) {
|
if (!prefer.s.showVisibilitySelectorOnBoost && !forceMenu) {
|
||||||
renote(defaultStore.state.visibilityOnBoost);
|
renote(prefer.s.visibilityOnBoost);
|
||||||
} else {
|
} else {
|
||||||
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
||||||
}
|
}
|
||||||
|
@ -781,12 +784,12 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
if (ev.target && isLink(ev.target as HTMLElement)) return;
|
if (ev.target && isLink(ev.target as HTMLElement)) return;
|
||||||
if (window.getSelection()?.toString() !== '') return;
|
if (window.getSelection()?.toString() !== '') return;
|
||||||
|
|
||||||
if (defaultStore.state.useReactionPickerForContextMenu) {
|
if (prefer.s.useReactionPickerForContextMenu) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
react();
|
react();
|
||||||
} else {
|
} else {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
|
const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
|
||||||
os.contextMenu(menu, ev).then(focus).finally(cleanup);
|
os.contextMenu(popupMenu, ev).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -795,8 +798,8 @@ function showMenu(): void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
|
const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
|
||||||
os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup);
|
os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function menuVersions(viaKeyboard = false): Promise<void> {
|
async function menuVersions(viaKeyboard = false): Promise<void> {
|
||||||
|
|
|
@ -116,7 +116,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
||||||
<MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
<MkButton v-else-if="!prefer.s.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
||||||
<div v-if="appearNote.files && appearNote.files.length > 0">
|
<div v-if="appearNote.files && appearNote.files.length > 0">
|
||||||
<MkMediaList ref="galleryEl" :mediaList="appearNote.files"/>
|
<MkMediaList ref="galleryEl" :mediaList="appearNote.files"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -174,9 +174,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i>
|
<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i>
|
||||||
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
|
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
|
||||||
<i v-else class="ph-smiley ph-bold ph-lg"></i>
|
<i v-else class="ph-smiley ph-bold ph-lg"></i>
|
||||||
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
|
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || prefer.s.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="clip()">
|
<button v-if="prefer.s.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="clip()">
|
||||||
<i class="ti ti-paperclip"></i>
|
<i class="ti ti-paperclip"></i>
|
||||||
</button>
|
</button>
|
||||||
<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="showMenu()">
|
<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="showMenu()">
|
||||||
|
@ -338,7 +338,7 @@ const likeButton = shallowRef<HTMLElement>();
|
||||||
const appearNote = computed(() => getAppearNote(note.value));
|
const appearNote = computed(() => getAppearNote(note.value));
|
||||||
const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
|
const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
|
||||||
const isMyRenote = $i && ($i.id === note.value.userId);
|
const isMyRenote = $i && ($i.id === note.value.userId);
|
||||||
const showContent = ref(defaultStore.state.uncollapseCW);
|
const showContent = ref(prefer.s.uncollapseCW);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const renoted = ref(false);
|
const renoted = ref(false);
|
||||||
const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false);
|
const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false);
|
||||||
|
@ -347,13 +347,13 @@ 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).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null;
|
const urls = parsed ? extractUrlFromMfm(parsed).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null;
|
||||||
const animated = computed(() => parsed ? checkAnimationFromMfm(parsed) : null);
|
const animated = computed(() => parsed ? checkAnimationFromMfm(parsed) : null);
|
||||||
const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
|
const allowAnim = ref(prefer.s.advancedMfm && prefer.s.animatedMfm ? true : false);
|
||||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
|
const showTicker = (prefer.s.instanceTicker === 'always') || (prefer.s.instanceTicker === 'remote' && appearNote.value.user.instance);
|
||||||
const conversation = ref<Misskey.entities.Note[]>([]);
|
const conversation = ref<Misskey.entities.Note[]>([]);
|
||||||
const replies = ref<Misskey.entities.Note[]>([]);
|
const replies = ref<Misskey.entities.Note[]>([]);
|
||||||
const quotes = ref<Misskey.entities.Note[]>([]);
|
const quotes = ref<Misskey.entities.Note[]>([]);
|
||||||
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));
|
||||||
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
const defaultLike = computed(() => prefer.s.like ? prefer.s.like : null);
|
||||||
|
|
||||||
const mergedCW = computed(() => computeMergedCw(appearNote.value));
|
const mergedCW = computed(() => computeMergedCw(appearNote.value));
|
||||||
|
|
||||||
|
@ -383,10 +383,10 @@ const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
|
||||||
const keymap = {
|
const keymap = {
|
||||||
'r': () => reply(),
|
'r': () => reply(),
|
||||||
'e|a|plus': () => react(),
|
'e|a|plus': () => react(),
|
||||||
'q': () => { if (canRenote.value && !renoted.value && !renoting) renote(defaultStore.state.visibilityOnBoost); },
|
'q': () => { if (canRenote.value && !renoted.value && !renoting) renote(prefer.s.visibilityOnBoost); },
|
||||||
'm': () => showMenu(),
|
'm': () => showMenu(),
|
||||||
'c': () => {
|
'c': () => {
|
||||||
if (!defaultStore.state.showClipButtonInNoteFooter) return;
|
if (!prefer.s.showClipButtonInNoteFooter) return;
|
||||||
clip();
|
clip();
|
||||||
},
|
},
|
||||||
'o': () => galleryEl.value?.openGallery(),
|
'o': () => galleryEl.value?.openGallery(),
|
||||||
|
@ -493,8 +493,8 @@ useTooltip(quoteButton, async (showing) => {
|
||||||
function boostVisibility(forceMenu: boolean = false) {
|
function boostVisibility(forceMenu: boolean = false) {
|
||||||
if (renoting) return;
|
if (renoting) return;
|
||||||
|
|
||||||
if (!defaultStore.state.showVisibilitySelectorOnBoost && !forceMenu) {
|
if (!prefer.s.showVisibilitySelectorOnBoost && !forceMenu) {
|
||||||
renote(defaultStore.state.visibilityOnBoost);
|
renote(prefer.s.visibilityOnBoost);
|
||||||
} else {
|
} else {
|
||||||
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
||||||
}
|
}
|
||||||
|
@ -734,18 +734,18 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
if (ev.target && isLink(ev.target as HTMLElement)) return;
|
if (ev.target && isLink(ev.target as HTMLElement)) return;
|
||||||
if (window.getSelection()?.toString() !== '') return;
|
if (window.getSelection()?.toString() !== '') return;
|
||||||
|
|
||||||
if (defaultStore.state.useReactionPickerForContextMenu) {
|
if (prefer.s.useReactionPickerForContextMenu) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
react();
|
react();
|
||||||
} else {
|
} else {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted });
|
const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted });
|
||||||
os.contextMenu(menu, ev).then(focus).finally(cleanup);
|
os.contextMenu(popupMenu, ev).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMenu(): void {
|
function showMenu(): void {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted });
|
const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted });
|
||||||
os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup);
|
os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function menuVersions(): Promise<void> {
|
async function menuVersions(): Promise<void> {
|
||||||
|
@ -824,7 +824,7 @@ function loadConversation() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appearNote.value.reply && appearNote.value.reply.replyId && defaultStore.state.autoloadConversation) loadConversation();
|
if (appearNote.value.reply && appearNote.value.reply.replyId && prefer.s.autoloadConversation) loadConversation();
|
||||||
|
|
||||||
function animatedMFM() {
|
function animatedMFM() {
|
||||||
if (allowAnim.value) {
|
if (allowAnim.value) {
|
||||||
|
|
|
@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
|
<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
|
||||||
<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
|
<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
|
||||||
</div>
|
</div>
|
||||||
<SkInstanceTicker v-if="showTicker && !isMobile && defaultStore.state.showTickerOnReplies" style="cursor: pointer; max-height: 5px; top: 3px; position: relative; margin-top: 0px !important;" :instance="note.user.instance" :host="note.user.host" @click.stop="showOnRemote()"/>
|
<SkInstanceTicker v-if="showTicker && !isMobile && prefer.s.showTickerOnReplies" style="cursor: pointer; max-height: 5px; top: 3px; position: relative; margin-top: 0px !important;" :instance="note.user.instance" :host="note.user.host" @click.stop="showOnRemote()"/>
|
||||||
<div :class="$style.classicInfo">
|
<div :class="$style.classicInfo">
|
||||||
<div v-if="mock">
|
<div v-if="mock">
|
||||||
<MkTime :time="note.createdAt" colored/>
|
<MkTime :time="note.createdAt" colored/>
|
||||||
|
@ -95,7 +95,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
const menuVersionsButton = shallowRef<HTMLElement>();
|
const menuVersionsButton = shallowRef<HTMLElement>();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && props.note.user.instance);
|
const showTicker = (prefer.s.instanceTicker === 'always') || (prefer.s.instanceTicker === 'remote' && props.note.user.instance);
|
||||||
|
|
||||||
const MOBILE_THRESHOLD = 500;
|
const MOBILE_THRESHOLD = 500;
|
||||||
const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
|
const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
|
||||||
|
|
|
@ -36,7 +36,7 @@ const props = defineProps<{
|
||||||
hideFiles?: boolean;
|
hideFiles?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let showContent = ref(defaultStore.state.uncollapseCW);
|
let showContent = ref(prefer.s.uncollapseCW);
|
||||||
|
|
||||||
const mergedCW = computed(() => computeMergedCw(props.note));
|
const mergedCW = computed(() => computeMergedCw(props.note));
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="depth < numberOfReplies">
|
<template v-if="depth < store.s.numberOfReplies">
|
||||||
<SkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="[$style.reply, { [$style.single]: replies.length === 1 }]" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply" :isReply="props.isReply"/>
|
<SkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="[$style.reply, { [$style.single]: replies.length === 1 }]" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply" :isReply="props.isReply"/>
|
||||||
</template>
|
</template>
|
||||||
<div v-else :class="$style.more">
|
<div v-else :class="$style.more">
|
||||||
|
@ -95,6 +95,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { computed, ref, shallowRef, watch } from 'vue';
|
import { computed, ref, shallowRef, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { computeMergedCw } from '@@/js/compute-merged-cw.js';
|
import { computeMergedCw } from '@@/js/compute-merged-cw.js';
|
||||||
|
import { host } from '@@/js/config.js';
|
||||||
|
import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
|
||||||
|
import type { Visibility } from '@/utility/boost-quote.js';
|
||||||
import SkNoteHeader from '@/components/SkNoteHeader.vue';
|
import SkNoteHeader from '@/components/SkNoteHeader.vue';
|
||||||
import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
|
import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
|
||||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||||
|
@ -104,22 +107,22 @@ import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import * as sound from '@/utility/sound.js';
|
import * as sound from '@/utility/sound.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { $i } from '@/account.js';
|
|
||||||
import { userPage } from '@/filters/user.js';
|
import { userPage } from '@/filters/user.js';
|
||||||
import { checkWordMute } from '@/utility/check-word-mute.js';
|
import { checkWordMute } from '@/utility/check-word-mute.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { pleaseLogin } from '@/utility/please-login.js';
|
||||||
import { host } from '@@/js/config.js';
|
|
||||||
import { pleaseLogin, type OpenOnRemoteOptions } from '@/utility/please-login.js';
|
|
||||||
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
import { reactionPicker } from '@/utility/reaction-picker.js';
|
import { reactionPicker } from '@/utility/reaction-picker.js';
|
||||||
import { claimAchievement } from '@/utility/achievements.js';
|
import { claimAchievement } from '@/utility/achievements.js';
|
||||||
import { getNoteMenu } from '@/utility/get-note-menu.js';
|
import { getNoteMenu } from '@/utility/get-note-menu.js';
|
||||||
import { useNoteCapture } from '@/utility/use-note-capture.js';
|
import { useNoteCapture } from '@/use/use-note-capture.js';
|
||||||
import { boostMenuItems, type Visibility, computeRenoteTooltip } from '@/utility/boost-quote.js';
|
import { boostMenuItems, computeRenoteTooltip } from '@/utility/boost-quote.js';
|
||||||
|
import { prefer } from '@/preferences';
|
||||||
|
import { store } from '@/store';
|
||||||
|
import { $i } from '@/i.js';
|
||||||
|
|
||||||
const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
|
const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
|
||||||
const hideLine = computed(() => { return props.detail ? true : false; });
|
const hideLine = computed(() => props.detail);
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -144,7 +147,6 @@ const translation = ref<any>(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const renoted = ref(false);
|
const renoted = ref(false);
|
||||||
const numberOfReplies = ref(defaultStore.state.numberOfReplies);
|
|
||||||
const reactButton = shallowRef<HTMLElement>();
|
const reactButton = shallowRef<HTMLElement>();
|
||||||
const renoteButton = shallowRef<HTMLElement>();
|
const renoteButton = shallowRef<HTMLElement>();
|
||||||
const quoteButton = shallowRef<HTMLElement>();
|
const quoteButton = shallowRef<HTMLElement>();
|
||||||
|
@ -154,7 +156,7 @@ const likeButton = shallowRef<HTMLElement>();
|
||||||
const renoteTooltip = computeRenoteTooltip(renoted);
|
const renoteTooltip = computeRenoteTooltip(renoted);
|
||||||
|
|
||||||
let appearNote = computed(() => isRenote ? props.note.renote as Misskey.entities.Note : props.note);
|
let appearNote = computed(() => isRenote ? props.note.renote as Misskey.entities.Note : props.note);
|
||||||
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
const defaultLike = computed(() => prefer.s.like ? prefer.s.like : null);
|
||||||
const replies = ref<Misskey.entities.Note[]>([]);
|
const replies = ref<Misskey.entities.Note[]>([]);
|
||||||
|
|
||||||
const mergedCW = computed(() => computeMergedCw(appearNote.value));
|
const mergedCW = computed(() => computeMergedCw(appearNote.value));
|
||||||
|
@ -189,8 +191,8 @@ useNoteCapture({
|
||||||
note: appearNote,
|
note: appearNote,
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
// only update replies if we are, in fact, showing replies
|
// only update replies if we are, in fact, showing replies
|
||||||
onReplyCallback: props.detail && props.depth < numberOfReplies.value ? addReplyTo : undefined,
|
onReplyCallback: props.detail && props.depth < store.s.numberOfReplies ? addReplyTo : undefined,
|
||||||
onDeleteCallback: props.detail && props.depth < numberOfReplies.value ? props.onDeleteCallback : undefined,
|
onDeleteCallback: props.detail && props.depth < store.s.numberOfReplies ? props.onDeleteCallback : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($i) {
|
if ($i) {
|
||||||
|
@ -299,15 +301,15 @@ function undoRenote() : void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let showContent = ref(defaultStore.state.uncollapseCW);
|
let showContent = ref(prefer.s.uncollapseCW);
|
||||||
|
|
||||||
watch(() => props.expandAllCws, (expandAllCws) => {
|
watch(() => props.expandAllCws, (expandAllCws) => {
|
||||||
if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
|
if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
|
||||||
});
|
});
|
||||||
|
|
||||||
function boostVisibility(forceMenu: boolean = false) {
|
function boostVisibility(forceMenu: boolean = false) {
|
||||||
if (!defaultStore.state.showVisibilitySelectorOnBoost && !forceMenu) {
|
if (!prefer.s.showVisibilitySelectorOnBoost && !forceMenu) {
|
||||||
renote(defaultStore.state.visibilityOnBoost);
|
renote(prefer.s.visibilityOnBoost);
|
||||||
} else {
|
} else {
|
||||||
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
os.popupMenu(boostMenuItems(appearNote, renote), renoteButton.value);
|
||||||
}
|
}
|
||||||
|
@ -415,17 +417,15 @@ function quote() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function menu(viaKeyboard = false): void {
|
function menu(): void {
|
||||||
const { menu, cleanup } = getNoteMenu({ note: props.note, translating, translation, menuButton, isDeleted });
|
const { popupMenu, cleanup } = getNoteMenu({ note: props.note, translating, translation, isDeleted });
|
||||||
os.popupMenu(menu, menuButton.value, {
|
os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup);
|
||||||
viaKeyboard,
|
|
||||||
}).then(focus).finally(cleanup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.detail) {
|
if (props.detail) {
|
||||||
misskeyApi('notes/children', {
|
misskeyApi('notes/children', {
|
||||||
noteId: props.note.id,
|
noteId: props.note.id,
|
||||||
limit: numberOfReplies.value,
|
limit: store.s.numberOfReplies,
|
||||||
showQuotes: false,
|
showQuotes: false,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
replies.value = res;
|
replies.value = res;
|
||||||
|
|
|
@ -92,11 +92,12 @@ import MkPoll from '@/components/MkPoll.vue';
|
||||||
import MkUrlPreview from '@/components/MkUrlPreview.vue';
|
import MkUrlPreview from '@/components/MkUrlPreview.vue';
|
||||||
import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
|
import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
|
||||||
import { userPage } from '@/filters/user.js';
|
import { userPage } from '@/filters/user.js';
|
||||||
import { defaultStore, noteViewInterruptors } from '@/store.js';
|
|
||||||
import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
|
import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { deepClone } from '@/utility/clone.js';
|
import { deepClone } from '@/utility/clone.js';
|
||||||
import { dateTimeFormat } from '@/utility/intl-const.js';
|
import { dateTimeFormat } from '@/utility/intl-const.js';
|
||||||
|
import { prefer } from '@/preferences';
|
||||||
|
import { getPluginHandlers } from '@/plugin.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -113,6 +114,7 @@ const inChannel = inject('inChannel', null);
|
||||||
let note = ref(deepClone(props.note));
|
let note = ref(deepClone(props.note));
|
||||||
|
|
||||||
// plugin
|
// plugin
|
||||||
|
const noteViewInterruptors = getPluginHandlers('note_view_interruptor');
|
||||||
if (noteViewInterruptors.length > 0) {
|
if (noteViewInterruptors.length > 0) {
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
let result = deepClone(note.value);
|
let result = deepClone(note.value);
|
||||||
|
@ -132,7 +134,7 @@ replaceContent();
|
||||||
const isRenote = (
|
const isRenote = (
|
||||||
note.value.renote != null &&
|
note.value.renote != null &&
|
||||||
note.value.text == null &&
|
note.value.text == null &&
|
||||||
note.value.fileIds.length === 0 &&
|
!note.value.fileIds?.length &&
|
||||||
note.value.poll == null
|
note.value.poll == null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -145,7 +147,7 @@ const showContent = ref(false);
|
||||||
const translation = ref(null);
|
const translation = ref(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const urls = appearNote.value.text ? extractUrlFromMfm(mfm.parse(appearNote.value.text)).filter(u => u !== renoteUrl && u !== renoteUri) : null;
|
const urls = appearNote.value.text ? extractUrlFromMfm(mfm.parse(appearNote.value.text)).filter(u => u !== renoteUrl && u !== renoteUri) : null;
|
||||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
|
const showTicker = (prefer.s.instanceTicker === 'always') || (prefer.s.instanceTicker === 'remote' && appearNote.value.user.instance);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,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 === 'respect' && props.author?.isCat && props.author?.speakAsCat && !defaultStore.state.disableCatSpeak;
|
const shouldNyaize = props.nyaize === 'respect' && props.author?.isCat && props.author?.speakAsCat && !prefer.s.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;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<FormSection v-if="instance.repositoryUrl !== 'https://activitypub.software/TransFem-org/Sharkey/'">
|
<FormSection v-if="instance.repositoryUrl !== 'https://activitypub.software/TransFem-org/Sharkey/'">
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<MkInfo>
|
<MkInfo>
|
||||||
{{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name }) }}
|
{{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name ?? '' }) }}
|
||||||
</MkInfo>
|
</MkInfo>
|
||||||
<FormLink v-if="instance.repositoryUrl" :to="instance.repositoryUrl" external>
|
<FormLink v-if="instance.repositoryUrl" :to="instance.repositoryUrl" external>
|
||||||
<template #icon><i class="ti ti-code"></i></template>
|
<template #icon><i class="ti ti-code"></i></template>
|
||||||
|
@ -91,7 +91,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onBeforeUnmount, ref, shallowRef, computed } from 'vue';
|
import { nextTick, onBeforeUnmount, ref, computed, useTemplateRef } from 'vue';
|
||||||
import { version } from '@@/js/config.js';
|
import { version } from '@@/js/config.js';
|
||||||
import FormLink from '@/components/form/link.vue';
|
import FormLink from '@/components/form/link.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
|
@ -101,12 +101,12 @@ import MkInfo from '@/components/MkInfo.vue';
|
||||||
import { physics } from '@/utility/physics.js';
|
import { physics } from '@/utility/physics.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
import { defaultStore } from '@/store.js';
|
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { definePageMetadata } from '@/utility/page-metadata.js';
|
|
||||||
import { claimAchievement, claimedAchievements } from '@/utility/achievements.js';
|
import { claimAchievement, claimedAchievements } from '@/utility/achievements.js';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/i';
|
||||||
|
import { definePage } from '@/page';
|
||||||
|
import { store } from '@/store.js';
|
||||||
|
|
||||||
type Section = {
|
type Section = {
|
||||||
heading: string,
|
heading: string,
|
||||||
|
@ -124,8 +124,13 @@ type Section = {
|
||||||
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 everyone = ref<Section[]>([
|
const everyone = ref<Section[]>([
|
||||||
{
|
{
|
||||||
heading: i18n.ts._aboutMisskey.projectMembers,
|
heading: i18n.ts._aboutMisskey.projectMembers,
|
||||||
|
@ -262,7 +267,7 @@ const everyone = ref<Section[]>([
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const containerEl = shallowRef<HTMLElement>();
|
const containerEl = useTemplateRef('containerEl');
|
||||||
|
|
||||||
await misskeyApi('sponsors', { forceUpdate: false }).then((res) => {
|
await misskeyApi('sponsors', { forceUpdate: false }).then((res) => {
|
||||||
const section: Section = {
|
const section: Section = {
|
||||||
|
@ -293,7 +298,7 @@ function fisher_yates<T>(array: T[]): T[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
function iconLoaded() {
|
function iconLoaded() {
|
||||||
const emojis = defaultStore.state.reactions;
|
const emojis = store.s.reactions;
|
||||||
const containerWidth = containerEl.value.offsetWidth;
|
const containerWidth = containerEl.value.offsetWidth;
|
||||||
for (let i = 0; i < 32; i++) {
|
for (let i = 0; i < 32; i++) {
|
||||||
easterEggEmojis.value.push({
|
easterEggEmojis.value.push({
|
||||||
|
@ -337,7 +342,7 @@ const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePageMetadata(() => ({
|
definePage(() => ({
|
||||||
title: i18n.ts.aboutMisskey,
|
title: i18n.ts.aboutMisskey,
|
||||||
icon: null,
|
icon: null,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, useTemplateRef, ref, watchEffect } from 'vue';
|
import { computed, useTemplateRef, ref, watchEffect } from 'vue';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import { defaultMemoryStorage } from '@/memory-storage';
|
import { defaultMemoryStorage } from '@/memory-storage.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.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';
|
||||||
|
|
|
@ -100,6 +100,7 @@ import { notesSearchAvailable } from '@/utility/check-permissions.js';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { useRouter } from '@/router.js';
|
import { useRouter } from '@/router.js';
|
||||||
import { deepMerge } from '@/utility/merge.js';
|
import { deepMerge } from '@/utility/merge.js';
|
||||||
|
import { store } from '@/store.js';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
@ -122,12 +123,12 @@ const featuredPagination = computed(() => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
const withRenotes = computed<boolean>({
|
const withRenotes = computed<boolean>({
|
||||||
get: () => defaultStore.reactiveState.tl.value.filter.withRenotes,
|
get: () => store.r.tl.value.filter.withRenotes,
|
||||||
set: (x) => saveTlFilter('withRenotes', x),
|
set: (x) => saveTlFilter('withRenotes', x),
|
||||||
});
|
});
|
||||||
|
|
||||||
const onlyFiles = computed<boolean>({
|
const onlyFiles = computed<boolean>({
|
||||||
get: () => defaultStore.reactiveState.tl.value.filter.onlyFiles,
|
get: () => store.r.tl.value.filter.onlyFiles,
|
||||||
set: (x) => saveTlFilter('onlyFiles', x),
|
set: (x) => saveTlFilter('onlyFiles', x),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -150,10 +151,9 @@ watch(() => props.channelId, async () => {
|
||||||
}
|
}
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
function saveTlFilter(key: keyof typeof defaultStore.state.tl.filter, newValue: boolean) {
|
function saveTlFilter(key: keyof typeof store.s.tl.filter, newValue: boolean) {
|
||||||
if (key !== 'withReplies' || $i) {
|
if (key !== 'withReplies' || $i) {
|
||||||
const out = deepMerge({ filter: { [key]: newValue } }, defaultStore.state.tl);
|
store.r.tl.value = deepMerge({ filter: { [key]: newValue } }, store.s.tl);
|
||||||
defaultStore.set('tl', out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template #default="{ items }">
|
<template #default="{ items }">
|
||||||
<MkDateSeparatedList v-slot="{ item }" :items="items" :direction="'down'" :noGap="false" :ad="false">
|
<MkDateSeparatedList v-slot="{ item }" :items="items" :direction="'down'" :noGap="false" :ad="false">
|
||||||
<MkNote :key="item.id" :note="item.note" :class="$style.note"/>
|
<DynamicNote :key="item.id" :note="item.note" :class="$style.note"/>
|
||||||
</MkDateSeparatedList>
|
</MkDateSeparatedList>
|
||||||
</template>
|
</template>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
|
@ -27,17 +27,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
|
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
|
||||||
import { defineAsyncComponent } from 'vue';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
import { infoImageUrl } from '@/instance.js';
|
import { infoImageUrl } from '@/instance.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import DynamicNote from '@/components/DynamicNote.vue';
|
||||||
|
|
||||||
const MkNote = defineAsyncComponent(() =>
|
|
||||||
(defaultStore.state.noteDesign === 'misskey') ? import('@/components/MkNote.vue') :
|
|
||||||
(defaultStore.state.noteDesign === 'sharkey') ? import('@/components/SkNote.vue') :
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
const pagination = {
|
const pagination = {
|
||||||
endpoint: 'i/favorites' as const,
|
endpoint: 'i/favorites' as const,
|
||||||
|
|
|
@ -70,8 +70,9 @@ import { $i } from '@/i.js';
|
||||||
// contextは非ログイン状態の情報しかないためログイン時は利用できない
|
// contextは非ログイン状態の情報しかないためログイン時は利用できない
|
||||||
const CTX_NOTE = !$i && assertServerContext(serverContext, 'note') ? serverContext.note : null;
|
const CTX_NOTE = !$i && assertServerContext(serverContext, 'note') ? serverContext.note : null;
|
||||||
|
|
||||||
|
// TODO DynamicNoteDetailed
|
||||||
const MkNoteDetailed = defineAsyncComponent(() =>
|
const MkNoteDetailed = defineAsyncComponent(() =>
|
||||||
(defaultStore.state.noteDesign === 'misskey')
|
(prefer.s.noteDesign === 'misskey')
|
||||||
? import('@/components/MkNoteDetailed.vue')
|
? import('@/components/MkNoteDetailed.vue')
|
||||||
: import('@/components/SkNoteDetailed.vue'),
|
: import('@/components/SkNoteDetailed.vue'),
|
||||||
);
|
);
|
||||||
|
|
|
@ -936,15 +936,15 @@ const noteDesign = prefer.model('noteDesign');
|
||||||
const uncollapseCW = prefer.model('uncollapseCW');
|
const uncollapseCW = prefer.model('uncollapseCW');
|
||||||
const expandLongNote = prefer.model('expandLongNote');
|
const expandLongNote = prefer.model('expandLongNote');
|
||||||
const disableCatSpeak = prefer.model('disableCatSpeak');
|
const disableCatSpeak = prefer.model('disableCatSpeak');
|
||||||
const enableFaviconNotificationDot = prefer.model('enableFaviconNotificationDot');
|
const enableFaviconNotificationDot = computed(store.makeGetterSetter('enableFaviconNotificationDot'));
|
||||||
const warnMissingAltText = prefer.model('warnMissingAltText');
|
const warnMissingAltText = prefer.model('warnMissingAltText');
|
||||||
const notificationClickable = prefer.model('notificationClickable');
|
const notificationClickable = computed(store.makeGetterSetter('notificationClickable'));
|
||||||
const warnExternalUrl = prefer.model('warnExternalUrl');
|
const warnExternalUrl = prefer.model('warnExternalUrl');
|
||||||
const showVisibilitySelectorOnBoost = prefer.model('showVisibilitySelectorOnBoost');
|
const showVisibilitySelectorOnBoost = prefer.model('showVisibilitySelectorOnBoost');
|
||||||
const visibilityOnBoost = prefer.model('visibilityOnBoost');
|
const visibilityOnBoost = prefer.model('visibilityOnBoost');
|
||||||
const cornerRadius = prefer.model('cornerRadius');
|
const cornerRadius = computed(store.makeGetterSetter('cornerRadius'));
|
||||||
const oneko = prefer.model('oneko');
|
const oneko = computed(store.makeGetterSetter('oneko'));
|
||||||
const numberOfReplies = prefer.model('numberOfReplies');
|
const numberOfReplies = computed(store.makeGetterSetter('numberOfReplies'));
|
||||||
|
|
||||||
const useCustomSearchEngine = computed(() => !Object.keys(searchEngineMap).includes(searchEngine.value));
|
const useCustomSearchEngine = computed(() => !Object.keys(searchEngineMap).includes(searchEngine.value));
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<SearchMarker path="/settings/profile" :label="i18n.ts.profile" :keywords="['profile']" icon="ti ti-user">
|
<SearchMarker path="/settings/profile" :label="i18n.ts.profile" :keywords="['profile']" icon="ti ti-user">
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<div class="_panel">
|
<div class="_panel">
|
||||||
<div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
|
<div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : '' }">
|
||||||
<div :class="$style.bannerEdit">
|
<div :class="$style.bannerEdit">
|
||||||
<SearchMarker :keywords="['banner', 'change', 'remove']">
|
<SearchMarker :keywords="['banner', 'change', 'remove']">
|
||||||
<MkButton primary rounded @click="changeOrRemoveBanner">{{ <SearchLabel>{{ i18n.ts._profile.changeBanner }}</SearchLabel> }}</MkButton>
|
<MkButton primary rounded @click="changeOrRemoveBanner"><SearchLabel>{{ i18n.ts._profile.changeBanner }}</SearchLabel></MkButton>
|
||||||
</SearchMarker>
|
</SearchMarker>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.backgroundEdit">
|
<div :class="$style.backgroundEdit">
|
||||||
<SearchMarker :keywords="['background', 'change', 'remove']">
|
<SearchMarker :keywords="['background', 'change', 'remove']">
|
||||||
<MkButton primary rounded @click="changeOrRemoveBackground">{{ <SearchLabel>{{ i18n.ts._profile.changeBackground }}</SearchLabel> }}</MkButton>
|
<MkButton primary rounded @click="changeOrRemoveBackground"><SearchLabel>{{ i18n.ts._profile.changeBackground }}</SearchLabel></MkButton>
|
||||||
</SearchMarker>
|
</SearchMarker>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -187,6 +187,7 @@ import { claimAchievement } from '@/utility/achievements.js';
|
||||||
import { store } from '@/store.js';
|
import { store } from '@/store.js';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
|
import { globalEvents } from '@/events.js';
|
||||||
|
|
||||||
const $i = ensureSignin();
|
const $i = ensureSignin();
|
||||||
|
|
||||||
|
@ -314,7 +315,6 @@ function changeAvatar(ev) {
|
||||||
$i.avatarId = i.avatarId;
|
$i.avatarId = i.avatarId;
|
||||||
$i.avatarUrl = i.avatarUrl;
|
$i.avatarUrl = i.avatarUrl;
|
||||||
claimAchievement('profileFilled');
|
claimAchievement('profileFilled');
|
||||||
globalEvents.emit('requestClearPageCache');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +365,6 @@ function changeBackground(ev) {
|
||||||
});
|
});
|
||||||
$i.backgroundId = i.backgroundId;
|
$i.backgroundId = i.backgroundId;
|
||||||
$i.backgroundUrl = i.backgroundUrl;
|
$i.backgroundUrl = i.backgroundUrl;
|
||||||
globalEvents.emit('requestClearPageCache');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,7 +381,6 @@ function changeOrRemoveAvatar(ev) {
|
||||||
});
|
});
|
||||||
$i.avatarId = i.avatarId;
|
$i.avatarId = i.avatarId;
|
||||||
$i.avatarUrl = i.avatarUrl;
|
$i.avatarUrl = i.avatarUrl;
|
||||||
globalEvents.emit('requestClearPageCache');
|
|
||||||
},
|
},
|
||||||
}], ev.currentTarget ?? ev.target);
|
}], ev.currentTarget ?? ev.target);
|
||||||
} else {
|
} else {
|
||||||
|
@ -404,7 +402,6 @@ function changeOrRemoveBanner(ev) {
|
||||||
});
|
});
|
||||||
$i.bannerId = i.bannerId;
|
$i.bannerId = i.bannerId;
|
||||||
$i.bannerUrl = i.bannerUrl;
|
$i.bannerUrl = i.bannerUrl;
|
||||||
globalEvents.emit('requestClearPageCache');
|
|
||||||
},
|
},
|
||||||
}], ev.currentTarget ?? ev.target);
|
}], ev.currentTarget ?? ev.target);
|
||||||
} else {
|
} else {
|
||||||
|
@ -426,7 +423,6 @@ function changeOrRemoveBackground(ev) {
|
||||||
});
|
});
|
||||||
$i.backgroundId = i.backgroundId;
|
$i.backgroundId = i.backgroundId;
|
||||||
$i.backgroundUrl = i.backgroundUrl;
|
$i.backgroundUrl = i.backgroundUrl;
|
||||||
globalEvents.emit('requestClearPageCache');
|
|
||||||
},
|
},
|
||||||
}], ev.currentTarget ?? ev.target);
|
}], ev.currentTarget ?? ev.target);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -33,10 +33,10 @@ import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { useRouter } from '@/router.js';
|
import { useRouter } from '@/router.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { store } from '@/store.js';
|
||||||
import { deepMerge } from '@/utility/merge.js';
|
import { deepMerge } from '@/utility/merge.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/i.js';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
@ -50,19 +50,18 @@ const tlEl = useTemplateRef('tlEl');
|
||||||
const rootEl = useTemplateRef('rootEl');
|
const rootEl = useTemplateRef('rootEl');
|
||||||
|
|
||||||
const withRenotes = computed<boolean>({
|
const withRenotes = computed<boolean>({
|
||||||
get: () => defaultStore.reactiveState.tl.value.filter.withRenotes,
|
get: () => store.r.tl.value.filter.withRenotes,
|
||||||
set: (x) => saveTlFilter('withRenotes', x),
|
set: (x) => saveTlFilter('withRenotes', x),
|
||||||
});
|
});
|
||||||
|
|
||||||
const onlyFiles = computed<boolean>({
|
const onlyFiles = computed<boolean>({
|
||||||
get: () => defaultStore.reactiveState.tl.value.filter.onlyFiles,
|
get: () => store.r.tl.value.filter.onlyFiles,
|
||||||
set: (x) => saveTlFilter('onlyFiles', x),
|
set: (x) => saveTlFilter('onlyFiles', x),
|
||||||
});
|
});
|
||||||
|
|
||||||
function saveTlFilter(key: keyof typeof defaultStore.state.tl.filter, newValue: boolean) {
|
function saveTlFilter(key: keyof typeof store.s.tl.filter, newValue: boolean) {
|
||||||
if (key !== 'withReplies' || $i) {
|
if (key !== 'withReplies' || $i) {
|
||||||
const out = deepMerge({ filter: { [key]: newValue } }, defaultStore.state.tl);
|
store.r.tl.value = deepMerge({ filter: { [key]: newValue } }, store.s.tl);
|
||||||
defaultStore.set('tl', out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -175,7 +175,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div>{{ i18n.ts.noNotes }}</div>
|
<div>{{ i18n.ts.noNotes }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="_panel">
|
<div v-else class="_panel">
|
||||||
<MkNote v-for="note of user.pinnedNotes" :key="note.id" class="note" :class="$style.pinnedNote" :note="note" :pinned="true"/>
|
<DynamicNote v-for="note of user.pinnedNotes" :key="note.id" class="note" :class="$style.pinnedNote" :note="note" :pinned="true"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkNotes v-else :class="$style.tl" :noGap="true" :pagination="AllPagination"/>
|
<MkNotes v-else :class="$style.tl" :noGap="true" :pagination="AllPagination"/>
|
||||||
|
@ -221,12 +221,7 @@ import { getStaticImageUrl } from '@/utility/media-proxy.js';
|
||||||
import { infoImageUrl } from '@/instance.js';
|
import { infoImageUrl } from '@/instance.js';
|
||||||
import MkSparkle from '@/components/MkSparkle.vue';
|
import MkSparkle from '@/components/MkSparkle.vue';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
|
import DynamicNote from '@/components/DynamicNote.vue';
|
||||||
const MkNote = defineAsyncComponent(() =>
|
|
||||||
defaultStore.state.noteDesign === 'sharkey'
|
|
||||||
? import('@/components/SkNote.vue')
|
|
||||||
: import('@/components/MkNote.vue'),
|
|
||||||
);
|
|
||||||
|
|
||||||
function calcAge(birthdate: string): number {
|
function calcAge(birthdate: string): number {
|
||||||
const date = new Date(birthdate);
|
const date = new Date(birthdate);
|
||||||
|
@ -277,7 +272,7 @@ const listenbrainzdata = ref(false);
|
||||||
if (props.user.listenbrainz) {
|
if (props.user.listenbrainz) {
|
||||||
(async function() {
|
(async function() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`https://api.listenbrainz.org/1/user/${props.user.listenbrainz}/playing-now`, {
|
const response = await window.fetch(`https://api.listenbrainz.org/1/user/${props.user.listenbrainz}/playing-now`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
@ -295,7 +290,7 @@ if (props.user.listenbrainz) {
|
||||||
|
|
||||||
const background = computed(() => {
|
const background = computed(() => {
|
||||||
if (props.user.backgroundUrl == null) return {};
|
if (props.user.backgroundUrl == null) return {};
|
||||||
if (defaultStore.state.disableShowingAnimatedImages) {
|
if (prefer.s.disableShowingAnimatedImages) {
|
||||||
return {
|
return {
|
||||||
'--backgroundImageStatic': `url('${getStaticImageUrl(props.user.backgroundUrl)}')`,
|
'--backgroundImageStatic': `url('${getStaticImageUrl(props.user.backgroundUrl)}')`,
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div>{{ i18n.ts.noNotes }}</div>
|
<div>{{ i18n.ts.noNotes }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="_panel">
|
<div v-else class="_panel">
|
||||||
<MkNote v-for="note of user.pinnedNotes" :key="note.id" class="note" :class="$style.pinnedNote" :note="note" :pinned="true"/>
|
<DynamicNote v-for="note of user.pinnedNotes" :key="note.id" class="note" :class="$style.pinnedNote" :note="note" :pinned="true"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkNotes v-else :noGap="true" :pagination="pagination" :class="$style.tl"/>
|
<MkNotes v-else :noGap="true" :pagination="pagination" :class="$style.tl"/>
|
||||||
|
@ -28,19 +28,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, defineAsyncComponent } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkNotes from '@/components/MkNotes.vue';
|
import MkNotes from '@/components/MkNotes.vue';
|
||||||
import MkTab from '@/components/MkTab.vue';
|
import MkTab from '@/components/MkTab.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { infoImageUrl } from '@/instance.js';
|
import { infoImageUrl } from '@/instance.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import DynamicNote from '@/components/DynamicNote.vue';
|
||||||
|
|
||||||
const MkNote = defineAsyncComponent(() =>
|
|
||||||
defaultStore.state.noteDesign === 'sharkey'
|
|
||||||
? import('@/components/SkNote.vue')
|
|
||||||
: import('@/components/MkNote.vue'),
|
|
||||||
);
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
user: Misskey.entities.UserDetailed;
|
user: Misskey.entities.UserDetailed;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import type { DeckProfile } from '@/deck.js';
|
import type { DeckProfile } from '@/deck.js';
|
||||||
|
import type { Theme } from '@/theme.js';
|
||||||
import { ColdDeviceStorage, store } from '@/store.js';
|
import { ColdDeviceStorage, store } from '@/store.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
@ -14,7 +15,7 @@ import { unisonReload } from '@/utility/unison-reload.js';
|
||||||
// TODO: そのうち消す
|
// TODO: そのうち消す
|
||||||
export function migrateOldSettings() {
|
export function migrateOldSettings() {
|
||||||
store.loaded.then(async () => {
|
store.loaded.then(async () => {
|
||||||
const themes = await misskeyApi('i/registry/get', { scope: ['client'], key: 'themes' }).catch(() => []);
|
const themes = await misskeyApi('i/registry/get', { scope: ['client'], key: 'themes' }).catch(() => []) as Theme[];
|
||||||
if (themes.length > 0) {
|
if (themes.length > 0) {
|
||||||
prefer.commit('themes', themes);
|
prefer.commit('themes', themes);
|
||||||
}
|
}
|
||||||
|
@ -133,6 +134,23 @@ export function migrateOldSettings() {
|
||||||
prefer.commit('sound.on.reaction', store.s.sound_reaction as any);
|
prefer.commit('sound.on.reaction', store.s.sound_reaction as any);
|
||||||
prefer.commit('defaultNoteVisibility', store.s.defaultNoteVisibility);
|
prefer.commit('defaultNoteVisibility', store.s.defaultNoteVisibility);
|
||||||
prefer.commit('defaultNoteLocalOnly', store.s.defaultNoteLocalOnly);
|
prefer.commit('defaultNoteLocalOnly', store.s.defaultNoteLocalOnly);
|
||||||
|
// Sharkey migrations
|
||||||
|
prefer.commit('collapseNotesRepliedTo', store.s.collapseNotesRepliedTo);
|
||||||
|
prefer.commit('collapseFiles', store.s.collapseFiles);
|
||||||
|
prefer.commit('uncollapseCW', store.s.uncollapseCW);
|
||||||
|
prefer.commit('expandLongNote', store.s.expandLongNote);
|
||||||
|
prefer.commit('like', store.s.like);
|
||||||
|
prefer.commit('autoloadConversation', store.s.autoloadConversation);
|
||||||
|
prefer.commit('showVisibilitySelectorOnBoost', store.s.showVisibilitySelectorOnBoost);
|
||||||
|
prefer.commit('visibilityOnBoost', store.s.visibilityOnBoost);
|
||||||
|
prefer.commit('trustedDomains', store.s.trustedDomains);
|
||||||
|
prefer.commit('warnExternalUrl', store.s.warnExternalUrl);
|
||||||
|
prefer.commit('followingFeed', store.s.followingFeed);
|
||||||
|
prefer.commit('warnMissingAltText', store.s.warnMissingAltText);
|
||||||
|
prefer.commit('disableCatSpeak', store.s.disableCatSpeak);
|
||||||
|
prefer.commit('showTickerOnReplies', store.s.showTickerOnReplies);
|
||||||
|
prefer.commit('searchEngine', store.s.searchEngine);
|
||||||
|
prefer.commit('noteDesign', store.s.noteDesign);
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
unisonReload();
|
unisonReload();
|
||||||
|
|
|
@ -11,7 +11,10 @@ import type { Plugin } from '@/plugin.js';
|
||||||
import type { DeviceKind } from '@/utility/device-kind.js';
|
import type { DeviceKind } from '@/utility/device-kind.js';
|
||||||
import type { DeckProfile } from '@/deck.js';
|
import type { DeckProfile } from '@/deck.js';
|
||||||
import type { PreferencesDefinition } from './manager.js';
|
import type { PreferencesDefinition } from './manager.js';
|
||||||
|
import type { FollowingFeedState } from '@/utility/following-feed-utils.js';
|
||||||
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
|
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
|
||||||
|
import { searchEngineMap } from '@/utility/search-engine-map.js';
|
||||||
|
import { defaultFollowingFeedState } from '@/utility/following-feed-utils.js';
|
||||||
|
|
||||||
/** サウンド設定 */
|
/** サウンド設定 */
|
||||||
export type SoundStore = {
|
export type SoundStore = {
|
||||||
|
@ -125,22 +128,68 @@ export const PREF_DEF = {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
collapseRenotes: {
|
collapseRenotes: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
collapseNotesRepliedTo: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
collapseFiles: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
uncollapseCW: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
expandLongNote: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
like: {
|
||||||
|
default: null as string | null,
|
||||||
|
},
|
||||||
|
autoloadConversation: {
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
showVisibilitySelectorOnBoost: {
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
visibilityOnBoost: {
|
||||||
|
default: 'public' as 'public' | 'home' | 'followers',
|
||||||
|
},
|
||||||
|
trustedDomains: {
|
||||||
|
default: [] as string[],
|
||||||
|
},
|
||||||
|
warnExternalUrl: {
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
followingFeed: {
|
||||||
|
default: defaultFollowingFeedState as Partial<FollowingFeedState>,
|
||||||
|
},
|
||||||
|
warnMissingAltText: {
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
disableCatSpeak: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
showTickerOnReplies: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
searchEngine: {
|
||||||
|
default: Object.keys(searchEngineMap)[0],
|
||||||
|
},
|
||||||
|
noteDesign: {
|
||||||
|
default: 'sharkey' as 'sharkey' | 'misskey',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
default: [
|
default: [
|
||||||
'notifications',
|
'notifications',
|
||||||
'clips',
|
|
||||||
'drive',
|
|
||||||
'followRequests',
|
|
||||||
'chat',
|
|
||||||
'-',
|
|
||||||
'explore',
|
'explore',
|
||||||
|
'followRequests',
|
||||||
|
'-',
|
||||||
'announcements',
|
'announcements',
|
||||||
'channels',
|
|
||||||
'search',
|
'search',
|
||||||
'-',
|
'-',
|
||||||
'ui',
|
'favorites',
|
||||||
|
'drive',
|
||||||
|
'achievements',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
statusbars: {
|
statusbars: {
|
||||||
|
@ -154,7 +203,7 @@ export const PREF_DEF = {
|
||||||
}[],
|
}[],
|
||||||
},
|
},
|
||||||
serverDisconnectedBehavior: {
|
serverDisconnectedBehavior: {
|
||||||
default: 'quiet' as 'quiet' | 'reload' | 'dialog',
|
default: 'quiet' as 'quiet' | 'disabled' | 'dialog',
|
||||||
},
|
},
|
||||||
nsfw: {
|
nsfw: {
|
||||||
default: 'respect' as 'respect' | 'force' | 'ignore',
|
default: 'respect' as 'respect' | 'force' | 'ignore',
|
||||||
|
@ -232,7 +281,7 @@ export const PREF_DEF = {
|
||||||
default: 'auto' as 'auto' | 'popup' | 'drawer',
|
default: 'auto' as 'auto' | 'popup' | 'drawer',
|
||||||
},
|
},
|
||||||
squareAvatars: {
|
squareAvatars: {
|
||||||
default: false,
|
default: true,
|
||||||
},
|
},
|
||||||
showAvatarDecorations: {
|
showAvatarDecorations: {
|
||||||
default: true,
|
default: true,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { apiUrl } from '@@/js/config.js';
|
import { apiUrl } from '@@/js/config.js';
|
||||||
import { defaultMemoryStorage } from '@/memory-storage';
|
import { defaultMemoryStorage } from '@/memory-storage.js';
|
||||||
import { waiting } from '@/os.js';
|
import { waiting } from '@/os.js';
|
||||||
import { unisonReload, reloadChannel } from '@/utility/unison-reload.js';
|
import { unisonReload, reloadChannel } from '@/utility/unison-reload.js';
|
||||||
import { $i } from '@/i.js';
|
import { $i } from '@/i.js';
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
import { markRaw, ref } from 'vue';
|
import { markRaw, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { searchEngineMap } from '@/utility/search-engine-map.js';
|
|
||||||
import lightTheme from '@@/themes/l-light.json5';
|
import lightTheme from '@@/themes/l-light.json5';
|
||||||
import darkTheme from '@@/themes/d-green-lime.json5';
|
import darkTheme from '@@/themes/d-green-lime.json5';
|
||||||
import { hemisphere } from '@@/js/intl-const.js';
|
import { hemisphere } from '@@/js/intl-const.js';
|
||||||
|
@ -14,7 +13,8 @@ import type { Plugin } from '@/plugin.js';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { Pizzax } from '@/lib/pizzax.js';
|
import { Pizzax } from '@/lib/pizzax.js';
|
||||||
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
|
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
|
||||||
import { defaultFollowingFeedState } from '@/utility/following-feed-utils.js';
|
import { defaultFollowingFeedState, type FollowingFeedState } from '@/utility/following-feed-utils';
|
||||||
|
import { searchEngineMap } from '@/utility/search-engine-map.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 「状態」を管理するストア(not「設定」)
|
* 「状態」を管理するストア(not「設定」)
|
||||||
|
@ -134,22 +134,6 @@ export const store = markRaw(new Pizzax('base', {
|
||||||
where: 'account',
|
where: 'account',
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
collapseNotesRepliedTo: {
|
|
||||||
where: 'account',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
collapseFiles: {
|
|
||||||
where: 'account',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
uncollapseCW: {
|
|
||||||
where: 'account',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
expandLongNote: {
|
|
||||||
where: 'device',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
widgets: {
|
widgets: {
|
||||||
where: 'account',
|
where: 'account',
|
||||||
default: [] as {
|
default: [] as {
|
||||||
|
@ -195,30 +179,6 @@ export const store = markRaw(new Pizzax('base', {
|
||||||
where: 'account',
|
where: 'account',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
like: {
|
|
||||||
where: 'account',
|
|
||||||
default: null as string | null,
|
|
||||||
},
|
|
||||||
autoloadConversation: {
|
|
||||||
where: 'account',
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
showVisibilitySelectorOnBoost: {
|
|
||||||
where: 'account',
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
visibilityOnBoost: {
|
|
||||||
where: 'account',
|
|
||||||
default: 'public' as 'public' | 'home' | 'followers',
|
|
||||||
},
|
|
||||||
trustedDomains: {
|
|
||||||
where: 'account',
|
|
||||||
default: [] as string[],
|
|
||||||
},
|
|
||||||
warnExternalUrl: {
|
|
||||||
where: 'account',
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
menu: {
|
menu: {
|
||||||
where: 'deviceAccount',
|
where: 'deviceAccount',
|
||||||
default: [
|
default: [
|
||||||
|
@ -249,10 +209,6 @@ export const store = markRaw(new Pizzax('base', {
|
||||||
where: 'deviceAccount',
|
where: 'deviceAccount',
|
||||||
default: [] as Misskey.entities.UserList[],
|
default: [] as Misskey.entities.UserList[],
|
||||||
},
|
},
|
||||||
followingFeed: {
|
|
||||||
where: 'account',
|
|
||||||
default: defaultFollowingFeedState,
|
|
||||||
},
|
|
||||||
serverDisconnectedBehavior: {
|
serverDisconnectedBehavior: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: 'disabled' as 'quiet' | 'dialog' | 'disabled',
|
default: 'disabled' as 'quiet' | 'dialog' | 'disabled',
|
||||||
|
@ -289,14 +245,6 @@ export const store = markRaw(new Pizzax('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
warnMissingAltText: {
|
|
||||||
where: 'device',
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
enableFaviconNotificationDot: {
|
|
||||||
where: 'device',
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
imageNewTab: {
|
imageNewTab: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -305,10 +253,6 @@ export const store = markRaw(new Pizzax('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
|
||||||
|
@ -333,18 +277,6 @@ export const store = markRaw(new Pizzax('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
showTickerOnReplies: {
|
|
||||||
where: 'device',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
searchEngine: {
|
|
||||||
where: 'account',
|
|
||||||
default: Object.keys(searchEngineMap)[0],
|
|
||||||
},
|
|
||||||
noteDesign: {
|
|
||||||
where: 'device',
|
|
||||||
default: 'sharkey' as 'sharkey' | 'misskey',
|
|
||||||
},
|
|
||||||
enableInfiniteScroll: {
|
enableInfiniteScroll: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: true,
|
default: true,
|
||||||
|
@ -393,10 +325,6 @@ export const store = markRaw(new Pizzax('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: 3,
|
default: 3,
|
||||||
},
|
},
|
||||||
numberOfReplies: {
|
|
||||||
where: 'device',
|
|
||||||
default: 5,
|
|
||||||
},
|
|
||||||
showNoteActionsOnlyHover: {
|
showNoteActionsOnlyHover: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -417,14 +345,6 @@ export const store = markRaw(new Pizzax('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
oneko: {
|
|
||||||
where: 'device',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
clickToOpen: {
|
|
||||||
where: 'device',
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
aiChanMode: {
|
aiChanMode: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -445,10 +365,6 @@ export const store = markRaw(new Pizzax('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: 'horizontal' as 'vertical' | 'horizontal',
|
default: 'horizontal' as 'vertical' | 'horizontal',
|
||||||
},
|
},
|
||||||
notificationClickable: {
|
|
||||||
where: 'device',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
enableCondensedLine: {
|
enableCondensedLine: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: true,
|
default: true,
|
||||||
|
@ -553,7 +469,95 @@ export const store = markRaw(new Pizzax('base', {
|
||||||
sfxVolume: 1,
|
sfxVolume: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
collapseNotesRepliedTo: {
|
||||||
|
where: 'device',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
collapseFiles: {
|
||||||
|
where: 'device',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
uncollapseCW: {
|
||||||
|
where: 'device',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
expandLongNote: {
|
||||||
|
where: 'device',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
like: {
|
||||||
|
where: 'device',
|
||||||
|
default: null as string | null,
|
||||||
|
},
|
||||||
|
autoloadConversation: {
|
||||||
|
where: 'device',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
showVisibilitySelectorOnBoost: {
|
||||||
|
where: 'device',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
visibilityOnBoost: {
|
||||||
|
where: 'device',
|
||||||
|
default: 'public' as 'public' | 'home' | 'followers',
|
||||||
|
},
|
||||||
|
trustedDomains: {
|
||||||
|
where: 'device',
|
||||||
|
default: [] as string[],
|
||||||
|
},
|
||||||
|
warnExternalUrl: {
|
||||||
|
where: 'device',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
followingFeed: {
|
||||||
|
where: 'device',
|
||||||
|
default: defaultFollowingFeedState as Partial<FollowingFeedState>,
|
||||||
|
},
|
||||||
|
warnMissingAltText: {
|
||||||
|
where: 'device',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
disableCatSpeak: {
|
||||||
|
where: 'device',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
showTickerOnReplies: {
|
||||||
|
where: 'device',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
searchEngine: {
|
||||||
|
where: 'device',
|
||||||
|
default: Object.keys(searchEngineMap)[0],
|
||||||
|
},
|
||||||
|
noteDesign: {
|
||||||
|
where: 'device',
|
||||||
|
default: 'sharkey' as 'sharkey' | 'misskey',
|
||||||
|
},
|
||||||
//#endregion
|
//#endregion
|
||||||
|
oneko: {
|
||||||
|
where: 'device',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
enableFaviconNotificationDot: {
|
||||||
|
where: 'device',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
numberOfReplies: {
|
||||||
|
where: 'device',
|
||||||
|
default: 5,
|
||||||
|
},
|
||||||
|
notificationClickable: {
|
||||||
|
where: 'device',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
cornerRadius: {
|
||||||
|
where: 'device',
|
||||||
|
default: null as 'Misskey' | null,
|
||||||
|
},
|
||||||
|
clickToOpen: {
|
||||||
|
where: 'device',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
|
|
@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<div v-if="$i && $i.isBot" id="botWarn"><span>{{ i18n.ts.loggedInAsBot }}</span></div>
|
<div v-if="$i && $i.isBot" id="botWarn"><span>{{ i18n.ts.loggedInAsBot }}</span></div>
|
||||||
|
|
||||||
<SkOneko v-if="defaultStore.state.oneko"/>
|
<SkOneko v-if="store.r.oneko"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -65,6 +65,7 @@ import { useStream } from '@/stream.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
import { globalEvents } from '@/events.js';
|
import { globalEvents } from '@/events.js';
|
||||||
|
import { store } from '@/store.js';
|
||||||
|
|
||||||
const SkOneko = defineAsyncComponent(() => import('@/components/SkOneko.vue'));
|
const SkOneko = defineAsyncComponent(() => import('@/components/SkOneko.vue'));
|
||||||
|
|
||||||
|
@ -108,7 +109,7 @@ if ($i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPointerEvents() {
|
function getPointerEvents() {
|
||||||
return defaultStore.state.notificationClickable ? 'all' : 'none';
|
return store.s.notificationClickable ? 'all' : 'none';
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { onUnmounted } from 'vue';
|
import { onUnmounted } from 'vue';
|
||||||
import type { Ref, ShallowRef } from 'vue';
|
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import type { Ref, ShallowRef } from 'vue';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
import { $i } from '@/i.js';
|
import { $i } from '@/i.js';
|
||||||
import { misskeyApi } from './misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
|
||||||
export function useNoteCapture(props: {
|
export function useNoteCapture(props: {
|
||||||
rootEl: ShallowRef<HTMLElement | undefined>;
|
rootEl: ShallowRef<HTMLElement | undefined>;
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ref, Ref, computed, ComputedRef } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import type { Ref, ComputedRef } from 'vue';
|
||||||
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { prefer } from '@/preferences';
|
||||||
import { MenuItem } from '@/types/menu.js';
|
import { store } from '@/store.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
this script should eventually contain all Sharkey-specific bits of
|
this script should eventually contain all Sharkey-specific bits of
|
||||||
|
@ -30,7 +32,7 @@ export function visibilityIsAtLeast(a: Visibility | string, b: Visibility | stri
|
||||||
}
|
}
|
||||||
|
|
||||||
export function boostMenuItems(appearNote: Ref<Misskey.entities.Note>, renote: (v: Visibility, l: boolean) => void): MenuItem[] {
|
export function boostMenuItems(appearNote: Ref<Misskey.entities.Note>, renote: (v: Visibility, l: boolean) => void): MenuItem[] {
|
||||||
const localOnly = ref(defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
|
const localOnly = ref(prefer.s.rememberNoteVisibility ? store.s.localOnly : prefer.s.defaultNoteLocalOnly);
|
||||||
const effectiveVisibility = (
|
const effectiveVisibility = (
|
||||||
appearNote.value.channel?.isSensitive
|
appearNote.value.channel?.isSensitive
|
||||||
? smallerVisibility(appearNote.value.visibility, 'home')
|
? smallerVisibility(appearNote.value.visibility, 'home')
|
||||||
|
@ -83,7 +85,7 @@ export function boostMenuItems(appearNote: Ref<Misskey.entities.Note>, renote: (
|
||||||
export function computeRenoteTooltip(renoted: Ref<boolean>): ComputedRef<string> {
|
export function computeRenoteTooltip(renoted: Ref<boolean>): ComputedRef<string> {
|
||||||
return computed(() => {
|
return computed(() => {
|
||||||
if (renoted.value) return i18n.ts.unrenote;
|
if (renoted.value) return i18n.ts.unrenote;
|
||||||
if (defaultStore.state.showVisibilitySelectorOnBoost) return i18n.ts.renote;
|
if (prefer.s.showVisibilitySelectorOnBoost) return i18n.ts.renote;
|
||||||
return i18n.ts.renoteShift;
|
return i18n.ts.renoteShift;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type JsonLike = string | number | boolean | null | undefined | JsonLike[] | { [key: string]: JsonLike } | Map<string, JsonLike>;
|
type JsonLike = string | number | boolean | null | undefined | JsonLike[] | { [key: string]: JsonLike | undefined } | Map<string, JsonLike>;
|
||||||
|
|
||||||
export function deepEqual(a: JsonLike, b: JsonLike): boolean {
|
export function deepEqual(a: JsonLike, b: JsonLike): boolean {
|
||||||
if (a === b) return true;
|
if (a === b) return true;
|
||||||
|
|
|
@ -10,7 +10,7 @@ import type { MenuItem } from '@/types/menu.js';
|
||||||
import { deepMerge } from '@/utility/merge.js';
|
import { deepMerge } from '@/utility/merge.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { popupMenu } from '@/os.js';
|
import { popupMenu } from '@/os.js';
|
||||||
import { prefer } from '@/preferences';
|
import { prefer } from '@/preferences.js';
|
||||||
|
|
||||||
export const followingTab = 'following' as const;
|
export const followingTab = 'following' as const;
|
||||||
export const mutualsTab = 'mutuals' as const;
|
export const mutualsTab = 'mutuals' as const;
|
||||||
|
@ -37,7 +37,7 @@ export type FollowingFeedModel = {
|
||||||
[Key in keyof FollowingFeedState]: WritableComputedRef<FollowingFeedState[Key]>;
|
[Key in keyof FollowingFeedState]: WritableComputedRef<FollowingFeedState[Key]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface FollowingFeedState {
|
export type FollowingFeedState = {
|
||||||
withNonPublic: boolean,
|
withNonPublic: boolean,
|
||||||
withQuotes: boolean,
|
withQuotes: boolean,
|
||||||
withBots: boolean,
|
withBots: boolean,
|
||||||
|
@ -45,7 +45,7 @@ export interface FollowingFeedState {
|
||||||
onlyFiles: boolean,
|
onlyFiles: boolean,
|
||||||
userList: FollowingFeedTab,
|
userList: FollowingFeedTab,
|
||||||
remoteWarningDismissed: boolean,
|
remoteWarningDismissed: boolean,
|
||||||
}
|
};
|
||||||
|
|
||||||
export const defaultFollowingFeedState: FollowingFeedState = {
|
export const defaultFollowingFeedState: FollowingFeedState = {
|
||||||
withNonPublic: false,
|
withNonPublic: false,
|
||||||
|
@ -169,9 +169,9 @@ export function createModel(storage?: Ref<StorageInterface>): FollowingFeedModel
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDefaultStorage() {
|
function createDefaultStorage(): Ref<StorageInterface> {
|
||||||
return computed(() => ({
|
return computed(() => ({
|
||||||
state: prefer.s.followingFeed,
|
state: prefer.r.followingFeed,
|
||||||
save(updated: typeof prefer.s.followingFeed) {
|
save(updated: typeof prefer.s.followingFeed) {
|
||||||
prefer.s.followingFeed = updated;
|
prefer.s.followingFeed = updated;
|
||||||
},
|
},
|
||||||
|
|
|
@ -196,7 +196,7 @@ export function getNoteMenu(props: {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 60 && appearNote.userId === $i.id) {
|
if (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 60 && appearNote.userId === $i?.id) {
|
||||||
claimAchievement('noteDeletedWithin1min');
|
claimAchievement('noteDeletedWithin1min');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -541,7 +541,7 @@ export function getNoteMenu(props: {
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
menu: menuItems,
|
popupMenu: menuItems,
|
||||||
cleanup,
|
cleanup,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,19 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Ref, defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { i18n } from '@/i18n.js';
|
|
||||||
import * as os from '@/os.js';
|
|
||||||
import { misskeyApi } from './misskey-api.js';
|
import { misskeyApi } from './misskey-api.js';
|
||||||
import { MenuItem } from '@/types/menu.js';
|
|
||||||
import { dateTimeFormat } from './intl-const.js';
|
import { dateTimeFormat } from './intl-const.js';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
|
||||||
|
interface NoteEdit {
|
||||||
|
oldDate: string;
|
||||||
|
updatedAt: string;
|
||||||
|
text: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getNoteVersionsMenu(props: {
|
export async function getNoteVersionsMenu(props: {
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -18,7 +24,7 @@ export async function getNoteVersionsMenu(props: {
|
||||||
const isRenote = (
|
const isRenote = (
|
||||||
props.note.renote != null &&
|
props.note.renote != null &&
|
||||||
props.note.text == null &&
|
props.note.text == null &&
|
||||||
props.note.fileIds.length === 0 &&
|
!props.note.fileIds?.length &&
|
||||||
props.note.poll == null
|
props.note.poll == null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -26,11 +32,11 @@ export async function getNoteVersionsMenu(props: {
|
||||||
|
|
||||||
const cleanups = [] as (() => void)[];
|
const cleanups = [] as (() => void)[];
|
||||||
|
|
||||||
function openVersion(info): void {
|
function openVersion(info: NoteEdit): void {
|
||||||
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/SkOldNoteWindow.vue')), {
|
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/SkOldNoteWindow.vue')), {
|
||||||
note: appearNote,
|
note: appearNote,
|
||||||
oldText: info.text,
|
oldText: info.text ?? '',
|
||||||
updatedAt: info.oldDate ? info.oldDate : info.updatedAt,
|
updatedAt: info.updatedAt,
|
||||||
}, {
|
}, {
|
||||||
closed: () => dispose(),
|
closed: () => dispose(),
|
||||||
});
|
});
|
||||||
|
@ -43,9 +49,7 @@ export async function getNoteVersionsMenu(props: {
|
||||||
|
|
||||||
await statePromise.then((versions) => {
|
await statePromise.then((versions) => {
|
||||||
for (const edit of versions) {
|
for (const edit of versions) {
|
||||||
const _time = edit.oldDate == null ? NaN :
|
const _time = new Date(edit.oldDate).getTime();
|
||||||
typeof edit.oldDate === 'number' ? edit.oldDate :
|
|
||||||
(edit.oldDate instanceof Date ? edit.oldDate : new Date(edit.oldDate)).getTime();
|
|
||||||
|
|
||||||
menu.push({
|
menu.push({
|
||||||
icon: 'ph-pencil-simple ph-bold ph-lg',
|
icon: 'ph-pencil-simple ph-bold ph-lg',
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { deepClone } from './clone.js';
|
import { deepClone } from './clone.js';
|
||||||
import type { Cloneable } from './clone.js';
|
import type { Cloneable } from './clone.js';
|
||||||
|
|
||||||
export type DeepPartial<T> = {
|
export type DeepPartial<T> = T | {
|
||||||
[P in keyof T]?: T[P] extends Record<PropertyKey, unknown> ? DeepPartial<T[P]> : T[P];
|
[P in keyof T]?: T[P] extends Record<PropertyKey, unknown> ? DeepPartial<T[P]> : T[P];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ function isPureObject(value: unknown): value is Record<PropertyKey, unknown> {
|
||||||
* valueにないキーをdefからもらう(再帰的)\
|
* valueにないキーをdefからもらう(再帰的)\
|
||||||
* nullはそのまま、undefinedはdefの値
|
* nullはそのまま、undefinedはdefの値
|
||||||
**/
|
**/
|
||||||
export function deepMerge<X extends object | Record<PropertyKey, unknown>>(value: DeepPartial<X>, def: X): X {
|
export function deepMerge<X extends Record<PropertyKey, unknown>>(value: DeepPartial<X>, def: X): X {
|
||||||
if (isPureObject(value) && isPureObject(def)) {
|
if (isPureObject(value) && isPureObject(def)) {
|
||||||
const result = deepClone(value as Cloneable) as X;
|
const result = deepClone(value as Cloneable) as X;
|
||||||
for (const [k, v] of Object.entries(def) as [keyof X, X[keyof X]][]) {
|
for (const [k, v] of Object.entries(def) as [keyof X, X[keyof X]][]) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue