diff --git a/tabby-settings/src/components/editProfileModal.component.pug b/tabby-settings/src/components/editProfileModal.component.pug
index 7869099b..0619345d 100644
--- a/tabby-settings/src/components/editProfileModal.component.pug
+++ b/tabby-settings/src/components/editProfileModal.component.pug
@@ -24,8 +24,10 @@
type='text',
alwaysVisibleTypeahead,
placeholder='Ungrouped',
- [(ngModel)]='profile.group',
+ [(ngModel)]='profileGroup',
[ngbTypeahead]='groupTypeahead',
+ [inputFormatter]="groupFormatter",
+ [resultFormatter]="groupFormatter",
)
.mb-3(*ngIf='!defaultsMode')
diff --git a/tabby-settings/src/components/editProfileModal.component.ts b/tabby-settings/src/components/editProfileModal.component.ts
index 90636672..a57ee865 100644
--- a/tabby-settings/src/components/editProfileModal.component.ts
+++ b/tabby-settings/src/components/editProfileModal.component.ts
@@ -2,7 +2,8 @@
import { Observable, OperatorFunction, debounceTime, map, distinctUntilChanged } from 'rxjs'
import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, Injector } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
-import { ConfigProxy, ConfigService, Profile, ProfileProvider, ProfileSettingsComponent, ProfilesService, TAB_COLORS } from 'tabby-core'
+import { ConfigProxy, ConfigService, PartialProfileGroup, Profile, ProfileProvider, ProfileSettingsComponent, ProfilesService, TAB_COLORS, ProfileGroup } from 'tabby-core'
+import { v4 as uuidv4 } from 'uuid'
const iconsData = require('../../../tabby-core/src/icons.json')
const iconsClassList = Object.keys(iconsData).map(
@@ -20,7 +21,8 @@ export class EditProfileModalComponent
{
@Input() profileProvider: ProfileProvider
@Input() settingsComponent: new () => ProfileSettingsComponent
@Input() defaultsMode = false
- groupNames: string[]
+ @Input() profileGroup: PartialProfileGroup | string | undefined
+ groups: PartialProfileGroup[]
@ViewChild('placeholder', { read: ViewContainerRef }) placeholder: ViewContainerRef
private _profile: Profile
@@ -33,11 +35,12 @@ export class EditProfileModalComponent {
config: ConfigService,
private modalInstance: NgbActiveModal,
) {
- this.groupNames = [...new Set(
- (config.store.profiles as Profile[])
- .map(x => x.group)
- .filter(x => !!x),
- )].sort() as string[]
+ if (!this.defaultsMode) {
+ this.profilesService.getProfileGroups().then(groups => {
+ this.groups = groups
+ this.profileGroup = groups.find(g => g.id === this.profile.group)
+ })
+ }
}
colorsAutocomplete = text$ => text$.pipe(
@@ -72,13 +75,15 @@ export class EditProfileModalComponent
{
}
}
- groupTypeahead = (text$: Observable) =>
+ groupTypeahead: OperatorFunction[]> = (text$: Observable) =>
text$.pipe(
debounceTime(200),
distinctUntilChanged(),
- map(q => this.groupNames.filter(x => !q || x.toLowerCase().includes(q.toLowerCase()))),
+ map(q => this.groups.filter(g => !q || g.name.toLowerCase().includes(q.toLowerCase()))),
)
+ groupFormatter = (g: PartialProfileGroup) => g.name
+
iconSearch: OperatorFunction = (text$: Observable) =>
text$.pipe(
debounceTime(200),
@@ -86,7 +91,20 @@ export class EditProfileModalComponent {
)
save () {
- this.profile.group ||= undefined
+ if (!this.profileGroup) {
+ this.profile.group = undefined
+ } else {
+ if (typeof this.profileGroup === 'string') {
+ const newGroup: PartialProfileGroup = {
+ id: uuidv4(),
+ name: this.profileGroup
+ }
+ this.groups.push(newGroup)
+ this.profileGroup = newGroup
+ }
+ this.profile.group = this.profileGroup.id
+ }
+
this.settingsComponentInstance?.save?.()
this.profile.__cleanup()
this.modalInstance.close(this._profile)
diff --git a/tabby-settings/src/components/profilesSettingsTab.component.ts b/tabby-settings/src/components/profilesSettingsTab.component.ts
index 6508e912..2ede9f2d 100644
--- a/tabby-settings/src/components/profilesSettingsTab.component.ts
+++ b/tabby-settings/src/components/profilesSettingsTab.component.ts
@@ -4,16 +4,9 @@ import slugify from 'slugify'
import deepClone from 'clone-deep'
import { Component, Inject } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
-import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider, TranslateService, Platform, AppHotkeyProvider } from 'tabby-core'
+import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider, TranslateService, Platform, AppHotkeyProvider, ProfileGroup, PartialProfileGroup } from 'tabby-core'
import { EditProfileModalComponent } from './editProfileModal.component'
-interface ProfileGroup {
- name?: string
- profiles: PartialProfile[]
- editable: boolean
- collapsed: boolean
-}
-
_('Filter')
_('Ungrouped')
@@ -26,7 +19,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
profiles: PartialProfile[] = []
builtinProfiles: PartialProfile[] = []
templateProfiles: PartialProfile[] = []
- profileGroups: ProfileGroup[]
+ profileGroups: PartialProfileGroup[]
filter = ''
Platform = Platform
@@ -158,55 +151,27 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
}
}
- refresh (): void {
+ async refresh (): Promise {
this.profiles = this.config.store.profiles
- this.profileGroups = []
- const profileGroupCollapsed = JSON.parse(window.localStorage.profileGroupCollapsed ?? '{}')
-
- for (const profile of this.profiles) {
- // Group null, undefined and empty together
- // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
- let group = this.profileGroups.find(x => x.name === (profile.group || ''))
- if (!group) {
- group = {
- // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
- name: profile.group || '',
- profiles: [],
- editable: true,
- collapsed: profileGroupCollapsed[profile.group ?? ''] ?? false,
- }
- this.profileGroups.push(group)
- }
- group.profiles.push(profile)
- }
-
- this.profileGroups.sort((a, b) => a.name?.localeCompare(b.name ?? '') ?? -1)
-
- const builtIn = {
- name: this.translate.instant('Built-in'),
- profiles: this.builtinProfiles,
- editable: false,
- collapsed: false,
- }
- builtIn.collapsed = profileGroupCollapsed[builtIn.name ?? ''] ?? false
- this.profileGroups.push(builtIn)
+ const groups = await this.profilesService.getProfileGroups(true, true)
+ groups.sort((a, b) => a.name.localeCompare(b.name))
+ groups.sort((a, b) => (a.id === 'built-in' ? 1 : 0) - (b.id === 'built-in' ? 1 : 0))
+ groups.sort((a, b) => (a.id === 'ungrouped' ? 0 : 1) - (b.id === 'ungrouped' ? 0 : 1))
+ this.profileGroups = groups
}
- async editGroup (group: ProfileGroup): Promise {
+ async editGroup (group: PartialProfileGroup): Promise {
const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = this.translate.instant('New name')
modal.componentInstance.value = group.name
const result = await modal.result
if (result) {
- for (const profile of this.profiles.filter(x => x.group === group.name)) {
- profile.group = result.value
- }
- this.config.store.profiles = this.profiles
+ group.name = result.value
await this.config.save()
}
}
- async deleteGroup (group: ProfileGroup): Promise {
+ async deleteGroup (group: PartialProfileGroup): Promise {
if ((await this.platform.showMessageBox(
{
type: 'warning',
@@ -231,18 +196,18 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
cancelId: 0,
},
)).response === 0) {
- for (const profile of this.profiles.filter(x => x.group === group.name)) {
+ for (const profile of this.profiles.filter(x => x.group === group.id)) {
delete profile.group
}
} else {
- this.config.store.profiles = this.config.store.profiles.filter(x => x.group !== group.name)
+ this.config.store.profiles = this.config.store.profiles.filter(x => x.group !== group.id)
}
await this.config.save()
}
}
- isGroupVisible (group: ProfileGroup): boolean {
- return !this.filter || group.profiles.some(x => this.isProfileVisible(x))
+ isGroupVisible (group: PartialProfileGroup): boolean {
+ return !this.filter || (group.profiles ?? []).some(x => this.isProfileVisible(x))
}
isProfileVisible (profile: PartialProfile): boolean {
@@ -270,11 +235,9 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
}[this.profilesService.providerForProfile(profile)?.id ?? ''] ?? 'warning'
}
- toggleGroupCollapse (group: ProfileGroup): void {
+ toggleGroupCollapse (group: PartialProfileGroup): void {
group.collapsed = !group.collapsed
- const profileGroupCollapsed = JSON.parse(window.localStorage.profileGroupCollapsed ?? '{}')
- profileGroupCollapsed[group.name ?? ''] = group.collapsed
- window.localStorage.profileGroupCollapsed = JSON.stringify(profileGroupCollapsed)
+ this.profilesService.saveProfileGroupCollapse(group)
}
async editDefaults (provider: ProfileProvider): Promise {