Merge remote-tracking branch 'misskey-original/develop' into develop
# Conflicts: # packages/frontend/src/components/MkPostForm.vue
This commit is contained in:
commit
88122a3d9d
58 changed files with 632 additions and 175 deletions
|
|
@ -29,7 +29,7 @@
|
|||
"@twemoji/parser": "15.0.0",
|
||||
"@vitejs/plugin-vue": "5.0.4",
|
||||
"@vue/compiler-sfc": "3.4.21",
|
||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
|
||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.4",
|
||||
"astring": "1.8.6",
|
||||
"broadcast-channel": "7.0.0",
|
||||
"buraha": "0.0.1",
|
||||
|
|
|
|||
|
|
@ -4,37 +4,59 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
-->
|
||||
|
||||
<template>
|
||||
<div :class="$style.root" class="_panel">
|
||||
<b>{{ clip.name }}</b>
|
||||
<div v-if="clip.description" :class="$style.description">{{ clip.description }}</div>
|
||||
<div v-if="clip.lastClippedAt">{{ i18n.ts.updatedAt }}: <MkTime :time="clip.lastClippedAt" mode="detail"/></div>
|
||||
<div :class="$style.user">
|
||||
<MkAvatar :user="clip.user" :class="$style.userAvatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
|
||||
<MkA :to="`/clips/${clip.id}`" :class="$style.link">
|
||||
<div :class="$style.root" class="_panel _gaps_s">
|
||||
<b>{{ clip.name }}</b>
|
||||
<div :class="$style.description">
|
||||
<div v-if="clip.description"><Mfm :text="clip.description" :plain="true" :nowrap="true"/></div>
|
||||
<div v-if="clip.lastClippedAt">{{ i18n.ts.updatedAt }}: <MkTime :time="clip.lastClippedAt" mode="detail"/></div>
|
||||
<div v-if="clip.notesCount != null">{{ i18n.ts.notesCount }}: {{ number(clip.notesCount) }} / {{ $i?.policies.noteEachClipsLimit }} ({{ i18n.tsx.remainingN({ n: remaining }) }})</div>
|
||||
</div>
|
||||
<div :class="$style.divider"></div>
|
||||
<div>
|
||||
<MkAvatar :user="clip.user" :class="$style.userAvatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkA>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { computed } from 'vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { $i } from '@/account.js';
|
||||
import number from '@/filters/number.js';
|
||||
|
||||
defineProps<{
|
||||
clip: any;
|
||||
const props = defineProps<{
|
||||
clip: Misskey.entities.Clip;
|
||||
}>();
|
||||
|
||||
const remaining = computed(() => {
|
||||
return ($i?.policies && props.clip.notesCount != null) ? ($i.policies.noteEachClipsLimit - props.clip.notesCount) : i18n.ts.unknown;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
.link {
|
||||
display: block;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: var(--accent);
|
||||
}
|
||||
}
|
||||
|
||||
.root {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.description {
|
||||
padding: 8px 0;
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: var(--divider);
|
||||
}
|
||||
|
||||
.user {
|
||||
padding-top: 16px;
|
||||
border-top: solid 0.5px var(--divider);
|
||||
.description {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.userAvatar {
|
||||
|
|
|
|||
|
|
@ -47,12 +47,12 @@ onMounted(() => {
|
|||
const width = rootEl.value!.offsetWidth;
|
||||
const height = rootEl.value!.offsetHeight;
|
||||
|
||||
if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
||||
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
|
||||
if (left + width - window.scrollX >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
||||
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.scrollX;
|
||||
}
|
||||
|
||||
if (top + height - window.pageYOffset >= (window.innerHeight - SCROLLBAR_THICKNESS)) {
|
||||
top = (window.innerHeight - SCROLLBAR_THICKNESS) - height + window.pageYOffset;
|
||||
if (top + height - window.scrollY >= (window.innerHeight - SCROLLBAR_THICKNESS)) {
|
||||
top = (window.innerHeight - SCROLLBAR_THICKNESS) - height + window.scrollY;
|
||||
}
|
||||
|
||||
if (top < 0) {
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ function onKeydown(evt: KeyboardEvent) {
|
|||
}
|
||||
|
||||
function onInputKeydown(evt: KeyboardEvent) {
|
||||
if (evt.key === 'Enter') {
|
||||
if (evt.key === 'Enter' && okButtonDisabledReason.value === null) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
ok();
|
||||
|
|
|
|||
|
|
@ -175,8 +175,8 @@ const align = () => {
|
|||
let left;
|
||||
let top;
|
||||
|
||||
const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
|
||||
const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
|
||||
const x = srcRect.left + (fixed.value ? 0 : window.scrollX);
|
||||
const y = srcRect.top + (fixed.value ? 0 : window.scrollY);
|
||||
|
||||
if (props.anchor.x === 'center') {
|
||||
left = x + (props.src.offsetWidth / 2) - (width / 2);
|
||||
|
|
@ -220,24 +220,24 @@ const align = () => {
|
|||
}
|
||||
} else {
|
||||
// 画面から横にはみ出る場合
|
||||
if (left + width - window.pageXOffset > (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
||||
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset - 1;
|
||||
if (left + width - window.scrollX > (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
||||
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.scrollX - 1;
|
||||
}
|
||||
|
||||
const underSpace = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - (top - window.pageYOffset);
|
||||
const underSpace = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - (top - window.scrollY);
|
||||
const upperSpace = (srcRect.top - MARGIN);
|
||||
|
||||
// 画面から縦にはみ出る場合
|
||||
if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
|
||||
if (top + height - window.scrollY > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
|
||||
if (props.noOverlap && props.anchor.x === 'center') {
|
||||
if (underSpace >= (upperSpace / 3)) {
|
||||
maxHeight.value = underSpace;
|
||||
} else {
|
||||
maxHeight.value = upperSpace;
|
||||
top = window.pageYOffset + ((upperSpace + MARGIN) - height);
|
||||
top = window.scrollY + ((upperSpace + MARGIN) - height);
|
||||
}
|
||||
} else {
|
||||
top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1;
|
||||
top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.scrollY - 1;
|
||||
}
|
||||
} else {
|
||||
maxHeight.value = underSpace;
|
||||
|
|
@ -255,15 +255,15 @@ const align = () => {
|
|||
let transformOriginX = 'center';
|
||||
let transformOriginY = 'center';
|
||||
|
||||
if (top >= srcRect.top + props.src.offsetHeight + (fixed.value ? 0 : window.pageYOffset)) {
|
||||
if (top >= srcRect.top + props.src.offsetHeight + (fixed.value ? 0 : window.scrollY)) {
|
||||
transformOriginY = 'top';
|
||||
} else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.pageYOffset)) {
|
||||
} else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.scrollY)) {
|
||||
transformOriginY = 'bottom';
|
||||
}
|
||||
|
||||
if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.pageXOffset)) {
|
||||
if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.scrollX)) {
|
||||
transformOriginX = 'left';
|
||||
} else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.pageXOffset)) {
|
||||
} else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.scrollX)) {
|
||||
transformOriginX = 'right';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -249,6 +249,7 @@ if (noteViewInterruptors.length > 0) {
|
|||
|
||||
const isRenote = (
|
||||
note.value.renote != null &&
|
||||
note.value.reply == null &&
|
||||
note.value.text == null &&
|
||||
note.value.cw == null &&
|
||||
note.value.fileIds && note.value.fileIds.length === 0 &&
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div :class="$style.noteContent">
|
||||
<p v-if="appearNote.cw != null" :class="$style.cw">
|
||||
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
|
||||
<MkCwButton v-model="showContent" :text="appearNote.text" :files="appearNote.files" :poll="appearNote.poll"/>
|
||||
<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll"/>
|
||||
</p>
|
||||
<div v-show="appearNote.cw == null || showContent">
|
||||
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
||||
|
|
@ -328,7 +328,9 @@ if (noteViewInterruptors.length > 0) {
|
|||
|
||||
const isRenote = (
|
||||
note.value.renote != null &&
|
||||
note.value.reply == null &&
|
||||
note.value.text == null &&
|
||||
note.value.cw == null &&
|
||||
note.value.fileIds && note.value.fileIds.length === 0 &&
|
||||
note.value.poll == null
|
||||
);
|
||||
|
|
|
|||
|
|
@ -272,7 +272,13 @@ const maxTextLength = computed((): number => {
|
|||
|
||||
const canPost = computed((): boolean => {
|
||||
return !props.mock && !posting.value && !posted.value &&
|
||||
(1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) &&
|
||||
(
|
||||
1 <= textLength.value ||
|
||||
1 <= files.value.length ||
|
||||
poll.value != null ||
|
||||
props.renote != null ||
|
||||
(props.reply != null && quoteId.value != null)
|
||||
) &&
|
||||
(textLength.value <= maxTextLength.value) &&
|
||||
(!poll.value || poll.value.choices.length >= 2);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ const left = ref(0);
|
|||
|
||||
onMounted(() => {
|
||||
const rect = props.source.getBoundingClientRect();
|
||||
const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset;
|
||||
const y = rect.top + props.source.offsetHeight + window.pageYOffset;
|
||||
const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.scrollX;
|
||||
const y = rect.top + props.source.offsetHeight + window.scrollY;
|
||||
|
||||
top.value = y;
|
||||
left.value = x;
|
||||
|
|
|
|||
|
|
@ -106,8 +106,8 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
const rect = props.source.getBoundingClientRect();
|
||||
const x = ((rect.left + (props.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset;
|
||||
const y = rect.top + props.source.offsetHeight + window.pageYOffset;
|
||||
const x = ((rect.left + (props.source.offsetWidth / 2)) - (300 / 2)) + window.scrollX;
|
||||
const y = rect.top + props.source.offsetHeight + window.scrollY;
|
||||
|
||||
top.value = y;
|
||||
left.value = x;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ const invalid = Number.isNaN(_time);
|
|||
const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
|
||||
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
const now = ref((props.origin ?? new Date()).getTime());
|
||||
const now = ref(props.origin?.getTime() ?? Date.now());
|
||||
const ago = computed(() => (now.value - _time) / 1000/*ms*/);
|
||||
|
||||
const relative = computed<string>(() => {
|
||||
|
|
@ -77,7 +77,7 @@ let tickId: number;
|
|||
let currentInterval: number;
|
||||
|
||||
function tick() {
|
||||
now.value = (new Date()).getTime();
|
||||
now.value = Date.now();
|
||||
const nextInterval = ago.value < 60 ? 10000 : ago.value < 3600 ? 60000 : 180000;
|
||||
|
||||
if (currentInterval !== nextInterval) {
|
||||
|
|
|
|||
|
|
@ -9,11 +9,16 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkSpacer :contentMax="800">
|
||||
<div v-if="clip" class="_gaps">
|
||||
<div class="_panel">
|
||||
<div v-if="clip.description" :class="$style.description">
|
||||
<Mfm :text="clip.description" :isNote="false"/>
|
||||
<div class="_gaps_s" :class="$style.description">
|
||||
<div v-if="clip.description">
|
||||
<Mfm :text="clip.description" :isNote="false"/>
|
||||
</div>
|
||||
<div v-else>({{ i18n.ts.noDescription }})</div>
|
||||
<div>
|
||||
<MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike rounded primary @click="unfavorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
|
||||
<MkButton v-else v-tooltip="i18n.ts.favorite" asLike rounded @click="favorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
|
||||
</div>
|
||||
</div>
|
||||
<MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike rounded primary @click="unfavorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
|
||||
<MkButton v-else v-tooltip="i18n.ts.favorite" asLike rounded @click="favorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
|
||||
<div :class="$style.user">
|
||||
<MkAvatar :user="clip.user" :class="$style.avatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,16 +11,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div v-if="tab === 'my'" key="my" class="_gaps">
|
||||
<MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||
|
||||
<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="_gaps">
|
||||
<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`">
|
||||
<MkClipPreview :clip="item"/>
|
||||
</MkA>
|
||||
<MkPagination v-slot="{ items }" ref="pagingComponent" :pagination="pagination" class="_gaps">
|
||||
<MkClipPreview v-for="item in items" :key="item.id" :clip="item"/>
|
||||
</MkPagination>
|
||||
</div>
|
||||
<div v-else-if="tab === 'favorites'" key="favorites" class="_gaps">
|
||||
<MkA v-for="item in favorites" :key="item.id" :to="`/clips/${item.id}`">
|
||||
<MkClipPreview :clip="item"/>
|
||||
</MkA>
|
||||
<MkClipPreview v-for="item in favorites" :key="item.id" :clip="item"/>
|
||||
</div>
|
||||
</MkHorizontalSwipe>
|
||||
</MkSpacer>
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div v-if="clips && clips.length > 0" class="_margin">
|
||||
<div style="font-weight: bold; padding: 12px;">{{ i18n.ts.clip }}</div>
|
||||
<div class="_gaps">
|
||||
<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`">
|
||||
<MkClipPreview :clip="item"/>
|
||||
</MkA>
|
||||
<MkClipPreview v-for="item in clips" :key="item.id" :clip="item"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!showPrev" class="_buttons" :class="$style.loadPrev">
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div style="height: 100cqh; overflow: auto; text-align: center;">
|
||||
<MkSpacer :marginMin="20" :marginMax="28">
|
||||
<div class="_gaps">
|
||||
<MkInfo><MkLink url="https://misskey-hub.net/docs/for-users/stepped-guides/how-to-enable-2fa/" target="_blank">{{ i18n.ts._2fa.moreDetailedGuideHere }}</MkLink></MkInfo>
|
||||
|
||||
<I18n :src="i18n.ts._2fa.step1" tag="div">
|
||||
<template #a>
|
||||
<a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a>
|
||||
|
|
@ -113,6 +115,7 @@ import { i18n } from '@/i18n.js';
|
|||
import * as os from '@/os.js';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import MkLink from '@/components/MkLink.vue';
|
||||
import { confetti } from '@/scripts/confetti.js';
|
||||
import { signinRequired } from '@/account.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkButton v-else danger @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton>
|
||||
</div>
|
||||
|
||||
<MkButton v-else-if="!$i.twoFactorEnabled" primary gradate @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton>
|
||||
<div v-else-if="!$i.twoFactorEnabled" class="_gaps_s">
|
||||
<MkButton primary gradate @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton>
|
||||
<MkLink url="https://misskey-hub.net/docs/for-users/stepped-guides/how-to-enable-2fa/" target="_blank"><i class="ti ti-help-circle"></i> {{ i18n.ts.learnMore }}</MkLink>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
|
|
@ -79,6 +82,7 @@ import MkInfo from '@/components/MkInfo.vue';
|
|||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkLink from '@/components/MkLink.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { signinRequired, updateAccount } from '@/account.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
|
|
|||
|
|
@ -44,6 +44,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template #label>{{ i18n.ts.keepOriginalUploading }}</template>
|
||||
<template #caption>{{ i18n.ts.keepOriginalUploadingDescription }}</template>
|
||||
</MkSwitch>
|
||||
<MkSwitch v-model="keepOriginalFilename">
|
||||
<template #label>{{ i18n.ts.keepOriginalFilename }}</template>
|
||||
<template #caption>{{ i18n.ts.keepOriginalFilenameDescription }}</template>
|
||||
</MkSwitch>
|
||||
<MkSwitch v-model="alwaysMarkNsfw" @update:modelValue="saveProfile()">
|
||||
<template #label>{{ i18n.ts.alwaysMarkSensitive }}</template>
|
||||
</MkSwitch>
|
||||
|
|
@ -96,6 +100,7 @@ const meterStyle = computed(() => {
|
|||
});
|
||||
|
||||
const keepOriginalUploading = computed(defaultStore.makeGetterSetter('keepOriginalUploading'));
|
||||
const keepOriginalFilename = computed(defaultStore.makeGetterSetter('keepOriginalFilename'));
|
||||
|
||||
misskeyApi('drive').then(info => {
|
||||
capacity.value = info.capacity;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,14 @@ export async function getNoteClipMenu(props: {
|
|||
isDeleted: Ref<boolean>;
|
||||
currentClip?: Misskey.entities.Clip;
|
||||
}) {
|
||||
function getClipName(clip: Misskey.entities.Clip) {
|
||||
if ($i && clip.userId === $i.id && clip.notesCount != null) {
|
||||
return `${clip.name} (${clip.notesCount}/${$i.policies.noteEachClipsLimit})`;
|
||||
} else {
|
||||
return clip.name;
|
||||
}
|
||||
}
|
||||
|
||||
const isRenote = (
|
||||
props.note.renote != null &&
|
||||
props.note.text == null &&
|
||||
|
|
@ -37,7 +45,7 @@ export async function getNoteClipMenu(props: {
|
|||
|
||||
const clips = await clipsCache.fetch();
|
||||
const menu: MenuItem[] = [...clips.map(clip => ({
|
||||
text: clip.name,
|
||||
text: getClipName(clip),
|
||||
action: () => {
|
||||
claimAchievement('noteClipped1');
|
||||
os.promiseDialog(
|
||||
|
|
@ -50,7 +58,18 @@ export async function getNoteClipMenu(props: {
|
|||
text: i18n.tsx.confirmToUnclipAlreadyClippedNote({ name: clip.name }),
|
||||
});
|
||||
if (!confirm.canceled) {
|
||||
os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id });
|
||||
os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id }).then(() => {
|
||||
clipsCache.set(clips.map(c => {
|
||||
if (c.id === clip.id) {
|
||||
return {
|
||||
...c,
|
||||
notesCount: Math.max(0, ((c.notesCount ?? 0) - 1)),
|
||||
};
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}));
|
||||
});
|
||||
if (props.currentClip?.id === clip.id) props.isDeleted.value = true;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -60,7 +79,18 @@ export async function getNoteClipMenu(props: {
|
|||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
).then(() => {
|
||||
clipsCache.set(clips.map(c => {
|
||||
if (c.id === clip.id) {
|
||||
return {
|
||||
...c,
|
||||
notesCount: (c.notesCount ?? 0) + 1,
|
||||
};
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}));
|
||||
});
|
||||
},
|
||||
})), { type: 'divider' }, {
|
||||
icon: 'ti ti-plus',
|
||||
|
|
|
|||
|
|
@ -15,6 +15,16 @@ const fallbackName = (key: string) => `idbfallback::${key}`;
|
|||
|
||||
let idbAvailable = typeof window !== 'undefined' ? !!(window.indexedDB && window.indexedDB.open) : true;
|
||||
|
||||
// iframe.contentWindow.indexedDB.deleteDatabase() がchromeのバグで使用できないため、indexedDBを無効化している。
|
||||
// バグが治って再度有効化するのであれば、cypressのコマンド内のコメントアウトを外すこと
|
||||
// see https://github.com/misskey-dev/misskey/issues/13605#issuecomment-2053652123
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
if (window.Cypress) {
|
||||
idbAvailable = false;
|
||||
console.log('Cypress detected. It will use localStorage.');
|
||||
}
|
||||
|
||||
if (idbAvailable) {
|
||||
await iset('idb-test', 'test')
|
||||
.catch(err => {
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
|
|||
let top: number;
|
||||
|
||||
if (props.anchorElement) {
|
||||
left = rect.left + window.pageXOffset + (props.anchorElement.offsetWidth / 2);
|
||||
top = (rect.top + window.pageYOffset - contentHeight) - props.innerMargin;
|
||||
left = rect.left + window.scrollX + (props.anchorElement.offsetWidth / 2);
|
||||
top = (rect.top + window.scrollY - contentHeight) - props.innerMargin;
|
||||
} else {
|
||||
left = props.x;
|
||||
top = (props.y - contentHeight) - props.innerMargin;
|
||||
|
|
@ -35,8 +35,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
|
|||
|
||||
left -= (el.offsetWidth / 2);
|
||||
|
||||
if (left + contentWidth - window.pageXOffset > window.innerWidth) {
|
||||
left = window.innerWidth - contentWidth + window.pageXOffset - 1;
|
||||
if (left + contentWidth - window.scrollX > window.innerWidth) {
|
||||
left = window.innerWidth - contentWidth + window.scrollX - 1;
|
||||
}
|
||||
|
||||
return [left, top];
|
||||
|
|
@ -47,8 +47,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
|
|||
let top: number;
|
||||
|
||||
if (props.anchorElement) {
|
||||
left = rect.left + window.pageXOffset + (props.anchorElement.offsetWidth / 2);
|
||||
top = (rect.top + window.pageYOffset + props.anchorElement.offsetHeight) + props.innerMargin;
|
||||
left = rect.left + window.scrollX + (props.anchorElement.offsetWidth / 2);
|
||||
top = (rect.top + window.scrollY + props.anchorElement.offsetHeight) + props.innerMargin;
|
||||
} else {
|
||||
left = props.x;
|
||||
top = (props.y) + props.innerMargin;
|
||||
|
|
@ -56,8 +56,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
|
|||
|
||||
left -= (el.offsetWidth / 2);
|
||||
|
||||
if (left + contentWidth - window.pageXOffset > window.innerWidth) {
|
||||
left = window.innerWidth - contentWidth + window.pageXOffset - 1;
|
||||
if (left + contentWidth - window.scrollX > window.innerWidth) {
|
||||
left = window.innerWidth - contentWidth + window.scrollX - 1;
|
||||
}
|
||||
|
||||
return [left, top];
|
||||
|
|
@ -68,8 +68,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
|
|||
let top: number;
|
||||
|
||||
if (props.anchorElement) {
|
||||
left = (rect.left + window.pageXOffset - contentWidth) - props.innerMargin;
|
||||
top = rect.top + window.pageYOffset + (props.anchorElement.offsetHeight / 2);
|
||||
left = (rect.left + window.scrollX - contentWidth) - props.innerMargin;
|
||||
top = rect.top + window.scrollY + (props.anchorElement.offsetHeight / 2);
|
||||
} else {
|
||||
left = (props.x - contentWidth) - props.innerMargin;
|
||||
top = props.y;
|
||||
|
|
@ -77,8 +77,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
|
|||
|
||||
top -= (el.offsetHeight / 2);
|
||||
|
||||
if (top + contentHeight - window.pageYOffset > window.innerHeight) {
|
||||
top = window.innerHeight - contentHeight + window.pageYOffset - 1;
|
||||
if (top + contentHeight - window.scrollY > window.innerHeight) {
|
||||
top = window.innerHeight - contentHeight + window.scrollY - 1;
|
||||
}
|
||||
|
||||
return [left, top];
|
||||
|
|
@ -89,15 +89,15 @@ export function calcPopupPosition(el: HTMLElement, props: {
|
|||
let top: number;
|
||||
|
||||
if (props.anchorElement) {
|
||||
left = (rect.left + props.anchorElement.offsetWidth + window.pageXOffset) + props.innerMargin;
|
||||
left = (rect.left + props.anchorElement.offsetWidth + window.scrollX) + props.innerMargin;
|
||||
|
||||
if (props.align === 'top') {
|
||||
top = rect.top + window.pageYOffset;
|
||||
top = rect.top + window.scrollY;
|
||||
if (props.alignOffset != null) top += props.alignOffset;
|
||||
} else if (props.align === 'bottom') {
|
||||
// TODO
|
||||
} else { // center
|
||||
top = rect.top + window.pageYOffset + (props.anchorElement.offsetHeight / 2);
|
||||
top = rect.top + window.scrollY + (props.anchorElement.offsetHeight / 2);
|
||||
top -= (el.offsetHeight / 2);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -106,8 +106,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
|
|||
top -= (el.offsetHeight / 2);
|
||||
}
|
||||
|
||||
if (top + contentHeight - window.pageYOffset > window.innerHeight) {
|
||||
top = window.innerHeight - contentHeight + window.pageYOffset - 1;
|
||||
if (top + contentHeight - window.scrollY > window.innerHeight) {
|
||||
top = window.innerHeight - contentHeight + window.scrollY - 1;
|
||||
}
|
||||
|
||||
return [left, top];
|
||||
|
|
@ -123,7 +123,7 @@ export function calcPopupPosition(el: HTMLElement, props: {
|
|||
const [left, top] = calcPosWhenTop();
|
||||
|
||||
// ツールチップを上に向かって表示するスペースがなければ下に向かって出す
|
||||
if (top - window.pageYOffset < 0) {
|
||||
if (top - window.scrollY < 0) {
|
||||
const [left, top] = calcPosWhenBottom();
|
||||
return { left, top, transformOrigin: 'center top' };
|
||||
}
|
||||
|
|
@ -141,7 +141,7 @@ export function calcPopupPosition(el: HTMLElement, props: {
|
|||
const [left, top] = calcPosWhenLeft();
|
||||
|
||||
// ツールチップを左に向かって表示するスペースがなければ右に向かって出す
|
||||
if (left - window.pageXOffset < 0) {
|
||||
if (left - window.scrollX < 0) {
|
||||
const [left, top] = calcPosWhenRight();
|
||||
return { left, top, transformOrigin: 'left center' };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import { reactive, ref } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { readAndCompressImage } from '@misskey-dev/browser-image-resizer';
|
||||
import { getCompressionConfig } from './upload/compress-config.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
|
@ -39,13 +40,16 @@ export function uploadFile(
|
|||
if (folder && typeof folder === 'object') folder = folder.id;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = Math.random().toString();
|
||||
const id = uuid();
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = async (): Promise<void> => {
|
||||
const filename = name ?? file.name ?? 'untitled';
|
||||
const extension = filename.split('.').length > 1 ? '.' + filename.split('.').pop() : '';
|
||||
|
||||
const ctx = reactive<Uploading>({
|
||||
id: id,
|
||||
name: name ?? file.name ?? 'untitled',
|
||||
id,
|
||||
name: defaultStore.state.keepOriginalFilename ? filename : id + extension,
|
||||
progressMax: undefined,
|
||||
progressValue: undefined,
|
||||
img: window.URL.createObjectURL(file),
|
||||
|
|
|
|||
|
|
@ -53,11 +53,11 @@ export function useChartTooltip(opts: { position: 'top' | 'middle' } = { positio
|
|||
const rect = context.chart.canvas.getBoundingClientRect();
|
||||
|
||||
tooltipShowing.value = true;
|
||||
tooltipX.value = rect.left + window.pageXOffset + context.tooltip.caretX;
|
||||
tooltipX.value = rect.left + window.scrollX + context.tooltip.caretX;
|
||||
if (opts.position === 'top') {
|
||||
tooltipY.value = rect.top + window.pageYOffset;
|
||||
tooltipY.value = rect.top + window.scrollY;
|
||||
} else if (opts.position === 'middle') {
|
||||
tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY;
|
||||
tooltipY.value = rect.top + window.scrollY + context.tooltip.caretY;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -712,6 +712,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
|||
where: 'device',
|
||||
default: false,
|
||||
},
|
||||
keepOriginalFilename: {
|
||||
where: 'device',
|
||||
default: true,
|
||||
},
|
||||
|
||||
sound_masterVolume: {
|
||||
where: 'device',
|
||||
|
|
|
|||
|
|
@ -68,9 +68,9 @@ watch(showColon, (v) => {
|
|||
});
|
||||
|
||||
const tick = () => {
|
||||
const now = new Date();
|
||||
ss.value = Math.floor(now.getTime() / 1000).toString();
|
||||
ms.value = Math.floor(now.getTime() % 1000 / 10).toString().padStart(2, '0');
|
||||
const now = Date.now();
|
||||
ss.value = Math.floor(now / 1000).toString();
|
||||
ms.value = Math.floor(now % 1000 / 10).toString().padStart(2, '0');
|
||||
if (ss.value !== prevSec) showColon.value = true;
|
||||
prevSec = ss.value;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue