mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 07:24:13 +00:00 
			
		
		
		
	Merge pull request 'Sort custom emoticons in picker by sortKey (fixes #18)' (#22) from emoticon-sorting into dev
Reviewed-on: https://codeberg.org/yeentown/barkey/pulls/22 Reviewed-by: zima <zima@noreply.codeberg.org>
This commit is contained in:
		
						commit
						dd90724c54
					
				
					 19 changed files with 160 additions and 16 deletions
				
			
		
							
								
								
									
										42
									
								
								locales/index.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								locales/index.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -11374,6 +11374,48 @@ export interface Locale extends ILocale {
 | 
			
		|||
     * Remote followers may have incomplete or outdated activity
 | 
			
		||||
     */
 | 
			
		||||
    "remoteFollowersWarning": string;
 | 
			
		||||
    /**
 | 
			
		||||
     * Sort key
 | 
			
		||||
     */
 | 
			
		||||
    "sortKey": string;
 | 
			
		||||
    "_unicodeEmoji": {
 | 
			
		||||
        /**
 | 
			
		||||
         * Smileys
 | 
			
		||||
         */
 | 
			
		||||
        "face": string;
 | 
			
		||||
        /**
 | 
			
		||||
         * People
 | 
			
		||||
         */
 | 
			
		||||
        "people": string;
 | 
			
		||||
        /**
 | 
			
		||||
         * Animals & nature
 | 
			
		||||
         */
 | 
			
		||||
        "animals_and_nature": string;
 | 
			
		||||
        /**
 | 
			
		||||
         * Food & drink
 | 
			
		||||
         */
 | 
			
		||||
        "food_and_drink": string;
 | 
			
		||||
        /**
 | 
			
		||||
         * Activity
 | 
			
		||||
         */
 | 
			
		||||
        "activity": string;
 | 
			
		||||
        /**
 | 
			
		||||
         * Travel & places
 | 
			
		||||
         */
 | 
			
		||||
        "travel_and_places": string;
 | 
			
		||||
        /**
 | 
			
		||||
         * Objects
 | 
			
		||||
         */
 | 
			
		||||
        "objects": string;
 | 
			
		||||
        /**
 | 
			
		||||
         * Symbols
 | 
			
		||||
         */
 | 
			
		||||
        "symbols": string;
 | 
			
		||||
        /**
 | 
			
		||||
         * Flags
 | 
			
		||||
         */
 | 
			
		||||
        "flags": string;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
declare const locales: {
 | 
			
		||||
    [lang: string]: Locale;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,9 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
			
		|||
<template>
 | 
			
		||||
<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと -->
 | 
			
		||||
<!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) -->
 | 
			
		||||
<section v-if="!hasChildSection" v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--divider);">
 | 
			
		||||
	<header class="_acrylic" @click="shown = !shown">
 | 
			
		||||
		<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ph-smiley-sticker ph-bold ph-lg"></i>:{{ emojis.length }})
 | 
			
		||||
<section v-if="!hasChildSection" :data-shown="shown" v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--divider);">
 | 
			
		||||
	<header class="_acrylic" :data-shown="shown" @click="shown = !shown">
 | 
			
		||||
		<i class="toggle ti-fw" :class="shown ? 'ph-bold ph-caret-down' : 'ph-bold ph-caret-right'"></i> <slot></slot>
 | 
			
		||||
		<span class="emoji-count"><i class="ph-smiley ph-bold ph-lg"></i> {{ emojis.length }}</span>
 | 
			
		||||
	</header>
 | 
			
		||||
	<div v-if="shown" class="body">
 | 
			
		||||
		<button
 | 
			
		||||
| 
						 | 
				
			
			@ -26,16 +27,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
			
		|||
	</div>
 | 
			
		||||
</section>
 | 
			
		||||
<!-- フォルダの中にはカスタム絵文字やフォルダがある -->
 | 
			
		||||
<section v-else v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--divider);">
 | 
			
		||||
	<header class="_acrylic" @click="shown = !shown">
 | 
			
		||||
		<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{ customEmojiTree?.length }} <i class="ph-smiley-sticker ph-bold ph-lg ti-fw"></i>:{{ emojis.length }})
 | 
			
		||||
