mirror of
https://codeberg.org/yeentown/barkey.git
synced 2025-04-28 09:36:56 +00:00
sort custom emoticons in picker by sortKey (#18)
This commit is contained in:
parent
597a1bd47a
commit
a3731f3392
19 changed files with 73 additions and 6 deletions
|
@ -0,0 +1,11 @@
|
|||
export class AddEmojiSortKey1733435351051 {
|
||||
name = 'AddEmojiSortKey1733435351051'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "emoji" ADD "sortKey" character varying(64)`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "sortKey"`);
|
||||
}
|
||||
}
|
|
@ -76,6 +76,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||
isSensitive: boolean;
|
||||
localOnly: boolean;
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
|
||||
sortKey: string | null;
|
||||
}, moderator?: MiUser): Promise<MiEmoji> {
|
||||
const emoji = await this.emojisRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
|
@ -91,6 +92,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||
isSensitive: data.isSensitive,
|
||||
localOnly: data.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||
sortKey: data.sortKey,
|
||||
});
|
||||
|
||||
if (data.host == null) {
|
||||
|
@ -121,6 +123,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||
isSensitive?: boolean;
|
||||
localOnly?: boolean;
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][];
|
||||
sortKey?: string | null;
|
||||
}, moderator?: MiUser): Promise<void> {
|
||||
const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
|
||||
const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() });
|
||||
|
@ -138,6 +141,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||
publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined,
|
||||
type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined,
|
||||
sortKey: data.sortKey,
|
||||
});
|
||||
|
||||
this.localEmojisCache.refresh();
|
||||
|
|
|
@ -34,6 +34,7 @@ export class EmojiEntityService {
|
|||
localOnly: emoji.localOnly ? true : undefined,
|
||||
isSensitive: emoji.isSensitive ? true : undefined,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined,
|
||||
sortKey: emoji.sortKey,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -62,6 +63,7 @@ export class EmojiEntityService {
|
|||
isSensitive: emoji.isSensitive,
|
||||
localOnly: emoji.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||
sortKey: emoji.sortKey,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -81,4 +81,9 @@ export class MiEmoji {
|
|||
array: true, length: 128, default: '{}',
|
||||
})
|
||||
public roleIdsThatCanBeUsedThisEmojiAsReaction: string[];
|
||||
|
||||
@Column('varchar', {
|
||||
length: 64, nullable: true,
|
||||
})
|
||||
public sortKey: string | null;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,10 @@ export const packedEmojiSimpleSchema = {
|
|||
format: 'id',
|
||||
},
|
||||
},
|
||||
sortKey: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
@ -102,5 +106,9 @@ export const packedEmojiDetailedSchema = {
|
|||
format: 'id',
|
||||
},
|
||||
},
|
||||
sortKey: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
|
|
@ -105,6 +105,7 @@ export class ImportCustomEmojisProcessorService {
|
|||
isSensitive: emojiInfo.isSensitive,
|
||||
localOnly: emojiInfo.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: [],
|
||||
sortKey: emojiInfo.sortKey?.normalize('NFC'),
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error || typeof e === 'string') {
|
||||
|
|
|
@ -56,6 +56,7 @@ export const paramDef = {
|
|||
roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: {
|
||||
type: 'string',
|
||||
} },
|
||||
sortKey: { type: 'string', nullable: true },
|
||||
},
|
||||
required: ['name', 'fileId'],
|
||||
} as const;
|
||||
|
@ -91,6 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
isSensitive: ps.isSensitive ?? false,
|
||||
localOnly: ps.localOnly ?? false,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [],
|
||||
sortKey: ps.sortKey?.normalize('NFC') ?? null,
|
||||
}, me);
|
||||
|
||||
return this.emojiEntityService.packDetailed(emoji);
|
||||
|
|
|
@ -97,6 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
isSensitive: emoji.isSensitive,
|
||||
localOnly: emoji.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||
sortKey: emoji.sortKey?.normalize('NFC') ?? null,
|
||||
}, me);
|
||||
|
||||
return this.emojiEntityService.packDetailed(addedEmoji);
|
||||
|
|
|
@ -56,6 +56,10 @@ export const meta = {
|
|||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
sortKey: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -56,6 +56,7 @@ export const paramDef = {
|
|||
roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: {
|
||||
type: 'string',
|
||||
} },
|
||||
sortKey: { type: 'string', nullable: true },
|
||||
},
|
||||
anyOf: [
|
||||
{ required: ['id'] },
|
||||
|
@ -104,6 +105,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
isSensitive: ps.isSensitive,
|
||||
localOnly: ps.localOnly,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction,
|
||||
sortKey: ps.sortKey,
|
||||
}, me);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ const devConfig: UserConfig = {
|
|||
publicDir: '../assets',
|
||||
base: '/embed',
|
||||
server: {
|
||||
host: 'localhost',
|
||||
host: true,
|
||||
port: 5174,
|
||||
proxy: {
|
||||
'/api': {
|
||||
|
|
|
@ -64,6 +64,7 @@ export function getConfig(): UserConfig {
|
|||
|
||||
server: {
|
||||
port: 5174,
|
||||
host: true
|
||||
},
|
||||
|
||||
plugins: [
|
||||
|
|
|
@ -49,6 +49,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkLink :url="emoji.url" target="_blank">{{ emoji.url }}</MkLink>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.sortKey }}</template>
|
||||
<template #value>
|
||||
<span v-if="emoji.sortKey === null || emoji.sortKey === undefined">{{ i18n.ts.none }}</span>
|
||||
<span v-else :class="$style.alias">{{ emoji.sortKey }}</span>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
|
|
@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
v-for="child in customEmojiTree"
|
||||
:key="`custom:${child.value}`"
|
||||
:initialShown="initialShown"
|
||||
:emojis="computed(() => customEmojis.filter(e => e.category === child.category).map(e => `:${e.name}:`))"
|
||||
:emojis="computed(() => customEmojis.filter(e => e.category === child.category).sort(compareBySortKey).map(e => `:${e.name}:`))"
|
||||
:hasChildSection="child.children.length !== 0"
|
||||
:customEmojiTree="child.children"
|
||||
@chosen="nestedChosen"
|
||||
|
@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
import { ref, computed, Ref } from 'vue';
|
||||
import { CustomEmojiFolderTree, getEmojiName } from '@@/js/emojilist.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { customEmojis } from '@/custom-emojis.js';
|
||||
import { customEmojis, compareBySortKey } from '@/custom-emojis.js';
|
||||
import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
|
@ -91,7 +91,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
v-for="child in customEmojiFolderRoot.children"
|
||||
:key="`custom:${child.value}`"
|
||||
:initialShown="false"
|
||||
:emojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).map(e => `:${e.name}:`))"
|
||||
:emojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).sort(compareBySortKey).map(e => `:${e.name}:`))"
|
||||
:disabledEmojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).filter(e => !canReact(e)).map(e => `:${e.name}:`))"
|
||||
:hasChildSection="child.children.length !== 0"
|
||||
:customEmojiTree="child.children"
|
||||
|
@ -133,7 +133,7 @@ import { isTouchUsing } from '@/scripts/touch.js';
|
|||
import { deviceKind } from '@/scripts/device-kind.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis.js';
|
||||
import { customEmojiCategories, customEmojis, customEmojisMap, compareBySortKey } from '@/custom-emojis.js';
|
||||
import { $i } from '@/account.js';
|
||||
import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js';
|
||||
|
||||
|
|
|
@ -20,6 +20,19 @@ export const customEmojiCategories = computed<[ ...string[], null ]>(() => {
|
|||
return markRaw([...Array.from(categories), null]);
|
||||
});
|
||||
|
||||
export function compareBySortKey(a: Misskey.entities.EmojiSimple, b: Misskey.entities.EmojiSimple): number {
|
||||
if (a.sortKey === b.sortKey) {
|
||||
if (a.name === b.name)
|
||||
return 0;
|
||||
return (a.name > b.name) ? 1 : -1;
|
||||
}
|
||||
if (a.sortKey === null)
|
||||
return 1;
|
||||
if (b.sortKey === null)
|
||||
return -1;
|
||||
return (a.sortKey > b.sortKey) ? 1 : -1;
|
||||
}
|
||||
|
||||
export const customEmojisMap = new Map<string, Misskey.entities.EmojiSimple>();
|
||||
watch(customEmojis, emojis => {
|
||||
customEmojisMap.clear();
|
||||
|
|
|
@ -68,6 +68,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</MkFolder>
|
||||
<MkSwitch v-model="isSensitive">isSensitive</MkSwitch>
|
||||
<MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch>
|
||||
<MkInput v-model="sortKey" autocapitalize="off">
|
||||
<template #label>{{ i18n.ts.sortKey }}</template>
|
||||
</MkInput>
|
||||
<MkButton v-if="emoji" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
|
@ -107,6 +110,7 @@ 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 sortKey = ref<string>(props.emoji ? (props.emoji.sortKey ?? '') : '');
|
||||
const file = ref<Misskey.entities.DriveFile>();
|
||||
|
||||
watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
|
||||
|
@ -153,6 +157,7 @@ async function done() {
|
|||
isSensitive: isSensitive.value,
|
||||
localOnly: localOnly.value,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id),
|
||||
sortKey: sortKey.value === '' ? null : sortKey.value,
|
||||
};
|
||||
|
||||
if (file.value) {
|
||||
|
|
|
@ -32,7 +32,7 @@ const devConfig: UserConfig = {
|
|||
publicDir: '../assets',
|
||||
base: './',
|
||||
server: {
|
||||
host: 'localhost',
|
||||
host: true,
|
||||
port: 5173,
|
||||
proxy: {
|
||||
'/api': {
|
||||
|
|
|
@ -66,6 +66,7 @@ export function getConfig(): UserConfig {
|
|||
|
||||
server: {
|
||||
port: 5173,
|
||||
host: true,
|
||||
headers: { // なんか効かない
|
||||
'X-Frame-Options': 'DENY',
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue