Skip to content

Commit ba022ef

Browse files
Mutugiiidogi
andauthored
all: smoother sorting (fixes #9929) (#9933)
Co-authored-by: dogi <dogi@users.noreply.github.com>
1 parent 3f2f3b4 commit ba022ef

7 files changed

Lines changed: 70 additions & 56 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "planet",
33
"license": "AGPL-3.0",
4-
"version": "0.22.84",
4+
"version": "0.22.85",
55
"myplanet": {
66
"latest": "v0.55.45",
77
"min": "v0.53.91"

src/app/courses/courses.component.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { map, switchMap, takeUntil } from 'rxjs/operators';
1414
import { FuzzySearchService } from '../shared/fuzzy-search.service';
1515
import {
1616
filterSpecificFields, composeFilterFunctions, createDeleteArray, filterTags,
17-
commonSortingDataAccessor, selectedOutOfFilter, filterShelf, trackById, filterIds, filterAdvancedSearch, filterSpecificFieldsHybrid
17+
commonSortingDataAccessor, filterShelf, trackById, filterIds, filterAdvancedSearch, filterSpecificFieldsHybrid
1818
} from '../shared/table-helpers';
1919
import * as constants from './constants';
2020
import { languages } from '../shared/languages';
@@ -26,7 +26,7 @@ import { CouchService } from '../shared/couchdb.service';
2626
import { PlanetMessageService } from '../shared/planet-message.service';
2727
import { DialogsPromptComponent } from '../shared/dialogs/dialogs-prompt.component';
2828
import { CoursesService } from './courses.service';
29-
import { dedupeShelfReduce, doesMarkdownPreviewTruncate, findByIdInArray, hasMarkdownImages, itemsShown } from '../shared/utils';
29+
import { dedupeShelfReduce, doesMarkdownPreviewTruncate, findByIdInArray, hasMarkdownImages } from '../shared/utils';
3030
import { StateService } from '../shared/state.service';
3131
import { DialogsLoadingService } from '../shared/dialogs/dialogs-loading.service';
3232
import { DialogGuardService } from '../shared/dialogs/dialog-guard.service';
@@ -88,6 +88,7 @@ export class CoursesComponent implements OnInit, OnChanges, AfterViewInit, OnDes
8888
return this.courses;
8989
}
9090
courses = new MatTableDataSource();
91+
private renderedRows: any[] = [];
9192
@ViewChild(MatSort) sort: MatSort;
9293
@ViewChild(MatPaginator) paginator: MatPaginator;
9394
@ViewChild(CoursesSearchComponent) searchComponent: CoursesSearchComponent;
@@ -188,6 +189,7 @@ export class CoursesComponent implements OnInit, OnChanges, AfterViewInit, OnDes
188189
this.userShelf = this.userService.shelf;
189190
this.courses.filterPredicate = this.filterPredicate;
190191
this.courses.sortingDataAccessor = commonSortingDataAccessor;
192+
this.courses.connect().pipe(takeUntil(this.onDestroy$)).subscribe(rows => this.renderedRows = rows);
191193
this.coursesService.coursesListener$(this.parent).pipe(
192194
takeUntil(this.onDestroy$),
193195
switchMap((courses: any) => this.parent && courses !== undefined ?
@@ -341,17 +343,15 @@ export class CoursesComponent implements OnInit, OnChanges, AfterViewInit, OnDes
341343

342344
/** Whether the number of selected elements matches the total number of rows. */
343345
isAllSelected() {
344-
return this.selection.selected.length === itemsShown(this.paginator);
346+
return this.renderedRows.length > 0 && this.renderedRows.every((row: any) => this.selection.isSelected(row._id));
345347
}
346348

347349
/** Selects all rows if they are not all selected; otherwise clear selection. */
348350
masterToggle() {
349-
const start = this.paginator.pageIndex * this.paginator.pageSize;
350-
const end = start + this.paginator.pageSize;
351351
if (this.isAllSelected()) {
352352
this.selection.clear();
353353
} else {
354-
this.courses.filteredData.slice(start, end).forEach((row: any) => this.selection.select(row._id));
354+
this.renderedRows.forEach((row: any) => this.selection.select(row._id));
355355
}
356356
}
357357

@@ -380,7 +380,10 @@ export class CoursesComponent implements OnInit, OnChanges, AfterViewInit, OnDes
380380
}
381381

382382
removeFilteredFromSelection() {
383-
this.selection.deselect(...selectedOutOfFilter(this.courses.filteredData, this.selection, this.paginator));
383+
queueMicrotask(() => {
384+
const visible = new Set(this.renderedRows.map((row: any) => row._id));
385+
this.selection.deselect(...this.selection.selected.filter(id => !visible.has(id)));
386+
});
384387
}
385388

386389
onSearchChange({ items, category }) {

src/app/manager-dashboard/manager-fetch.component.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Component, OnInit, AfterViewInit, ViewChild } from '@angular/core';
1+
import { Component, OnInit, AfterViewInit, OnDestroy, ViewChild } from '@angular/core';
22
import { Router } from '@angular/router';
3-
import { of } from 'rxjs';
4-
import { switchMap } from 'rxjs/operators';
3+
import { of, Subject } from 'rxjs';
4+
import { switchMap, takeUntil } from 'rxjs/operators';
55
import { CouchService } from '../shared/couchdb.service';
66
import { StateService } from '../shared/state.service';
77
import { ManagerService } from './manager.service';
@@ -12,7 +12,7 @@ import {
1212
MatTableDataSource, MatTable, MatColumnDef, MatHeaderCellDef, MatHeaderCell, MatCellDef, MatCell, MatHeaderRowDef, MatHeaderRow,
1313
MatRowDef, MatRow, MatNoDataRow
1414
} from '@angular/material/table';
15-
import { findByIdInArray, itemsShown } from '../shared/utils';
15+
import { findByIdInArray } from '../shared/utils';
1616
import { commonSortingDataAccessor } from '../shared/table-helpers';
1717
import { SyncService } from '../shared/sync.service';
1818
import { PlanetMessageService } from '../shared/planet-message.service';
@@ -43,14 +43,16 @@ import { MatCheckbox } from '@angular/material/checkbox';
4343
]
4444
})
4545