<section v-else v-panel :data-shown="shown" style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--divider);">
 | 
			
		||||
	<header class="_acrylic" :data-shown="shown" @click="shown = !shown">
 | 
			
		||||
		<i class="toggle ti-fw" :class="shown ? 'ph-bold ph-caret-down' : 'ph-bold ph-caret-right'"></i> <slot></slot>
 | 
			
		||||
		<span class="emoji-count"><i class="ph-folder ph-bold"></i> {{ customEmojiTree?.length }}</span>
 | 
			
		||||
		<span class="emoji-count" v-if="emojis.length > 0"><i class="ph-smiley ph-bold"></i> {{ emojis.length }}</span>
 | 
			
		||||
	</header>
 | 
			
		||||
	<div v-if="shown" style="padding-left: 9px;">
 | 
			
		||||
		<MkEmojiPickerSection
 | 
			
		||||
			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"
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
			
		|||
			{{ child.value || i18n.ts.other }}
 | 
			
		||||
		</MkEmojiPickerSection>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div v-if="shown" class="body">
 | 
			
		||||
	<div v-if="shown && emojis.length > 0" class="body">
 | 
			
		||||
		<button
 | 
			
		||||
			v-for="emoji in emojis"
 | 
			
		||||
			:key="emoji"
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +67,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"
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
			
		|||
		</div>
 | 
			
		||||
		<div v-once class="group">
 | 
			
		||||
			<header class="_acrylic">{{ i18n.ts.emoji }}</header>
 | 
			
		||||
			<XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ category }}</XSection>
 | 
			
		||||
			<XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ i18n.ts._unicodeEmoji[category] ?? category }}</XSection>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="tabs">
 | 
			
		||||
| 
						 | 
				
			
			@ -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';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -186,11 +186,13 @@ function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): Cu
 | 
			
		|||
	const parts = input.split('/').map(p => p.trim());
 | 
			
		||||
	let currentNode: CustomEmojiFolderTree = root;
 | 
			
		||||
 | 
			
		||||
	let currentPath = [];
 | 
			
		||||
	for (const part of parts) {
 | 
			
		||||
		currentPath.push(part);
 | 
			
		||||
		let existingNode = currentNode.children.find((node) => node.value === part);
 | 
			
		||||
 | 
			
		||||
		if (!existingNode) {
 | 
			
		||||
			const newNode: CustomEmojiFolderTree = { value: part, category: input, children: [] };
 | 
			
		||||
			const newNode: CustomEmojiFolderTree = { value: part, category: currentPath.join("/"), children: [] };
 | 
			
		||||
			currentNode.children.push(newNode);
 | 
			
		||||
			existingNode = newNode;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -501,6 +503,8 @@ defineExpose({
 | 
			
		|||
 | 
			
		||||
	display: flex;
 | 
			
		||||
	flex-direction: column;
 | 
			
		||||
	/* Required to make header background blur work */
 | 
			
		||||
	contain: layout style;
 | 
			
		||||
 | 
			
		||||
	&.s1 {
 | 
			
		||||
		--eachSize: 40px;
 | 
			
		||||
| 
						 | 
				
			
			@ -698,18 +702,34 @@ defineExpose({
 | 
			
		|||
 | 
			
		||||
		::v-deep(section) {
 | 
			
		||||
			> header {
 | 
			
		||||
				position: sticky;
 | 
			
		||||
				top: 0;
 | 
			
		||||
				left: 0;
 | 
			
		||||
				line-height: 28px;
 | 
			
		||||
				z-index: 1;
 | 
			
		||||
				padding: 0 8px;
 | 
			
		||||
				font-size: 12px;
 | 
			
		||||
				cursor: pointer;
 | 
			
		||||
 | 
			
		||||
				/* Closed sections should not have sticky headers. */
 | 
			
		||||
				&:not([data-shown=false]) {
 | 
			
		||||
					position: sticky;
 | 
			
		||||
					z-index: 1;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				&:hover {
 | 
			
		||||
					color: var(--accent);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				> .emoji-count {
 | 
			
		||||
					float: right;
 | 
			
		||||
					margin-left: 8px;
 | 
			
		||||
					color: var(--fgTransparent);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Open subsections should have a z-index above the parent's header. This prevents the blur effect from getting muddy. */
 | 
			
		||||
			&[data-shown=true] {
 | 
			
		||||
				position: relative;
 | 
			
		||||
				z-index: 2;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			> .body {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -397,3 +397,16 @@ _auth:
 | 
			
		|||
  allowed: "Allowed"
 | 
			
		||||
_announcement:
 | 
			
		||||
  new: "New"
 | 
			
		||||
 | 
			
		||||
sortKey: "Sort key"
 | 
			
		||||
 | 
			
		||||
_unicodeEmoji:
 | 
			
		||||
  face: "Smileys"
 | 
			
		||||
  people: "People"
 | 
			
		||||
  animals_and_nature: "Animals & nature"
 | 
			
		||||
  food_and_drink: "Food & drink"
 | 
			
		||||
  activity: "Activity"
 | 
			
		||||
  travel_and_places: "Travel & places"
 | 
			
		||||
  objects: "Objects"
 | 
			
		||||
  symbols: "Symbols"
 | 
			
		||||
  flags: "Flags"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue