Merge remote-tracking branch 'misskey-dev/develop' into io

This commit is contained in:
まっちゃとーにゅ 2024-01-13 23:48:15 +09:00
commit 1b509cb955
No known key found for this signature in database
GPG key ID: 6AFBBF529601C1DB
96 changed files with 6555 additions and 674 deletions

View file

@ -0,0 +1,102 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')">
<template #header>:{{ emoji.name }}:</template>
<template #default>
<MkSpacer>
<div style="display: flex; flex-direction: column; gap: 1em;">
<div :class="$style.emojiImgWrapper">
<MkCustomEmoji :name="emoji.name" :normal="true" style="height: 100%;"></MkCustomEmoji>
</div>
<MkKeyValue>
<template #key>{{ i18n.ts.name }}</template>
<template #value>{{ emoji.name }}</template>
</MkKeyValue>
<MkKeyValue>
<template #key>{{ i18n.ts.tags }}</template>
<template #value>
<div v-if="emoji.aliases.length === 0">{{ i18n.ts.none }}</div>
<div v-else :class="$style.aliases">
<span v-for="alias in emoji.aliases" :key="alias" :class="$style.alias">
{{ alias }}
</span>
</div>
</template>
</MkKeyValue>
<MkKeyValue>
<template #key>{{ i18n.ts.category }}</template>
<template #value>{{ emoji.category ?? i18n.ts.none }}</template>
</MkKeyValue>
<MkKeyValue>
<template #key>{{ i18n.ts.sensitive }}</template>
<template #value>{{ emoji.isSensitive ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue>
<template #key>{{ i18n.ts.localOnly }}</template>
<template #value>{{ emoji.localOnly ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue>
<template #key>{{ i18n.ts.license }}</template>
<template #value>{{ emoji.license ?? i18n.ts.none }}</template>
</MkKeyValue>
<MkKeyValue :copy="emoji.url">
<template #key>{{ i18n.ts.emojiUrl }}</template>
<template #value>
<a :href="emoji.url" target="_blank">{{ emoji.url }}</a>
</template>
</MkKeyValue>
</div>
</MkSpacer>
</template>
</MkModalWindow>
</template>
<script lang="ts" setup>
import * as Misskey from 'misskey-js';
import { defineProps, shallowRef } from 'vue';
import { i18n } from '@/i18n.js';
import MkModalWindow from '@/components/MkModalWindow.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
const props = defineProps<{
emoji: Misskey.entities.EmojiDetailed,
}>();
const emit = defineEmits<{
(ev: 'ok', cropped: Misskey.entities.DriveFile): void;
(ev: 'cancel'): void;
(ev: 'closed'): void;
}>();
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
const cancel = () => {
emit('cancel');
dialogEl.value!.close();
};
</script>
<style lang="scss" module>
.emojiImgWrapper {
max-width: 100%;
height: 40cqh;
background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--X5) 8px, var(--X5) 14px);
border-radius: var(--radius);
margin: auto;
overflow-y: hidden;
}
.aliases {
display: flex;
flex-wrap: wrap;
gap: 3px;
}
.alias {
display: inline-block;
padding: 3px 10px;
background-color: var(--X5);
border: solid 1px var(--divider);
border-radius: var(--radius);
}
</style>

View file