46-
export class ManagerFetchComponent implements OnInit, AfterViewInit {
46+
export class ManagerFetchComponent implements OnInit, AfterViewInit, OnDestroy {
4747
selection = new SelectionModel(true, []);
4848
@ViewChild(MatSort) sort: MatSort;
4949
@ViewChild(MatPaginator) paginator: MatPaginator;
5050
planetConfiguration = this.stateService.configuration;
5151
displayedColumns = [ 'select', 'item', 'date' ];
5252
pushedItems = new MatTableDataSource();
5353
isLoading = true;
54+
private renderedRows: any[] = [];
55+
private onDestroy$ = new Subject<void>();
5456

5557
constructor(
5658
private couchService: CouchService,
@@ -64,6 +66,7 @@ export class ManagerFetchComponent implements OnInit, AfterViewInit {
6466
ngOnInit() {
6567
this.isLoading = true;
6668
this.pushedItems.sortingDataAccessor = commonSortingDataAccessor;
69+
this.pushedItems.connect().pipe(takeUntil(this.onDestroy$)).subscribe(rows => this.renderedRows = rows);
6770

6871
this.managerService.getPushedList().subscribe((pushedList: any) => {
6972
this.pushedItems.data = pushedList.map((item: any) => ({
@@ -81,23 +84,26 @@ export class ManagerFetchComponent implements OnInit, AfterViewInit {
8184
this.pushedItems.paginator = this.paginator;
8285
}
8386

87+
ngOnDestroy() {
88+
this.onDestroy$.next();
89+
this.onDestroy$.complete();
90+
}
91+
8492
onPaginateChange(e: PageEvent) {
8593
this.selection.clear();
8694
}
8795

8896
/** Whether the number of selected elements matches the total number of rows. */
8997
isAllSelected() {
90-
return this.selection.selected.length === itemsShown(this.paginator);
98+
return this.renderedRows.length > 0 && this.renderedRows.every((row: any) => this.selection.isSelected(row._id));
9199
}
92100

93101
/** Selects all rows if they are not all selected; otherwise clear selection. */
94102
masterToggle() {
95-
const start = this.paginator.pageIndex * this.paginator.pageSize;
96-
const end = start + this.paginator.pageSize;
97103
if (this.isAllSelected()) {
98104
this.selection.clear();
99105
} else {
100-
this.pushedItems.filteredData.slice(start, end).forEach((row: any) => this.selection.select(row._id));
106+
this.renderedRows.forEach((row: any) => this.selection.select(row._id));
101107
}
102108
}
103109

src/app/meetups/meetups.component.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
MatHeaderRowDef, MatHeaderRow, MatRowDef, MatRow, MatNoDataRow
88
} from '@angular/material/table';
99
import { PlanetMessageService } from '../shared/planet-message.service';
10-
import { filterSpecificFields, selectedOutOfFilter, composeFilterFunctions, filterSpecificFieldsByWord } from '../shared/table-helpers';
10+
import { filterSpecificFields, composeFilterFunctions, filterSpecificFieldsByWord } from '../shared/table-helpers';
1111
import { SelectionModel } from '@angular/cdk/collections';
1212
import { Router, ActivatedRoute, RouterLink } from '@angular/router';
1313
import { UserService } from '../shared/user.service';
@@ -16,7 +16,7 @@ import { takeUntil } from 'rxjs/operators';
1616
import { MeetupService } from './meetups.service';
1717
import { StateService } from '../shared/state.service';
1818
import { DialogsLoadingService } from '../shared/dialogs/dialogs-loading.service';
19-
import { findByIdInArray, itemsShown } from '../shared/utils';
19+
import { findByIdInArray } from '../shared/utils';
2020
import { MatToolbar } from '@angular/material/toolbar';
2121
import { MatIconButton, MatMiniFabButton, MatButton } from '@angular/material/button';
2222
import { MatIcon } from '@angular/material/icon';
@@ -59,6 +59,7 @@ import { FeedbackDirective } from '../feedback/feedback.directive';
5959
export class MeetupsComponent implements OnInit, AfterViewInit, OnDestroy {
6060

6161
meetups = new MatTableDataSource();
62+
private renderedRows: any[] = [];
6263
message = '';
6364
readonly dbName = 'meetups';
6465
deleteDialog: any;
@@ -107,6 +108,7 @@ export class MeetupsComponent implements OnInit, AfterViewInit, OnDestroy {
107108
this.countSelectedShelf(source.selected);
108109
});
109110
this.couchService.checkAuthorization('meetups').subscribe((isAuthorized) => this.isAuthorized = isAuthorized);
111+
this.meetups.connect().pipe(takeUntil(this.onDestroy$)).subscribe(rows => this.renderedRows = rows);
110112
}
111113

112114
ngAfterViewInit() {
@@ -115,24 +117,27 @@ export class MeetupsComponent implements OnInit, AfterViewInit, OnDestroy {
115117
}
116118

117119
isAllSelected() {
118-
return this.selection.selected.length === itemsShown(this.paginator);
120+
return this.renderedRows.length > 0 && this.renderedRows.every((row: any) => this.selection.isSelected(row._id));
119121
}
120122
onPaginateChange(e: PageEvent) {
121123
this.selection.clear();
122124
}
123125

124126

125127
masterToggle() {
126-
const start = this.paginator.pageIndex * this.paginator.pageSize;
127-
const end = start + this.paginator.pageSize;
128-
this.isAllSelected() ?
129-
this.selection.clear() :
130-
this.meetups.filteredData.slice(start, end).forEach((row: any) => this.selection.select(row._id));
128+
if (this.isAllSelected()) {
129+
this.selection.clear();
130+
} else {
131+
this.renderedRows.forEach((row: any) => this.selection.select(row._id));
132+
}
131133
}
132134

133135
applyFilter(filterValue: string) {
134136
this.meetups.filter = filterValue;
135-
this.selection.deselect(...selectedOutOfFilter(this.meetups.filteredData, this.selection, this.paginator));
137+
queueMicrotask(() => {
138+
const visible = new Set(this.renderedRows.map((row: any) => row._id));
139+
this.selection.deselect(...this.selection.selected.filter(id => !visible.has(id)));
140+
});
136141
}
137142

138143
ngOnDestroy() {

src/app/resources/resources.component.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { UserService } from '../shared/user.service';
1616
import { FuzzySearchService } from '../shared/fuzzy-search.service';
1717
import {
1818
filterSpecificFields, composeFilterFunctions, filterTags, filterAdvancedSearch, filterShelf,
19-
createDeleteArray, commonSortingDataAccessor, filterSpecificFieldsHybrid, selectedOutOfFilter, trackById
19+
createDeleteArray, commonSortingDataAccessor, filterSpecificFieldsHybrid, trackById
2020
} from '../shared/table-helpers';
2121
import { ResourcesService } from './resources.service';
2222
import { environment } from '../../environments/environment';
@@ -25,7 +25,7 @@ import { FormControl } from '../../../node_modules/@angular/forms';
2525
import { PlanetTagInputComponent } from '../shared/forms/planet-tag-input.component';
2626
import { DialogsListService } from '../shared/dialogs/dialogs-list.service';
2727
import { DialogsListComponent } from '../shared/dialogs/dialogs-list.component';
28-
import { doesMarkdownPreviewTruncate, findByIdInArray, hasMarkdownImages, itemsShown } from '../shared/utils';
28+
import { doesMarkdownPreviewTruncate, findByIdInArray, hasMarkdownImages } from '../shared/utils';
2929
import { StateService } from '../shared/state.service';
3030
import { DialogsLoadingService } from '../shared/dialogs/dialogs-loading.service';
3131
import { DialogGuardService } from '../shared/dialogs/dialog-guard.service';
@@ -73,6 +73,7 @@ import { TruncateTextPipe } from '../shared/truncate-text.pipe';
7373
export class ResourcesComponent implements OnInit, AfterViewInit, OnDestroy {
7474
isLoading = true;
7575
resources = new MatTableDataSource();
76+
private renderedRows: any[] = [];
7677
pageEvent: PageEvent;
7778
@ViewChild(MatPaginator) paginator: MatPaginator;
7879
@ViewChild(MatSort) sort: MatSort;
@@ -194,6 +195,7 @@ export class ResourcesComponent implements OnInit, AfterViewInit, OnDestroy {
194195
this.removeFilteredFromSelection();
195196
});
196197
this.selection.changed.subscribe(({ source }) => this.onSelectionChange(source.selected));
198+
this.resources.connect().pipe(takeUntil(this.onDestroy$)).subscribe(rows => this.renderedRows = rows);
197199
this.couchService.checkAuthorization('resources').subscribe((isAuthorized) => this.isAuthorized = isAuthorized);
198200
this.initialSort = this.route.snapshot.paramMap.get('sort');
199201
}
@@ -210,7 +212,10 @@ export class ResourcesComponent implements OnInit, AfterViewInit, OnDestroy {
210212
}
211213

212214
removeFilteredFromSelection() {
213-
this.selection.deselect(...selectedOutOfFilter(this.resources.filteredData, this.selection, this.paginator));
215+
queueMicrotask(() => {
216+
const visible = new Set(this.renderedRows.map((row: any) => row._id));
217+
this.selection.deselect(...this.selection.selected.filter(id => !visible.has(id)));
218+
});
214219
}
215220

216221

@@ -234,7 +239,7 @@ export class ResourcesComponent implements OnInit, AfterViewInit, OnDestroy {
234239

235240
/** Whether the number of selected elements matches the total number of rows. */
236241
isAllSelected() {
237-
return this.selection.selected.length === itemsShown(this.paginator);
242+
return this.renderedRows.length > 0 && this.renderedRows.every((row: any) => this.selection.isSelected(row._id));
238243
}
239244

240245
applyResFilter(filterResValue: string) {
@@ -243,11 +248,11 @@ export class ResourcesComponent implements OnInit, AfterViewInit, OnDestroy {
243248

244249
/** Selects all rows if they are not all selected; otherwise clear selection. */
245250
masterToggle() {
246-
const start = this.paginator.pageIndex * this.paginator.pageSize;
247-
const end = start + this.paginator.pageSize;
248-
this.isAllSelected() ?
249-
this.selection.clear() :
250-
this.resources.filteredData.slice(start, end).forEach((row: any) => this.selection.select(row._id));
251+
if (this.isAllSelected()) {
252+
this.selection.clear();
253+
} else {
254+
this.renderedRows.forEach((row: any) => this.selection.select(row._id));
255+
}
251256
}
252257

253258
updateResource(resource) {

src/app/surveys/surveys.component.html

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,8 @@
4141
<mat-cell *matCellDef="let row">
4242
<mat-checkbox (change)="$event ? selection.toggle(row._id) : null"
4343
[checked]="selection.isSelected(row._id)"
44-
[disabled]="currentFilter.viewMode === 'adopt'"
45-
[matTooltip]="getActionTooltip(row, 'select')"
46-
*ngIf="!row.parent === true">
44+
[disabled]="!isRowSelectable(row)"
45+
[matTooltip]="getActionTooltip(row, 'select')">
4746
</mat-checkbox>
4847
</mat-cell>
4948
</ng-container>
@@ -68,7 +67,7 @@
6867
<ng-container matColumnDef="action">
6968
<mat-header-cell *matHeaderCellDef i18n>Action</mat-header-cell>
7069
<mat-cell *matCellDef="let element">
71-
<ng-container *ngIf="!element.parent === true && currentFilter.viewMode !== 'adopt'">
70+
<ng-container *ngIf="element.parent !== true && currentFilter.viewMode !== 'adopt'">
7271
<div [matTooltip]="getActionTooltip(element, 'edit')">
7372
<button mat-raised-button color="primary" *ngIf="!teamId" (click)="routeToEditSurvey('update', element._id)" [disabled]="element.isArchived">
7473
<mat-icon>edit</mat-icon>

src/app/surveys/surveys.component.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { forkJoin, Observable, Subject, throwError, of } from 'rxjs';
1313
import { catchError, switchMap, tap, takeUntil } from 'rxjs/operators';
1414
import { CouchService } from '../shared/couchdb.service';
1515
import { ChatService } from '../shared/chat.service';
16-
import { filterSpecificFields, sortNumberOrString, createDeleteArray, selectedOutOfFilter } from '../shared/table-helpers';
16+
import { filterSpecificFields, sortNumberOrString, createDeleteArray } from '../shared/table-helpers';
1717
import { SubmissionsService } from '../submissions/submissions.service';
1818
import { PlanetMessageService } from '../shared/planet-message.service';
1919
import { StateService } from '../shared/state.service';
@@ -60,6 +60,7 @@ interface SurveyFilterForm {
6060
export class SurveysComponent implements OnInit, AfterViewInit, OnDestroy {
6161
selection = new SelectionModel(true, []);
6262
surveys = new MatTableDataSource<any>();
63+
private renderedRows: any[] = [];
6364
@ViewChild(MatSort) sort: MatSort;
6465
@ViewChild(MatPaginator) paginator: MatPaginator;
6566
@Output() surveyCount = new EventEmitter<number>();
@@ -117,6 +118,7 @@ export class SurveysComponent implements OnInit, AfterViewInit, OnDestroy {
117118
this.couchService.checkAuthorization(this.dbName)
118119
.pipe(takeUntil(this.onDestroy$)).subscribe((isAuthorized) => this.isAuthorized = isAuthorized);
119120
this.surveys.connect().pipe(takeUntil(this.onDestroy$)).subscribe(surveys => {
121+
this.renderedRows = surveys;
120122
this.parentCount = surveys.filter(survey => survey.parent === true).length;
121123
this.surveyCount.emit(surveys.length);
122124
});
@@ -255,32 +257,26 @@ export class SurveysComponent implements OnInit, AfterViewInit, OnDestroy {
255257

256258
applyFilter(filterValue: string) {
257259
this.surveys.filter = filterValue;
258-
this.selection.deselect(...selectedOutOfFilter(this.surveys.filteredData, this.selection, this.paginator));
260+
queueMicrotask(() => {
261+
const visibleSelection = new Set(this.renderedRows.map(row => row._id));
262+
this.selection.deselect(...this.selection.selected.filter(selectedId => !visibleSelection.has(selectedId)));
263+
});
259264
}
260265

261266
isAllSelected() {
262-
const start = this.paginator.pageIndex * this.paginator.pageSize;
263-
const end = start + this.paginator.pageSize;
264-
265-
const selectableRowsInPage = this.surveys.filteredData
266-
.slice(start, end)
267-
.filter(row => this.isRowSelectable(row));
268-
269-
return selectableRowsInPage.every(row => this.selection.isSelected(row._id));
267+
const selectableRowsInPage = this.renderedRows.filter(row => this.isRowSelectable(row));
268+
return selectableRowsInPage.length > 0 && selectableRowsInPage.every(row => this.selection.isSelected(row._id));
270269
}
271270

272271
isRowSelectable(row: any): boolean {
273-
const isDisabled = (row.teamId && this.isManagerRoute) || this.currentFilter.viewMode === 'adopt';
274-
return row.parent !== true && !isDisabled;
272+
return row.parent !== true && this.currentFilter.viewMode !== 'adopt';
275273
}
276274

277275
masterToggle() {
278-
const start = this.paginator.pageIndex * this.paginator.pageSize;
279-
const end = start + this.paginator.pageSize;
280276
if (this.isAllSelected()) {
281277
this.selection.clear();
282278
} else {
283-
this.surveys.filteredData.slice(start, end).forEach((row: any) => {
279+
this.renderedRows.forEach((row: any) => {
284280
if (this.isRowSelectable(row)) {
285281
this.selection.select(row._id);
286282
}
@@ -598,8 +594,8 @@ export class SurveysComponent implements OnInit, AfterViewInit, OnDestroy {
598594
}
599595
}
600596

601-
if (survey.teamId && this.isManagerRoute) {
602-
return $localize`This is a team created survey`;
597+
if (action === 'select' && survey.parent === true) {
598+
return $localize`This survey was created on the parent planet and cannot be managed here`;
603599
}
604600

605601
if (this.currentFilter.viewMode === 'adopt') {

0 commit comments

Comments
 (0)