Merge tag '2023.10.2' into merge-upstream

This commit is contained in:
riku6460
2023-10-21 18:25:55 +09:00
282 changed files with 4676 additions and 4314 deletions

View File

@@ -42,6 +42,7 @@ import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { $i } from '@/account.js';
import { defaultStore } from "@/store.js";
const props = withDefaults(defineProps<{
user: Misskey.entities.UserDetailed,
@@ -52,6 +53,10 @@ const props = withDefaults(defineProps<{
large: false,
});
const emit = defineEmits<{
(_: 'update:user', value: Misskey.entities.UserDetailed): void
}>();
let isFollowing = $ref(props.user.isFollowing);
let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou);
let wait = $ref(false);
@@ -95,6 +100,11 @@ async function onClick() {
} else {
await os.api('following/create', {
userId: props.user.id,
withReplies: defaultStore.state.defaultWithReplies,
});
emit('update:user', {
...props.user,
withReplies: defaultStore.state.defaultWithReplies
});
hasPendingFollowRequestFromYou = true;

View File

@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="[$style.root, { yellow: instance.isNotResponding, red: instance.isBlocked, gray: instance.isSuspended }]">
<div :class="[$style.root, { yellow: instance.isNotResponding, red: instance.isBlocked, gray: instance.isSuspended, blue: instance.isSilenced }]">
<img class="icon" :src="getInstanceIcon(instance)" alt="" loading="lazy"/>
<div class="body">
<span class="host">{{ instance.name ?? instance.host }}</span>
@@ -89,6 +89,12 @@ function getInstanceIcon(instance): string {
height: 30px;
}
&:global(.blue) {
--c: rgba(0, 42, 255, 0.15);
background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);
background-size: 16px 16px;
}
&:global(.yellow) {
--c: rgb(255 196 0 / 15%);
background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe }]" :to="url" :style="{ background: bgCss }">
<img :class="$style.icon" :src="`/avatar/@${username}@${host}`" alt="">
<img :class="$style.icon" :src="avatarUrl" alt="">
<span>
<span>@{{ username }}</span>
<span v-if="(host != localHost) || defaultStore.state.showFullAcct" :class="$style.host">@{{ toUnicode(host) }}</span>
@@ -15,11 +15,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { toUnicode } from 'punycode';
import { } from 'vue';
import { computed } from 'vue';
import tinycolor from 'tinycolor2';
import { host as localHost } from '@/config.js';
import { $i } from '@/account.js';
import { defaultStore } from '@/store.js';
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
const props = defineProps<{
username: string;
@@ -37,6 +38,11 @@ const isMe = $i && (
const bg = tinycolor(getComputedStyle(document.documentElement).getPropertyValue(isMe ? '--mentionMe' : '--mention'));
bg.setAlpha(0.1);
const bgCss = bg.toRgbString();
const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages
? getStaticImageUrl(`/avatar/@${props.username}@${props.host}`)
: `/avatar/@${props.username}@${props.host}`,
);
</script>
<style lang="scss" module>

View File

@@ -232,6 +232,7 @@ const keymap = {
useNoteCapture({
rootEl: el,
note: $$(appearNote),
pureNote: $$(note),
isDeletedRef: isDeleted,
});

View File

@@ -94,7 +94,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<footer>
<div :class="$style.noteFooterInfo">
<MkA :to="notePage(appearNote)">
<MkTime :time="appearNote.createdAt" mode="detail"/>
<MkTime :time="appearNote.createdAt" mode="detail" colored/>
</MkA>
</div>
<MkReactionsViewer ref="reactionsViewer" :note="appearNote"/>
@@ -296,6 +296,7 @@ const reactionsPagination = $computed(() => ({
useNoteCapture({
rootEl: el,
note: $$(appearNote),
pureNote: $$(note),
isDeletedRef: isDeleted,
});

View File

@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div :class="$style.info">
<MkA :to="notePage(note)">
<MkTime :time="note.createdAt"/>
<MkTime :time="note.createdAt" colored/>
</MkA>
<span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]">
<i v-if="note.visibility === 'home'" class="ti ti-home"></i>

View File

@@ -283,6 +283,12 @@ useTooltip(reactionRef, (showing) => {
.quote:first-child {
margin-right: 4px;
position: relative;
&:before {
position: absolute;
transform: rotate(180deg);
}
}
.quote:last-child {

View File

@@ -41,7 +41,7 @@ const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
const pagination: Paging = {
endpoint: 'i/notifications' as const,
limit: 10,
limit: 20,
params: computed(() => ({
excludeTypes: props.excludeTypes ?? undefined,
})),

View File

@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
<button class="_button" :class="$style.menu" @click="showMenu"><i class="ti ti-dots"></i></button>
<MkFollowButton v-if="$i && user.id != $i.id" :class="$style.follow" :user="user" mini/>
<MkFollowButton v-if="$i && user.id != $i.id" v-model:user="user" :class="$style.follow" mini/>
</div>
<div v-else>
<MkLoading/>

View File

@@ -17,6 +17,7 @@ import MkSparkle from '@/components/MkSparkle.vue';
import MkA from '@/components/global/MkA.vue';
import { host } from '@/config.js';
import { defaultStore } from '@/store.js';
import { nyaize } from '@/scripts/nyaize.js';
const QUOTE_STYLE = `
display: block;
@@ -55,10 +56,13 @@ export default function(props: {
* @param ast MFM AST
* @param scale How times large the text is
*/
const genEl = (ast: mfm.MfmNode[], scale: number) => ast.map((token): VNode | string | (VNode | string)[] => {
const genEl = (ast: mfm.MfmNode[], scale: number, disableNyaize = false) => ast.map((token): VNode | string | (VNode | string)[] => {
switch (token.type) {
case 'text': {
const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
let text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
if (!disableNyaize && props.author?.isCat) {
text = nyaize(text);
}
if (!props.plain) {
const res: (VNode | string)[] = [];
@@ -260,7 +264,7 @@ export default function(props: {
key: Math.random(),
url: token.props.url,
rel: 'nofollow noopener',
}, genEl(token.children, scale))];
}, genEl(token.children, scale, true))];
}
case 'mention': {
@@ -299,11 +303,11 @@ export default function(props: {
if (!props.nowrap) {
return [h('div', {
style: QUOTE_STYLE,
}, genEl(token.children, scale))];
}, genEl(token.children, scale, true))];
} else {
return [h('span', {
style: QUOTE_STYLE,
}, genEl(token.children, scale))];
}, genEl(token.children, scale, true))];
}
}
@@ -358,7 +362,7 @@ export default function(props: {
}
case 'plain': {
return [h('span', genEl(token.children, scale))];
return [h('span', genEl(token.children, scale, true))];
}
default: {

View File

@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<time :title="absolute">
<time :title="absolute" :class="{ [$style.old1]: colored && (ago > 60 * 60 * 24 * 90), [$style.old2]: colored && (ago > 60 * 60 * 24 * 180) }">
<template v-if="invalid">{{ i18n.ts._ago.invalid }}</template>
<template v-else-if="mode === 'relative'">{{ relative }}</template>
<template v-else-if="mode === 'absolute'">{{ absolute }}</template>
@@ -22,6 +22,7 @@ const props = withDefaults(defineProps<{
time: Date | string | number | null;
origin?: Date | null;
mode?: 'relative' | 'absolute' | 'detail';
colored?: boolean;
}>(), {
origin: isChromatic() ? new Date('2023-04-01T00:00:00Z') : null,
mode: 'relative',
@@ -75,3 +76,13 @@ if (!invalid && props.origin === null && (props.mode === 'relative' || props.mod
});
}
</script>
<style lang="scss" module>
.old1 {
color: var(--warn);
}
.old1.old2 {
color: var(--error);
}
</style>