Skip to content

Commit f2dc94e

Browse files
committed
Save configs and wordlists table state in session storage
Closes #1088
1 parent 3871005 commit f2dc94e

4 files changed

Lines changed: 146 additions & 10 deletions

File tree

openbullet2-web-client/src/app/main/components/configs/configs.component.html

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ <h5 class="color-bad">
3333
<ng-template pTemplate="end">
3434
<button class="button button-secondary" *ngIf="displayMode === 'table'"
3535
pTooltip="Clear filters" tooltipPosition="bottom"
36-
(click)="configDt.clear()">
36+
(click)="clearTable()">
3737
Clear
3838
<fa-icon [icon]="faFilterCircleXmark" [fixedWidth]="true"></fa-icon>
3939
</button>
@@ -42,7 +42,9 @@ <h5 class="color-bad">
4242
spellcheck="false"
4343
class="ml-2"
4444
[style]="{ height: '33px' }"
45-
(input)="configDt.filterGlobal($any($event.target).value, 'contains')" placeholder="Search keyword" />
45+
[(ngModel)]="searchTerm"
46+
(input)="applyGlobalFilter()"
47+
placeholder="Search keyword" />
4648
</span>
4749
</ng-template>
4850
</p-menubar>
@@ -55,15 +57,20 @@ <h5 class="color-bad">
5557
[value]="configs"
5658
[tableStyle]="{ 'min-width': '50rem', 'max-width': '100%' }"
5759
[paginator]="true"
58-
[rows]="10"
60+
[rows]="tableRows"
61+
[first]="tableFirst"
5962
[resizableColumns]="true"
6063
[scrollable]="true"
6164
scrollDirection="horizontal"
6265
columnResizeMode="expand"
6366
[showCurrentPageReport]="true"
6467
[rowsPerPageOptions]="[10, 25, 50]"
68+
stateStorage="session"
69+
stateKey="configs-table"
6570
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
6671
[globalFilterFields]="['name', 'author', 'category', 'isRemote', 'proxies', 'wordlists', 'creationDate', 'lastModified']"
72+
(onPage)="onTablePageChange($event)"
73+
(onStateRestore)="onTableStateRestore($event)"
6774
>
6875
<ng-template pTemplate="emptymessage">
6976
<div class="m-3">

