Merge branch 'develop' into merge/2024-02-03
# Conflicts: # locales/index.d.ts # packages/backend/src/core/entities/UserEntityService.ts # packages/frontend/src/_dev_boot_.ts # packages/misskey-js/src/autogen/types.ts # sharkey-locales/en-US.yml
This commit is contained in:
commit
f36029f795
24 changed files with 512 additions and 35 deletions
|
|
@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<button
|
||||
class="_button"
|
||||
:class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou, [$style.full]: full, [$style.large]: large }]"
|
||||
:disabled="wait"
|
||||
:disabled="wait || disabled"
|
||||
@click="onClick"
|
||||
>
|
||||
<template v-if="!wait">
|
||||
|
|
@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { host } from '@@/js/config.js';
|
||||
import * as os from '@/os.js';
|
||||
|
|
@ -51,13 +51,16 @@ const props = withDefaults(defineProps<{
|
|||
user: Misskey.entities.UserDetailed,
|
||||
full?: boolean,
|
||||
large?: boolean,
|
||||
disabled?: boolean,
|
||||
}>(), {
|
||||
full: false,
|
||||
large: false,
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(_: 'update:user', value: Misskey.entities.UserDetailed): void
|
||||
(_: 'update:user', value: Misskey.entities.UserDetailed): void,
|
||||
(_: 'update:wait', value: boolean): void,
|
||||
}>();
|
||||
|
||||
const isFollowing = ref(props.user.isFollowing);
|
||||
|
|
@ -65,6 +68,9 @@ const hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFro
|
|||
const wait = ref(false);
|
||||
const connection = useStream().useChannel('main');
|
||||
|
||||
// Emit the "wait" status so external components can synchronize state
|
||||
watch(wait, value => emit('update:wait', value));
|
||||
|
||||
if (props.user.isFollowing == null && $i) {
|
||||
misskeyApi('users/show', {
|
||||
userId: props.user.id,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
<template v-else-if="notification.type === 'follow'">
|
||||
<span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}</span>
|
||||
<div v-if="full" :class="$style.followRequestCommands">
|
||||
<MkFollowButton v-if="userDetailed" :class="$style.followCommandButton" :user="userDetailed" :transparent="false" :full="false"/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="notification.type === 'followRequestAccepted'">
|
||||
<div :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.followRequestAccepted }}</div>
|
||||
|
|
@ -136,6 +139,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div v-if="full && !followRequestDone" :class="$style.followRequestCommands">
|
||||
<MkButton :class="$style.followRequestCommandButton" rounded primary @click="acceptFollowRequest()"><i class="ti ti-check"/> {{ i18n.ts.accept }}</MkButton>
|
||||
<MkButton :class="$style.followRequestCommandButton" rounded danger @click="rejectFollowRequest()"><i class="ti ti-x"/> {{ i18n.ts.reject }}</MkButton>
|
||||
<MkFollowButton v-if="userDetailed" :class="$style.followCommandButton" :user="userDetailed" :transparent="false" :full="false"/>
|
||||
</div>
|
||||
</template>
|
||||
<span v-else-if="notification.type === 'test'" :class="$style.text">{{ i18n.ts._notification.notificationWillBeDisplayedLikeThis }}</span>
|
||||
|
|
@ -179,8 +183,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { Ref, ref, watch } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { UserDetailed } from 'misskey-js/autogen/models.js';
|
||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { getNoteSummary } from '@/scripts/get-note-summary.js';
|
||||
|
|
@ -190,6 +195,7 @@ import { i18n } from '@/i18n.js';
|
|||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { signinRequired } from '@/account.js';
|
||||
import { infoImageUrl } from '@/instance.js';
|
||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||
|
||||
const $i = signinRequired();
|
||||
|
||||
|
|
@ -202,6 +208,26 @@ const props = withDefaults(defineProps<{
|
|||
full: false,
|
||||
});
|
||||
|
||||
const userDetailed: Ref<UserDetailed | null> = ref(null);
|
||||
|
||||
// watch() is required because computed() doesn't support async.
|
||||
watch(props, async () => {
|
||||
const type = props.notification.type;
|
||||
|
||||
// To avoid extra lookups, only do the query when it actually matters.
|
||||
if (type === 'follow' || type === 'receiveFollowRequest') {
|
||||
const user = await misskeyApi('users/show', {
|
||||
userId: props.notification.userId,
|
||||
});
|
||||
|
||||
userDetailed.value = user;
|
||||
followRequestDone.value = !user.hasPendingFollowRequestToYou;
|
||||
} else {
|
||||
userDetailed.value = null;
|
||||
followRequestDone.value = false;
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
type ExportCompletedNotification = Misskey.entities.Notification & { type: 'exportCompleted' };
|
||||
|
||||
const exportEntityName = {
|
||||
|
|
@ -216,7 +242,7 @@ const exportEntityName = {
|
|||
userList: i18n.ts.lists,
|
||||
} as const satisfies Record<ExportCompletedNotification['exportedEntity'], string>;
|
||||
|
||||
const followRequestDone = ref(false);
|
||||
const followRequestDone = ref(true);
|
||||
|
||||
const acceptFollowRequest = () => {
|
||||
if (!('user' in props.notification)) return;
|
||||
|
|
@ -434,13 +460,24 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
|
|||
.followRequestCommands {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
max-width: 300px;
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
.followRequestCommandButton {
|
||||
max-width: 175px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.flexSpacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.followCommandButton {
|
||||
margin-left: auto;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.reactionsItem {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -366,6 +366,29 @@ if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
|||
cw.value = props.reply.cw;
|
||||
}
|
||||
|
||||
// apply default CW
|
||||
if ($i.defaultCW) {
|
||||
useCw.value = true;
|
||||
|
||||
if (!cw.value || $i.defaultCWPriority === 'default') {
|
||||
cw.value = $i.defaultCW;
|
||||
} else if ($i.defaultCWPriority !== 'parent') {
|
||||
// This is a fancy way of simulating /\bsearch\b/ without a regular expression.
|
||||
// We're checking to see whether the default CW appears inside the existing CW, but *only* if there's word boundaries.
|
||||
const parts = cw.value.split($i.defaultCW);
|
||||
const hasExistingDefaultCW = parts.length === 2 && !/\w$/.test(parts[0]) && !/^\w/.test(parts[1]);
|
||||
if (!hasExistingDefaultCW) {
|
||||
// We need to merge the CWs
|
||||
if ($i.defaultCWPriority === 'defaultParent') {
|
||||
cw.value = `${$i.defaultCW}, ${cw.value}`;
|
||||
} else if ($i.defaultCWPriority === 'parentDefault') {
|
||||
cw.value = `${cw.value}, ${$i.defaultCW}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
// else { do nothing, because existing CW takes priority. }
|
||||
}
|
||||
|
||||
function watchForDraft() {
|
||||
watch(text, () => saveDraft());
|
||||
watch(useCw, () => saveDraft());
|
||||
|
|
|
|||
|
|
@ -358,6 +358,10 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
|||
return h('ruby', {}, [...genEl(token.children.slice(0, token.children.length - 1), scale), h('rt', text.trim())]);
|
||||
}
|
||||
}
|
||||
case 'group': { // this is mostly a hack for the insides of `ruby`
|
||||
style = '';
|
||||
break;
|
||||
}
|
||||
case 'unixtime': {
|
||||
const child = token.children[0];
|
||||
const unixtime = parseInt(child.type === 'text' ? child.props.text : '');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue