diff --git a/.editorconfig b/.editorconfig index 9775128ca33..5dc2447e503 100644 --- a/.editorconfig +++ b/.editorconfig @@ -433,7 +433,7 @@ ij_typescript_import_prefer_absolute_path = global ij_typescript_import_sort_members = true ij_typescript_import_sort_module_name = false ij_typescript_import_use_node_resolution = true -ij_typescript_imports_wrap = on_every_item +ij_typescript_imports_wrap = always ij_typescript_indent_case_from_switch = true ij_typescript_indent_chained_calls = true ij_typescript_indent_package_children = 0 diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml index 03b3c863c7d..e4c3a4158f1 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -582,6 +582,7 @@ en: watched: 'I am watching' all: 'All events' add: 'Add setting for project' + already_selected: 'This project is already selected' remove_projects: 'Remove notifications for projects' title: "Notification settings" self_notify: "I want to be notified of changes that I make myself" diff --git a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.html b/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.html index 07d64e2269c..a3281d413ae 100644 --- a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.html +++ b/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.html @@ -14,10 +14,26 @@ [placeholder]="text.please_select" (change)="selectProject($event)" (keydown.escape)="active = false" + [fetchDataDirectly]="true" [filters]="autocompleterOptions.filters" [resource]="autocompleterOptions.resource" [getOptionsFn]="autocompleterOptions.getOptionsFn" appendTo="body" > + + + + {{ item.name }} + + + {{ text.already_selected }} + + \ No newline at end of file diff --git a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts b/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts index 6baa16f6af8..60c699080eb 100644 --- a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts +++ b/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts @@ -1,12 +1,20 @@ import { - EventEmitter, Component, ChangeDetectionStrategy, Output, Input, + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { Observable, of } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { + map, + withLatestFrom, +} from 'rxjs/operators'; import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; import { HalSourceLink } from 'core-app/features/hal/resources/hal-resource'; +import { UserPreferencesQuery } from 'core-app/features/user-preferences/state/user-preferences.query'; export interface NotificationSettingProjectOption { name:string; @@ -21,7 +29,7 @@ export interface NotificationSettingProjectOption { export class NotificationSettingInlineCreateComponent { @Input() userId:string; - @Output() onSelect = new EventEmitter(); + @Output() selected = new EventEmitter(); /** Active inline-create mode */ active = false; @@ -29,41 +37,47 @@ export class NotificationSettingInlineCreateComponent { text = { add_setting: this.I18n.t('js.notifications.settings.add'), please_select: this.I18n.t('js.placeholders.selection'), + already_selected: this.I18n.t('js.notifications.settings.already_selected'), }; public autocompleterOptions = { filters: [], resource: 'default', - getOptionsFn: (query:string):Observable => this.autocomplete(query), + getOptionsFn: (query:string):Observable => this.autocomplete(query), }; constructor( private I18n:I18nService, private apiV3Service:APIV3Service, + private query:UserPreferencesQuery, ) { } - selectProject($event:NotificationSettingProjectOption) { - this.onSelect.emit({ title: $event.name, href: $event.href }); + selectProject($event:NotificationSettingProjectOption):void { + this.selected.emit({ title: $event.name, href: $event.href }); this.active = false; } - private autocomplete(term:string):Observable { - if (!term) { - return of([]); - } - + private autocomplete(term:string|null):Observable { const filters = new ApiV3FilterBuilder() - .add('name_and_identifier', '~', [term]) .add('visible', '=', [this.userId]); + if (term) { + filters.add('name_and_identifier', '~', [term]); + } + return this .apiV3Service .projects .filtered(filters) .get() .pipe( - map((collection) => collection.elements.map((project) => ({ href: project.href!, name: project.name }))), + withLatestFrom(this.query.selectedProjects$), + map(([collection, selected]) => collection.elements.map( + (project) => ( + { href: project.href || '', name: project.name, disabled: selected.has(project.href) } + ), + )), ); } } diff --git a/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.html b/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.html index be01ba4a9f0..bfb7ebdf6bd 100644 --- a/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.html +++ b/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.html @@ -77,7 +77,7 @@ diff --git a/frontend/src/app/features/user-preferences/state/user-preferences.query.ts b/frontend/src/app/features/user-preferences/state/user-preferences.query.ts index 6c04e8f96bc..7cf0f1d62b7 100644 --- a/frontend/src/app/features/user-preferences/state/user-preferences.query.ts +++ b/frontend/src/app/features/user-preferences/state/user-preferences.query.ts @@ -25,6 +25,15 @@ export class UserPreferencesQuery extends Query { map((settings) => settings.filter((notification) => notification._links.project.href !== null)), ); + /** Selected projects */ + selectedProjects$ = this + .notificationSettings$ + .pipe( + map((notifications) => ( + new Set(notifications.map((setting) => setting._links.project?.href)) + )), + ); + preferences$ = this.select(); constructor(protected store:UserPreferencesStore) { diff --git a/spec/features/users/notifications/shared_examples.rb b/spec/features/users/notifications/shared_examples.rb index cda75f31896..43cfc13fa88 100644 --- a/spec/features/users/notifications/shared_examples.rb +++ b/spec/features/users/notifications/shared_examples.rb @@ -59,6 +59,13 @@ shared_examples 'notification settings workflow' do expect(in_app.mentioned).to be_truthy expect(in_app.watched).to be_falsey expect(in_app.all).to be_truthy + + # Trying to add the same project again will not be possible (Regression #38072) + click_button 'Add setting for project' + container = page.find('[data-qa-selector="notification-setting-inline-create"] ng-select') + settings_page.search_autocomplete container, query: project.name, results_selector: 'body' + expect(page).to have_text 'This project is already selected' + expect(page).to have_selector('.ng-option-disabled', text: project.name) end end end