openbullet2-web-client/src/app/main/components/configs/configs.component.ts

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
faX,
1111
} from '@fortawesome/free-solid-svg-icons';
1212
import * as moment from 'moment';
13-
import { ConfirmationService, MenuItem, MessageService } from 'primeng/api';
13+
import { ConfirmationService, MenuItem, MessageService, TableState } from 'primeng/api';
14+
import { Table, TablePageEvent } from 'primeng/table';
1415
import { saveFile } from 'src/app/shared/utils/files';
1516
import { ConfigInfoDto, ConfigMode } from '../../dtos/config/config-info.dto';
1617
import { ConfigDto } from '../../dtos/config/config.dto';
@@ -29,6 +30,9 @@ export class ConfigsComponent implements OnInit {
2930
configs: ConfigInfoDto[] | null = null;
3031
obSettings: OBSettingsDto | null = null;
3132

33+
@ViewChild('configDt')
34+
configDt: Table | undefined = undefined;
35+
3236
@ViewChild('uploadConfigsComponent')
3337
uploadConfigsComponent: UploadConfigsComponent | undefined = undefined;
3438

@@ -45,6 +49,9 @@ export class ConfigsComponent implements OnInit {
4549

4650
displayMode = 'grid';
4751
uploadConfigsModalVisible = false;
52+
tableFirst = 0;
53+
tableRows = 10;
54+
searchTerm = '';
4855

4956
configMenuItems: MenuItem[] = [
5057
{
@@ -120,6 +127,7 @@ export class ConfigsComponent implements OnInit {
120127
refreshConfigs(reload: boolean) {
121128
this.configService.getAllConfigs(reload).subscribe((configs) => {
122129
this.configs = configs;
130+
this.reapplyTableState();
123131

124132
if (reload) {
125133
this.messageService.add({
@@ -136,6 +144,32 @@ export class ConfigsComponent implements OnInit {
136144
this.volatileSettings.configsDisplayMode = mode;
137145
}
138146

147+
onTablePageChange(event: TablePageEvent) {
148+
this.tableFirst = event.first;
149+
this.tableRows = event.rows;
150+
}
151+
152+
onTableStateRestore(state: TableState) {
153+
this.tableFirst = state.first ?? 0;
154+
this.tableRows = state.rows ?? 10;
155+
156+
const globalFilter = state.filters?.['global'];
157+
if (globalFilter && !Array.isArray(globalFilter) && globalFilter.value) {
158+
this.searchTerm = String(globalFilter.value);
159+
}
160+
}
161+
162+
applyGlobalFilter() {
163+
this.tableFirst = 0;
164+
this.configDt?.filterGlobal(this.searchTerm, 'contains');
165+
}
166+
167+
clearTable() {
168+
this.searchTerm = '';
169+
this.tableFirst = 0;
170+
this.configDt?.clear();
171+
}
172+
139173
openUploadConfigsModal() {
140174
this.uploadConfigsComponent?.reset();
141175
this.uploadConfigsModalVisible = true;
@@ -345,7 +379,34 @@ export class ConfigsComponent implements OnInit {
345379
summary: 'Deleted',
346380
detail: `Config ${config.name} was deleted`,
347381
});
348-
this.refreshConfigs(false);
382+
383+
if (this.configs !== null) {
384+
this.configs = this.configs.filter((c) => c.id !== config.id);
385+
}
386+
387+
this.reapplyTableState();
388+
});
389+
}
390+
391+
private reapplyTableState() {
392+
setTimeout(() => {
393+
if (!this.configDt) {
394+
return;
395+
}
396+
397+
if (this.searchTerm) {
398+
this.configDt.filterGlobal(this.searchTerm, 'contains');
399+
}
400+
401+
const filteredCount = this.configDt.filteredValue?.length ?? this.configs?.length ?? 0;
402+
403+
if (filteredCount === 0) {
404+
this.tableFirst = 0;
405+
return;
406+
}
407+
408+
const maxFirst = Math.floor((filteredCount - 1) / this.tableRows) * this.tableRows;
409+
this.tableFirst = Math.min(this.tableFirst, maxFirst);
349410
});
350411
}
351412
}

openbullet2-web-client/src/app/main/components/wordlists/wordlists.component.html

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ <h6 class="color-inactive">
1919
<ng-template pTemplate="end">
2020
<button class="button button-secondary"
2121
pTooltip="Clear filters" tooltipPosition="bottom"
22-
(click)="wordlistsDt.clear()">
22+
(click)="clearTable()">
2323
Clear
2424
<fa-icon [icon]="faFilterCircleXmark" [fixedWidth]="true"></fa-icon>
2525
</button>
@@ -28,7 +28,9 @@ <h6 class="color-inactive">
2828
spellcheck="false"
2929
class="ml-2"
3030
[style]="{ height: '33px' }"
31-
(input)="wordlistsDt.filterGlobal($any($event.target).value, 'contains')" placeholder="Search keyword" />
31+
[(ngModel)]="searchTerm"
32+
(input)="applyGlobalFilter()"
33+
placeholder="Search keyword" />
3234
</span>
3335
</ng-template>
3436
</p-menubar>
@@ -41,15 +43,20 @@ <h6 class="color-inactive">
4143
[value]="wordlists"
4244
[tableStyle]="{ 'min-width': '50rem', 'max-width': '100%' }"
4345
[paginator]="true"
44-
[rows]="10"
46+
[rows]="tableRows"
47+
[first]="tableFirst"
4548
[resizableColumns]="true"
4649
[scrollable]="true"
4750
scrollDirection="horizontal"
4851
columnResizeMode="expand"
4952
[showCurrentPageReport]="true"
5053
[rowsPerPageOptions]="[10, 25, 50]"
54+
stateStorage="session"
55+
stateKey="wordlists-table"
5156
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
5257
[globalFilterFields]="['name', 'filePath', 'purpose', 'lineCount', 'wordlistType']"
58+
(onPage)="onTablePageChange($event)"
59+
(onStateRestore)="onTableStateRestore($event)"
5360
>
5461
<ng-template pTemplate="emptymessage">
5562
<div class="m-3">

openbullet2-web-client/src/app/main/components/wordlists/wordlists.component.ts

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Component, OnInit, ViewChild } from '@angular/core';
22
import { faFileLines, faFilterCircleXmark, faPen, faX } from '@fortawesome/free-solid-svg-icons';
3-
import { ConfirmationService, MenuItem, MessageService } from 'primeng/api';
3+
import { ConfirmationService, MenuItem, MessageService, TableState } from 'primeng/api';
4+
import { Table, TablePageEvent } from 'primeng/table';
45
import { EnvironmentSettingsDto } from '../../dtos/settings/environment-settings.dto';
56
import { CreateWordlistDto } from '../../dtos/wordlist/create-wordlist.dto';
67
import { UpdateWordlistInfoDto } from '../../dtos/wordlist/update-wordlist-info.dto';
@@ -19,6 +20,9 @@ export class WordlistsComponent implements OnInit {
1920
envSettings: EnvironmentSettingsDto | null = null;
2021
wordlists: WordlistDto[] | null = null;
2122

23+
@ViewChild('wordlistsDt')
24+
wordlistsDt: Table | undefined = undefined;
25+
2226
@ViewChild('addWordlistComponent')
2327
addWordlistComponent: AddWordlistComponent | undefined = undefined;
2428

@@ -34,6 +38,9 @@ export class WordlistsComponent implements OnInit {
3438
addWordlistModalVisible = false;
3539
uploadWordlistModalVisible = false;
3640
updateWordlistInfoModalVisible = false;
41+
tableFirst = 0;
42+
tableRows = 10;
43+
searchTerm = '';
3744

3845
wordlistTypes: string[] = [];
3946

@@ -92,9 +99,36 @@ export class WordlistsComponent implements OnInit {
9299

93100
this.wordlistService.getAllWordlists().subscribe((wordlists) => {
94101
this.wordlists = wordlists;
102+
this.reapplyTableState();
95103
});
96104
}
97105

106+
onTablePageChange(event: TablePageEvent) {
107+
this.tableFirst = event.first;
108+
this.tableRows = event.rows;
109+
}
110+
111+
onTableStateRestore(state: TableState) {
112+
this.tableFirst = state.first ?? 0;
113+
this.tableRows = state.rows ?? 10;
114+
115+
const globalFilter = state.filters?.['global'];
116+
if (globalFilter && !Array.isArray(globalFilter) && globalFilter.value) {
117+
this.searchTerm = String(globalFilter.value);
118+
}
119+
}
120+
121+
applyGlobalFilter() {
122+
this.tableFirst = 0;
123+
this.wordlistsDt?.filterGlobal(this.searchTerm, 'contains');
124+
}
125+
126+
clearTable() {
127+
this.searchTerm = '';
128+
this.tableFirst = 0;
129+
this.wordlistsDt?.clear();
130+
}
131+
98132
openAddWordlistModal() {
99133
this.addWordlistComponent?.reset();
100134
this.addWordlistModalVisible = true;
@@ -156,7 +190,12 @@ export class WordlistsComponent implements OnInit {
156190
summary: 'Deleted',
157191
detail: `Wordlist ${wordlist.name} was deleted${alsoDeleteFile ? ', along with its file' : ''}`,
158192
});
159-
this.refreshWordlists();
193+
194+
if (this.wordlists !== null) {
195+
this.wordlists = this.wordlists.filter((w) => w.id !== wordlist.id);
196+
}
197+
198+
this.reapplyTableState();
160199
});
161200
}
162201

@@ -181,4 +220,26 @@ export class WordlistsComponent implements OnInit {
181220
this.refreshWordlists();
182221
});
183222
}
223+
224+
private reapplyTableState() {
225+
setTimeout(() => {
226+
if (!this.wordlistsDt) {
227+
return;
228+
}
229+
230+
if (this.searchTerm) {
231+
this.wordlistsDt.filterGlobal(this.searchTerm, 'contains');
232+
}
233+
234+
const filteredCount = this.wordlistsDt.filteredValue?.length ?? this.wordlists?.length ?? 0;
235+
236+
if (filteredCount === 0) {
237+
this.tableFirst = 0;
238+
return;
239+
}
240+
241+
const maxFirst = Math.floor((filteredCount - 1) / this.tableRows) * this.tableRows;
242+
this.tableFirst = Math.min(this.tableFirst, maxFirst);
243+
});
244+
}
184245
}

0 commit comments

Comments
 (0)