Merge remote-tracking branch 'misskey-original/develop' into develop
# Conflicts: # package.json # packages/frontend/src/components/MkEmojiEditDialog.vue # packages/frontend/src/components/MkMenu.vue # packages/frontend/src/components/MkNote.vue # packages/frontend/src/pages/timeline.vue
This commit is contained in:
commit
78a34d3de3
115 changed files with 4837 additions and 2932 deletions
|
|
@ -4,10 +4,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
-->
|
||||
|
||||
<template>
|
||||
<MkModalWindow
|
||||
ref="dialog"
|
||||
:width="400"
|
||||
@close="dialog.close()"
|
||||
<MkWindow
|
||||
ref="windowEl"
|
||||
:initialWidth="400"
|
||||
:initialHeight="500"
|
||||
:canResize="false"
|
||||
@close="windowEl.close()"
|
||||
@closed="$emit('closed')"
|
||||
>
|
||||
<template v-if="emoji" #header>:{{ emoji.name }}:</template>
|
||||
|
|
@ -81,14 +83,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkModalWindow>
|
||||
</MkWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { DriveFile } from 'misskey-js/built/entities.js';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkWindow from '@/components/MkWindow.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
|
|
@ -106,18 +108,18 @@ const props = defineProps<{
|
|||
isRequest: boolean,
|
||||
}>();
|
||||
|
||||
let dialog = ref<InstanceType<typeof MkModalWindow> | null>(null);
|
||||
let name = ref(props.emoji ? props.emoji.name : '');
|
||||
let category = ref(props.emoji ? props.emoji.category : '');
|
||||
let aliases = ref(props.emoji ? props.emoji.aliases.join(' ') : '');
|
||||
let license = ref(props.emoji ? (props.emoji.license ?? '') : '');
|
||||
let isSensitive = ref(props.emoji ? props.emoji.isSensitive : false);
|
||||
let localOnly = ref(props.emoji ? props.emoji.localOnly : false);
|
||||
let roleIdsThatCanBeUsedThisEmojiAsReaction = ref((props.emoji && props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction) ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
|
||||
let rolesThatCanBeUsedThisEmojiAsReaction = ref<Misskey.entities.Role[]>([]);
|
||||
let file = ref<Misskey.entities.DriveFile>();
|
||||
const windowEl = ref<InstanceType<typeof MkWindow> | null>(null);
|
||||
const name = ref<string>(props.emoji ? props.emoji.name : '');
|
||||
const category = ref<string>(props.emoji ? props.emoji.category : '');
|
||||
const aliases = ref<string>(props.emoji ? props.emoji.aliases.join(' ') : '');
|
||||
const license = ref<string>(props.emoji ? (props.emoji.license ?? '') : '');
|
||||
const isSensitive = ref(props.emoji ? props.emoji.isSensitive : false);
|
||||
const localOnly = ref(props.emoji ? props.emoji.localOnly : false);
|
||||
const roleIdsThatCanBeUsedThisEmojiAsReaction = ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
|
||||
const rolesThatCanBeUsedThisEmojiAsReaction = ref<Misskey.entities.Role[]>([]);
|
||||
const file = ref<Misskey.entities.DriveFile>();
|
||||
let isRequest = ref(props.isRequest ?? false);
|
||||
watch((roleIdsThatCanBeUsedThisEmojiAsReaction), async () => {
|
||||
watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
|
||||
rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => misskeyApi('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
|
||||
}, { immediate: true });
|
||||
const isNotifyIsHome = ref(props.emoji ? props.emoji.isNotifyIsHome : false);
|
||||
|
|
@ -190,7 +192,7 @@ async function done() {
|
|||
},
|
||||
});
|
||||
|
||||
dialog.value.close();
|
||||
windowEl.value.close();
|
||||
} else {
|
||||
const created = isRequest.value
|
||||
? await os.apiWithDialog('admin/emoji/add-request', params)
|
||||
|
|
@ -200,7 +202,7 @@ async function done() {
|
|||
created: created,
|
||||
});
|
||||
|
||||
dialog.value.close();
|
||||
windowEl.value.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +219,7 @@ async function del() {
|
|||
emit('done', {
|
||||
deleted: true,
|
||||
});
|
||||
dialog.value.close();
|
||||
windowEl.value.close();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template>
|
||||
<div
|
||||
ref="rootEl"
|
||||
:class="[$style.transitionRoot, (defaultStore.state.animation && $style.enableAnimation)]"
|
||||
@touchstart="touchStart"
|
||||
@touchmove="touchMove"
|
||||
@touchend="touchEnd"
|
||||
:class="[$style.transitionRoot]"
|
||||
@touchstart.passive="touchStart"
|
||||
@touchmove.passive="touchMove"
|
||||
@touchend.passive="touchEnd"
|
||||
>
|
||||
<Transition
|
||||
:class="[$style.transitionChildren, { [$style.swiping]: isSwipingForClass }]"
|
||||
|
|
@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef, computed, nextTick, watch } from 'vue';
|
||||
import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
|
||||
|
|
@ -154,7 +155,7 @@ function touchEnd(event: TouchEvent) {
|
|||
|
||||
pullDistance.value = 0;
|
||||
isSwiping.value = false;
|
||||
setTimeout(() => {
|
||||
window.setTimeout(() => {
|
||||
isSwipingForClass.value = false;
|
||||
}, 400);
|
||||
}
|
||||
|
|
@ -178,29 +179,29 @@ watch(tabModel, (newTab, oldTab) => {
|
|||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.transitionRoot.enableAnimation {
|
||||
.transitionRoot {
|
||||
display: grid;
|
||||
grid-template-columns: 100%;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.transitionChildren {
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
transform: translateX(var(--swipe));
|
||||
.transitionChildren {
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
transform: translateX(var(--swipe));
|
||||
|
||||
&.swipeAnimation_enterActive,
|
||||
&.swipeAnimation_leaveActive {
|
||||
transition: transform .3s cubic-bezier(0.65, 0.05, 0.36, 1);
|
||||
}
|
||||
&.swipeAnimation_enterActive,
|
||||
&.swipeAnimation_leaveActive {
|
||||
transition: transform .3s cubic-bezier(0.65, 0.05, 0.36, 1);
|
||||
}
|
||||
|
||||
&.swipeAnimationRight_leaveTo,
|
||||
&.swipeAnimationLeft_enterFrom {
|
||||
transform: translateX(calc(100% + 24px));
|
||||
}
|
||||
&.swipeAnimationRight_leaveTo,
|
||||
&.swipeAnimationLeft_enterFrom {
|
||||
transform: translateX(calc(100% + 24px));
|
||||
}
|
||||
|
||||
&.swipeAnimationRight_enterFrom,
|
||||
&.swipeAnimationLeft_leaveTo {
|
||||
transform: translateX(calc(-100% - 24px));
|
||||
}
|
||||
&.swipeAnimationRight_enterFrom,
|
||||
&.swipeAnimationLeft_leaveTo {
|
||||
transform: translateX(calc(-100% - 24px));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ function close() {
|
|||
left: 32px;
|
||||
color: var(--indicator);
|
||||
font-size: 8px;
|
||||
animation: blink 1s infinite;
|
||||
animation: global-blink 1s infinite;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
top: 16px;
|
||||
|
|
|
|||
|
|
@ -518,7 +518,7 @@ onBeforeUnmount(() => {
|
|||
right: 18px;
|
||||
color: var(--indicator);
|
||||
font-size: 8px;
|
||||
animation: blink 1s infinite;
|
||||
animation: global-blink 1s infinite;
|
||||
}
|
||||
|
||||
.divider {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<template>
|
||||
<div
|
||||
v-if="!hardMuted && !muted"
|
||||
v-if="!hardMuted && muted === false"
|
||||
v-show="!isDeleted"
|
||||
ref="el"
|
||||
v-hotkey="keymap"
|
||||
|
|
@ -142,7 +142,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</article>
|
||||
</div>
|
||||
<div v-else-if="muted && !hideMutedNotes" :class="$style.muted" @click="muted = false">
|
||||
<I18n :src="i18n.ts.userSaysSomething" tag="small">
|
||||
<I18n v-if="muted === 'sensitiveMute'" :src="i18n.ts.userSaysSomethingSensitive" tag="small">
|
||||
<template #name>
|
||||
<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
|
||||
<MkUserName :user="appearNote.user"/>
|
||||
</MkA>
|
||||
</template>
|
||||
</I18n>
|
||||
<I18n v-else :src="i18n.ts.userSaysSomething" tag="small">
|
||||
<template #name>
|
||||
<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
|
||||
<MkUserName :user="appearNote.user"/>
|
||||
|
|
@ -211,6 +218,7 @@ const emit = defineEmits<{
|
|||
(ev: 'removeReaction', emoji: string): void;
|
||||
}>();
|
||||
|
||||
const inTimeline = inject<boolean>('inTimeline', false);
|
||||
const inChannel = inject('inChannel', null);
|
||||
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
|
||||
const note = ref(deepClone(props.note));
|
||||
|
|
@ -257,19 +265,27 @@ const isLong = shouldCollapsed(appearNote.value, urls.value ?? []);
|
|||
const collapsed = ref(appearNote.value.cw == null && isLong);
|
||||
const isDeleted = ref(false);
|
||||
const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
|
||||
const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords));
|
||||
const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords, true));
|
||||
const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null);
|
||||
const translating = ref(false);
|
||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.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 renoteCollapsed = ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || (appearNote.value.myReaction != null)));
|
||||
|
||||
function checkMute(note: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null): boolean {
|
||||
/* Overload FunctionにLintが対応していないのでコメントアウト
|
||||
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean;
|
||||
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: false): boolean | 'sensitiveMute';
|
||||
*/
|
||||
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly = false): boolean | 'sensitiveMute' {
|
||||
if (mutedWords == null) return false;
|
||||
|
||||
if (checkWordMute(note, $i, mutedWords)) return true;
|
||||
if (note.reply && checkWordMute(note.reply, $i, mutedWords)) return true;
|
||||
if (note.renote && checkWordMute(note.renote, $i, mutedWords)) return true;
|
||||
if (checkWordMute(noteToCheck, $i, mutedWords)) return true;
|
||||
if (noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords)) return true;
|
||||
if (noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords)) return true;
|
||||
|
||||
if (checkOnly) return false;
|
||||
|
||||
if (inTimeline && !defaultStore.state.tl.filter.withSensitive && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute';
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ const emit = defineEmits<{
|
|||
(ev: 'queue', count: number): void;
|
||||
}>();
|
||||
|
||||
provide('inTimeline', true);
|
||||
provide('inChannel', computed(() => props.src === 'channel'));
|
||||
|
||||
type TimelineQueryType = {
|
||||
|
|
|
|||
|
|
@ -96,12 +96,12 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) {
|
|||
if (t == null || typeof t === 'boolean') return null;
|
||||
return t.match(/^[0-9.]+s$/) ? t : null;
|
||||
};
|
||||
|
||||
|
||||
const validColor = (c: string | null | undefined): string | null => {
|
||||
if (c == null) return null;
|
||||
return c.match(/^[0-9a-f]{3,6}$/i) ? c : null;
|
||||
};
|
||||
|
||||
|
||||
const useAnim = defaultStore.state.advancedMfm && defaultStore.state.animatedMfm;
|
||||
|
||||
/**
|
||||
|
|
@ -155,7 +155,7 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) {
|
|||
case 'tada': {
|
||||
const speed = validTime(token.props.args.speed) ?? '1s';
|
||||
const delay = validTime(token.props.args.delay) ?? '0s';
|
||||
style = 'font-size: 150%;' + (useAnim ? `animation: tada ${speed} linear infinite both; animation-delay: ${delay};` : '');
|
||||
style = 'font-size: 150%;' + (useAnim ? `animation: global-tada ${speed} linear infinite both; animation-delay: ${delay};` : '');
|
||||
break;
|
||||
}
|
||||
case 'jelly': {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue