[38072] Disable already selected projects in notification settings

https://community.openproject.org/wp/38072
This commit is contained in:
Oliver Günther
2021-07-20 21:45:13 +02:00
parent bb918c6793
commit d103b709ca
7 changed files with 63 additions and 16 deletions
+1 -1
View File
@@ -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
+1
View File
@@ -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"
@@ -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"
>
<ng-template op-autocompleter-option-tmp let-item let-index="index" let-search="search">
<div
*ngIf="item"
class="ng-option-label"
>
<!--Selectable option-->
<div [ngOptionHighlight]="search">{{ item.name }}</div>
<!-- Project already selected -->
<div
*ngIf="item.disabled"
class="ellipsis"
>{{ text.already_selected }}</div>
</div>
</ng-template>
</op-autocompleter>
</div>
@@ -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<HalSourceLink>();
@Output() selected = new EventEmitter<HalSourceLink>();
/** 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<any[]> => this.autocomplete(query),
getOptionsFn: (query:string):Observable<unknown[]> => 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<NotificationSettingProjectOption[]> {
if (!term) {
return of([]);
}
private autocomplete(term:string|null):Observable<NotificationSettingProjectOption[]> {
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) }
),
)),
);
}
}
@@ -77,7 +77,7 @@
<op-notification-setting-inline-create
*ngIf="userId"
[userId]="userId"
(onSelect)="addRow($event)"
(selected)="addRow($event)"
data-qa-selector="notification-setting-inline-create"
></op-notification-setting-inline-create>
</div>
@@ -25,6 +25,15 @@ export class UserPreferencesQuery extends Query<UserPreferencesModel> {
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) {
@@ -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