@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
import { Chart } from 'chart.js';
import * as Misskey from 'misskey-js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { defaultStore } from '@/store.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
@ -23,9 +24,16 @@ import { initChart } from '@/scripts/init-chart.js';
initChart();
const props = defineProps<{
src: string;
}>();
export type HeatmapSource = 'active-users' | 'notes' | 'ap-requests-inbox-received' | 'ap-requests-deliver-succeeded' | 'ap-requests-deliver-failed';
const props = withDefaults(defineProps<{
src: HeatmapSource;
user?: Misskey.entities.User;
label?: string;
}>(), {
user: undefined,
label: '',
});
const rootEl = shallowRef<HTMLDivElement>(null);
const chartEl = shallowRef<HTMLCanvasElement>(null);
@ -75,8 +83,13 @@ async function renderChart() {
const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' });
values = raw.readWrite;
} else if (props.src === 'notes') {
const raw = await misskeyApi('charts/notes', { limit: chartLimit, span: 'day' });
values = raw.local.inc;
if (props.user) {
const raw = await misskeyApi('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' });
values = raw.inc;
} else {
const raw = await misskeyApi('charts/notes', { limit: chartLimit, span: 'day' });
values = raw.local.inc;
}
} else if (props.src === 'ap-requests-inbox-received') {
const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' });
values = raw.inboxReceived;
@ -105,7 +118,7 @@ async function renderChart() {
type: 'matrix',
data: {
datasets: [{
label: 'Read & Write',
label: props.label,
data: format(values),
pointRadius: 0,
borderWidth: 0,
@ -128,6 +141,9 @@ async function renderChart() {
const a = c.chart.chartArea ?? {};
return (a.bottom - a.top) / 7 - marginEachCell;
},
/* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107>
}] satisfies ChartData[],
*/
}],
},
options: {
@ -195,7 +211,7 @@ async function renderChart() {
},
label(context) {
const v = context.dataset.data[context.dataIndex];
return ['Active: ' + v.v];
return [v.v];
},
},
//mode: 'index',

View file

@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="ap-requests-deliver-failed">AP Requests: deliverFailed</option>
</MkSelect>
<div class="_panel" :class="$style.heatmap">
<MkHeatmap :src="heatmapSrc"/>
<MkHeatmap :src="heatmapSrc" :label="'Read & Write'"/>
</div>
</MkFoldableSection>
@ -92,7 +92,7 @@ import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import * as os from '@/os.js';
import { misskeyApiGet } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import MkHeatmap from '@/components/MkHeatmap.vue';
import MkHeatmap, { type HeatmapSource } from '@/components/MkHeatmap.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue';
import MkRetentionLineChart from '@/components/MkRetentionLineChart.vue';
@ -103,7 +103,7 @@ initChart();
const chartLimit = 500;
const chartSpan = ref<'hour' | 'day'>('hour');
const chartSrc = ref('active-users');
const heatmapSrc = ref('active-users');
const heatmapSrc = ref<HeatmapSource>('active-users');
const subDoughnutEl = shallowRef<HTMLCanvasElement>();
const pubDoughnutEl = shallowRef<HTMLCanvasElement>();

View file

@ -16,7 +16,7 @@ import * as os from '@/os.js';
const props = withDefaults(defineProps<{
x: number;
y: number;
value?: number;
value?: number | string;
}>(), {
value: 1,
});

View file

@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
class="_button"
:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]"
@click="toggleReaction()"
@contextmenu.prevent.stop="menu"
>
<MkReactionIcon :class="defaultStore.state.limitWidthOfReaction ? $style.limitWidth : ''" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
<span :class="$style.count">{{ count }}</span>
@ -21,6 +22,7 @@ import { computed, inject, onMounted, shallowRef, watch } from 'vue';
import * as Misskey from 'misskey-js';
import XDetails from '@/components/MkReactionsViewer.details.vue';
import MkReactionIcon from '@/components/MkReactionIcon.vue';
import MkCustomEmojiDetailedDialog from './MkCustomEmojiDetailedDialog.vue';
import * as os from '@/os.js';
import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
import { useTooltip } from '@/scripts/use-tooltip.js';
@ -98,6 +100,22 @@ async function toggleReaction() {
}
}
async function menu(ev) {
if (!canToggle.value) return;
if (!props.reaction.includes(":")) return;
os.popupMenu([{
text: i18n.ts.info,
icon: 'ti ti-info-circle',
action: async () => {
os.popup(MkCustomEmojiDetailedDialog, {
emoji: await misskeyApiGet('emoji', {
name: props.reaction.replace(/:/g, '').replace(/@\./, ''),
}),
});
},
}], ev.currentTarget ?? ev.target);
}
function anime() {
if (document.hidden) return;
if (!defaultStore.state.animation) return;

View file

@ -24,9 +24,11 @@ import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js'
import { defaultStore } from '@/store.js';
import { customEmojisMap } from '@/custom-emojis.js';
import * as os from '@/os.js';
import { misskeyApiGet } from '@/scripts/misskey-api.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import * as sound from '@/scripts/sound.js';
import { i18n } from '@/i18n.js';
import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';
const props = defineProps<{
name: string;
@ -93,7 +95,19 @@ function onClick(ev: MouseEvent) {
react(`:${props.name}:`);
sound.playMisskeySfx('reaction');
},
}] : [])], ev.currentTarget ?? ev.target);
}] : []), {
text: i18n.ts.info,
icon: 'ti ti-info-circle',
action: async () => {
os.popup(MkCustomEmojiDetailedDialog, {
emoji: await misskeyApiGet('emoji', {
name: customEmojiName.value,
}),
}, {
anchor: ev.target,
});
},
}], ev.currentTarget ?? ev.target);
}
}
</script>

View file

@ -61,7 +61,12 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) {
if (t == null) 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;
/**
@ -240,17 +245,30 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) {
break;
}
case 'fg': {
let color = token.props.args.color;
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
let color = validColor(token.props.args.color);
color = color ?? 'f00';
style = `color: #${color}; overflow-wrap: anywhere;`;
break;
}
case 'bg': {
let color = token.props.args.color;
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
let color = validColor(token.props.args.color);
color = color ?? 'f00';
style = `background-color: #${color}; overflow-wrap: anywhere;`;
break;
}
case 'border': {
let color = validColor(token.props.args.color);
color = color ? `#${color}` : 'var(--accent)';
let b_style = token.props.args.style;
if (
!['hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset']
.includes(b_style)
) b_style = 'solid';
const width = parseFloat(token.props.args.width ?? '1');
const radius = parseFloat(token.props.args.radius ?? '0');
style = `border: ${width}px ${b_style} ${color}; border-radius: ${radius}px`;
break;
}
case 'ruby': {
if (token.children.length === 1) {
const child = token.children[0];