diff --git a/komga-webui/src/locales/en.json b/komga-webui/src/locales/en.json index 740ed6569..d953cc87e 100644 --- a/komga-webui/src/locales/en.json +++ b/komga-webui/src/locales/en.json @@ -1034,10 +1034,17 @@ "more": "More titles" }, "ui_settings": { + "general": "General", "label_oauth2_auto_login": "Automatic OAuth2 login", "label_oauth2_hide_login": "Hide login fields if OAuth2 is enabled", "label_poster_blur_unread": "Blur poster for unread books and series", "label_poster_stretch": "Stretch poster to fit card", + "label_series_groups": "Series grouping", + "section_oauth2": "OAuth2", + "series_groups": { + "alpha": "Alphabetical", + "japanese": "Gojūon (Japanese)" + }, "tooltip_oauth2_auto_login": "Requires a single OAuth2 provider, and 'hide login fields' enabled" }, "updates": { diff --git a/komga-webui/src/types/komga-clientsettings.ts b/komga-webui/src/types/komga-clientsettings.ts index 596efecaa..6e5ecd2d8 100644 --- a/komga-webui/src/types/komga-clientsettings.ts +++ b/komga-webui/src/types/komga-clientsettings.ts @@ -18,6 +18,7 @@ export enum CLIENT_SETTING { WEBUI_POSTER_STRETCH = 'webui.poster.stretch', WEBUI_POSTER_BLUR_UNREAD = 'webui.poster.blur_unread', WEBUI_LIBRARIES = 'webui.libraries', + WEBUI_SERIES_GROUPS = 'webui.series_groups', } export interface ClientSettingLibrary { @@ -29,3 +30,85 @@ export interface ClientSettingLibraryUpdate { libraryId: string, patch: ClientSettingLibrary, } + +export interface ClientSettingsSeriesGroup { + name: string, + groups: Record +} + +export const SERIES_GROUP_ALPHA = { + name: 'alpha', + groups: { + 'A': ['A'], + 'B': ['B'], + 'C': ['C'], + 'D': ['D'], + 'E': ['E'], + 'F': ['F'], + 'G': ['G'], + 'H': ['H'], + 'I': ['I'], + 'J': ['J'], + 'K': ['K'], + 'L': ['L'], + 'M': ['M'], + 'N': ['N'], + 'O': ['O'], + 'P': ['P'], + 'Q': ['Q'], + 'R': ['R'], + 'S': ['S'], + 'T': ['T'], + 'U': ['U'], + 'V': ['V'], + 'W': ['W'], + 'X': ['X'], + 'Y': ['Y'], + 'Z': ['Z'], + }, +} as ClientSettingsSeriesGroup + +export const SERIES_GROUP_JAPANESE = { + name: 'japanese', + groups: { + 'あ': ['あ', 'ア'], + 'い': ['い', 'イ'], + 'う': ['う', 'ゔ', 'ウ', 'ヴ'], + 'え': ['え', 'エ'], + 'お': ['お', 'オ'], + 'か': ['か', 'が', 'カ', 'ガ'], + 'き': ['き', 'ぎ', 'キ', 'ギ'], + 'く': ['く', 'ぐ', 'ク', 'グ'], + 'け': ['け', 'げ', 'ケ', 'ゲ'], + 'こ': ['こ', 'ご', 'コ', 'ゴ'], + 'さ': ['さ', 'ざ', 'サ', 'ザ'], + 'し': ['し', 'じ', 'シ', 'ジ'], + 'す': ['す', 'ず', 'ス', 'ズ'], + 'せ': ['せ', 'ぜ', 'セ', 'ゼ'], + 'そ': ['そ', 'ぞ', 'ソ', 'ゾ'], + 'た': ['た', 'だ', 'タ', 'ダ'], + 'ち': ['ち', 'ぢ', 'チ', 'ヂ'], + 'つ': ['つ', 'づ', 'ツ', 'ズ'], + 'て': ['て', 'で', 'テ', 'デ'], + 'と': ['と', 'ど', 'ト', 'ド'], + 'は': ['は', 'ば', 'ぱ', 'ハ', 'バ', 'パ'], + 'ひ': ['ひ', 'び', 'ぴ', 'ヒ', 'ビ', 'ピ'], + 'ふ': ['ふ', 'ぶ', 'ぷ', 'フ', 'ブ', 'プ'], + 'へ': ['へ', 'べ', 'ぺ', 'ヘ', 'ベ', 'ベ'], + 'ほ': ['ほ', 'ぼ', 'ぽ', 'ホ', 'ボ', 'ポ'], + 'ま': ['ま', 'マ'], + 'み': ['み', 'ミ'], + 'む': ['む', 'ム'], + 'め': ['め', 'メ'], + 'も': ['も', 'モ'], + 'や': ['や', 'ヤ'], + 'ゆ': ['ゆ', 'ユ'], + 'よ': ['よ', 'ヨ'], + 'ら': ['ら', 'ラ'], + 'り': ['り', 'リ'], + 'る': ['る', 'ル'], + 'れ': ['れ', 'レ'], + 'ろ': ['ろ', 'ロ'], + 'わ': ['わ', 'ワ'], + }, +} as ClientSettingsSeriesGroup diff --git a/komga-webui/src/views/BrowseLibraries.vue b/komga-webui/src/views/BrowseLibraries.vue index d88de0b5f..19963aebb 100644 --- a/komga-webui/src/views/BrowseLibraries.vue +++ b/komga-webui/src/views/BrowseLibraries.vue @@ -74,7 +74,7 @@ { + let s: Record + try { + s = (JSON.parse(this.$store.getters.getClientSettings[CLIENT_SETTING.WEBUI_SERIES_GROUPS].value) as ClientSettingsSeriesGroup).groups + } catch (_) { + s = SERIES_GROUP_ALPHA.groups + } + return s + }, + seriesGroupingKeys(): string[] { + return ['ALL', '#', ...this.$_.keys(this.seriesGrouping)] + }, + seriesGroupingValues(): string[] { + return this.$_(this.seriesGrouping).values().flatten().map(this.$_.lowerCase).toArray() as unknown as string[] + }, library(): LibraryDto | undefined { return this.getLibraryLazy(this.libraryId) }, @@ -337,11 +352,13 @@ export default Vue.extend({ symbolCondition(): SearchConditionSeries | undefined { if (this.selectedSymbol === 'ALL') return undefined if (this.selectedSymbol === '#') return new SearchConditionAllOfSeries( - this.alphabeticalNavigation - .filter(it => it !== 'ALL' && it !== '#') + this.seriesGroupingValues .map(it => new SearchConditionTitleSort(new SearchOperatorDoesNotBeginWith(it))), ) - return new SearchConditionTitleSort(new SearchOperatorBeginsWith(this.selectedSymbol)) + return new SearchConditionAnyOfSeries( + this.seriesGrouping[this.selectedSymbol] + .map(it => new SearchConditionTitleSort(new SearchOperatorBeginsWith(it))), + ) }, itemContext(): ItemContext[] { if (this.sortActive.key === 'booksMetadata.releaseDate') return [ItemContext.RELEASE_DATE] @@ -737,11 +754,11 @@ export default Vue.extend({ condition: new SearchConditionAllOfSeries(groupConditions), } as SeriesSearch) const nonAlpha = seriesGroups - .filter((g) => !(/[a-zA-Z]/).test(g.group)) + .filter((g) => !this.seriesGroupingValues.includes(g.group)) .reduce((a, b) => a + b.count, 0) const all = seriesGroups.reduce((a, b) => a + b.count, 0) this.seriesGroups = [ - ...seriesGroups.filter((g) => (/[a-zA-Z]/).test(g.group)), + ...seriesGroups.filter((g) => this.seriesGroupingValues.includes(g.group)), {group: '#', count: nonAlpha} as GroupCountDto, {group: 'ALL', count: all} as GroupCountDto, ] diff --git a/komga-webui/src/views/UISettings.vue b/komga-webui/src/views/UISettings.vue index 9a841ecab..c9fd8391a 100644 --- a/komga-webui/src/views/UISettings.vue +++ b/komga-webui/src/views/UISettings.vue @@ -2,6 +2,23 @@ + {{ $t('ui_settings.general') }} + + + + + + + + + + {{ $t('ui_settings.section_oauth2') }} + import Vue from 'vue' -import {CLIENT_SETTING, ClientSettingGlobalUpdateDto} from '@/types/komga-clientsettings' +import { + CLIENT_SETTING, + ClientSettingGlobalUpdateDto, + ClientSettingsSeriesGroup, + SERIES_GROUP_ALPHA, + SERIES_GROUP_JAPANESE, +} from '@/types/komga-clientsettings' export default Vue.extend({ name: 'UISettings', @@ -56,12 +79,14 @@ export default Vue.extend({ form: { oauth2HideLogin: false, oauth2AutoLogin: false, + seriesGroups: 'alpha', }, }), validations: { form: { oauth2HideLogin: {}, oauth2AutoLogin: {}, + seriesGroups: {}, }, }, mounted() { @@ -80,6 +105,10 @@ export default Vue.extend({ await this.$store.dispatch('getClientSettingsGlobal') this.form.oauth2HideLogin = this.$store.state.komgaSettings.clientSettingsGlobal[CLIENT_SETTING.WEBUI_OAUTH2_HIDE_LOGIN]?.value === 'true' this.form.oauth2AutoLogin = this.$store.state.komgaSettings.clientSettingsGlobal[CLIENT_SETTING.WEBUI_OAUTH2_AUTO_LOGIN]?.value === 'true' + try { + this.form.seriesGroups = (JSON.parse(this.$store.state.komgaSettings.clientSettingsGlobal[CLIENT_SETTING.WEBUI_SERIES_GROUPS]?.value) as ClientSettingsSeriesGroup)?.name + } catch (_) { + } this.$v.form.$reset() }, async saveSettings() { @@ -96,6 +125,12 @@ export default Vue.extend({ allowUnauthorized: true, } + if (this.$v.form?.seriesGroups?.$dirty) + newSettings[CLIENT_SETTING.WEBUI_SERIES_GROUPS] = { + value: JSON.stringify(this.form.seriesGroups === 'alpha' ? SERIES_GROUP_ALPHA : SERIES_GROUP_JAPANESE), + allowUnauthorized: false, + } + await this.$komgaSettings.updateClientSettingGlobal(newSettings) await this.refreshSettings()