Skip to content

Commit e5ea040

Browse files
committed
cfx-ui: server list performance improvements
1 parent 16f7504 commit e5ea040

5 files changed

+104
-66
lines changed

ext/cfx-ui/src/app/servers/components/server-tag-filter.component.ts

+38-29
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,46 @@ export class ServerTagFilterComponent implements OnInit, OnChanges, OnDestroy {
2525
@Output()
2626
public tagsChanged = new EventEmitter<ServerTags>();
2727

28-
get tags(): ServerTag[] {
28+
tags: ServerTag[] = [];
29+
30+
serverTags: {[addr: string]: string[]} = {};
31+
32+
constructor(private serversService: ServersService) {
33+
this.serversService
34+
.getReplayedServers()
35+
.filter(server => !!server)
36+
.subscribe(server => {
37+
this.addFilterIndex(server);
38+
});
39+
40+
this.serversService
41+
.getReplayedServers()
42+
.bufferTime(500)
43+
.subscribe(server => {
44+
this.updateTagList();
45+
});
46+
}
47+
48+
private updateTagList() {
2949
const tagList = Object.entries(
30-
Object.values(this.serverTags)
31-
.reduce<{[k: string]: number}>((acc: {[k: string]: number}, val: string[]) => {
32-
for (const str of val) {
33-
if (!acc.hasOwnProperty(str)) {
34-
acc[str] = 0;
35-
}
36-
37-
acc[str]++;
50+
Object.values(this.serverTags)
51+
.reduce<{[k: string]: number}>((acc: {[k: string]: number}, val: string[]) => {
52+
for (const str of val) {
53+
if (!acc.hasOwnProperty(str)) {
54+
acc[str] = 0;
3855
}
39-
return acc;
40-
}, {})
41-
)
42-
.map(([name, count]) => {
43-
return {
44-
name,
45-
count
56+
57+
acc[str]++;
4658
}
47-
});
59+
return acc;
60+
}, {})
61+
)
62+
.map(([name, count]) => {
63+
return {
64+
name,
65+
count
66+
}
67+
});
4868

4969
tagList.sort((a, b) => {
5070
if (a.count === b.count) {
@@ -56,18 +76,7 @@ export class ServerTagFilterComponent implements OnInit, OnChanges, OnDestroy {
5676
}
5777
});
5878

59-
return tagList.slice(0, 50);
60-
}
61-
62-
serverTags: {[addr: string]: string[]} = {};
63-
64-
constructor(private serversService: ServersService) {
65-
this.serversService
66-
.getReplayedServers()
67-
.filter(server => !!server)
68-
.subscribe(server => {
69-
this.addFilterIndex(server);
70-
});
79+
this.tags = tagList.slice(0, 50);
7180
}
7281

7382
private addFilterIndex(server: Server) {

ext/cfx-ui/src/app/servers/components/servers-container.component.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Component, OnInit, PLATFORM_ID, Inject } from '@angular/core';
22
import { ActivatedRoute } from '@angular/router';
33

4-
import { Server, ServerIcon, PinConfig } from '../server';
4+
import { Server, ServerIcon, PinConfigCached } from '../server';
55
import { ServersService } from '../servers.service';
66
import { ServerFilters, ServerFilterContainer, ServerTags } from './server-filter.component';
77

@@ -25,7 +25,7 @@ export class ServersContainerComponent implements OnInit {
2525
localServers: Server[]; // temp value
2626
icons: ServerIcon[];
2727

28-
pinConfig: PinConfig;
28+
pinConfig: PinConfigCached;
2929

3030
filters: ServerFilterContainer;
3131

@@ -34,7 +34,7 @@ export class ServersContainerComponent implements OnInit {
3434
constructor(private serverService: ServersService, private gameService: GameService, private route: ActivatedRoute,
3535
@Inject(PLATFORM_ID) private platformId: any) {
3636
this.filters = new ServerFilterContainer();
37-
this.pinConfig = new PinConfig();
37+
this.pinConfig = new PinConfigCached(null);
3838
}
3939

4040
serversArray: Server[] = [];
@@ -61,7 +61,7 @@ export class ServersContainerComponent implements OnInit {
6161

6262
loadServers() {
6363
this.serverService.loadPinConfig()
64-
.then(pinConfig => this.pinConfig = pinConfig);
64+
.then(pinConfig => this.pinConfig = new PinConfigCached(pinConfig));
6565

6666

6767
const typedServers = this.serverService

ext/cfx-ui/src/app/servers/components/servers-list.component.html

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
<virtual-scroller
99
#scroll
1010
[items]="sortedServers"
11-
[bufferAmount]="15"
11+
[bufferAmount]="25"
1212
[stripedTable]="true"
13+
[executeRefreshOutsideAngularZone]="true"
14+
[scrollThrottlingTime]="100"
15+
(vsUpdate)="changeDetectorRef.detectChanges()"
1316
>
1417
<div class="dummy"></div>
1518
<servers-list-item

ext/cfx-ui/src/app/servers/components/servers-list.component.ts

+43-32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Component, OnInit, OnChanges, Input, NgZone, Inject, PLATFORM_ID } from '@angular/core';
2-
import { Server, PinConfig } from '../server';
1+
import { Component, OnInit, OnChanges, Input, NgZone, Inject, PLATFORM_ID, ChangeDetectorRef } from '@angular/core';
2+
import { Server, PinConfigCached } from '../server';
33
import { ServersListHeadingColumn } from './servers-list-header.component';
44
import { ServerFilterContainer } from './server-filter.component';
55
import { Subject } from 'rxjs/Subject';
@@ -24,18 +24,21 @@ export class ServersListComponent implements OnInit, OnChanges {
2424
private filters: ServerFilterContainer;
2525

2626
@Input()
27-
private pinConfig: PinConfig;
27+
private pinConfig: PinConfigCached;
2828

2929
private subscriptions: { [addr: string]: any } = {};
3030

31+
private lastLength: number;
32+
3133
sortOrder: string[];
3234

3335
columns: ServersListHeadingColumn[];
3436

3537
localServers: Server[];
3638
sortedServers: Server[];
3739

38-
constructor(private zone: NgZone, @Inject(LocalStorage) private localStorage: any, @Inject(PLATFORM_ID) private platformId: any) {
40+
constructor(private zone: NgZone, @Inject(LocalStorage) private localStorage: any, @Inject(PLATFORM_ID) private platformId: any,
41+
public changeDetectorRef: ChangeDetectorRef) {
3942
this.servers = [];
4043

4144
this.columns = [
@@ -83,6 +86,12 @@ export class ServersListComponent implements OnInit, OnChanges {
8386
if (changed) {
8487
changed = false;
8588

89+
for (const server of (this.servers || [])) {
90+
if (!this.subscriptions[server.address]) {
91+
this.subscriptions[server.address] = server.onChanged.subscribe(a => this.changeSubject.next());
92+
}
93+
}
94+
8695
zone.run(() => {
8796
this.sortAndFilterServers();
8897
});
@@ -100,7 +109,7 @@ export class ServersListComponent implements OnInit, OnChanges {
100109
return false;
101110
}
102111

103-
return (this.pinConfig.pinnedServers.indexOf(server.address) >= 0)
112+
return this.pinConfig.pinnedServers.has(server.address);
104113
}
105114

106115
isPremium(server: Server) {
@@ -213,7 +222,9 @@ export class ServersListComponent implements OnInit, OnChanges {
213222
const filters = filterList.filters;
214223

215224
const hiddenByTags = (server: Server) => {
216-
if (filterList.tags) {
225+
const tagListEntries = (filterList.tags) ? Object.entries(filterList.tags.tagList) : [];
226+
227+
if (tagListEntries.length > 0) {
217228
const tags =
218229
(server && server.data && server.data.vars && server.data.vars.tags) ?
219230
(<string>server.data.vars.tags)
@@ -225,7 +236,7 @@ export class ServersListComponent implements OnInit, OnChanges {
225236

226237
const tagSet = new Set<string>(tags);
227238

228-
for (const [ tag, active ] of Object.entries(filterList.tags.tagList)) {
239+
for (const [ tag, active ] of tagListEntries) {
229240
if (active) {
230241
if (!tagSet.has(tag)) {
231242
return true;
@@ -247,7 +258,7 @@ export class ServersListComponent implements OnInit, OnChanges {
247258
}
248259

249260
if (server.currentPlayers === 0 && filters.hideEmpty) {
250-
if (!this.isPinned(server) || !this.pinConfig.pinIfEmpty) {
261+
if (!this.isPinned(server) || !this.pinConfig.data.pinIfEmpty) {
251262
return false;
252263
}
253264
}
@@ -271,7 +282,7 @@ export class ServersListComponent implements OnInit, OnChanges {
271282
}
272283

273284
sortAndFilterServers() {
274-
const servers = (this.servers || []).concat().filter(this.getFilter(this.filters));
285+
const servers = (this.servers || []).filter(this.getFilter(this.filters));
275286

276287
const sortChain = (a: Server, b: Server, ...stack: ((a: Server, b: Server) => number)[]) => {
277288
for (const entry of stack) {
@@ -311,26 +322,30 @@ export class ServersListComponent implements OnInit, OnChanges {
311322
}
312323
};
313324

325+
const sortList = [
326+
(a: Server, b: Server) => {
327+
const aPinned = this.isPinned(a);
328+
const bPinned = this.isPinned(b);
329+
330+
if (aPinned === bPinned) {
331+
return 0;
332+
} else if (aPinned && !bPinned) {
333+
return -1;
334+
} else if (!aPinned && bPinned) {
335+
return 1;
336+
}
337+
},
338+
sortSortable(this.sortOrder),
339+
sortSortable(['upvotePower', '-']),
340+
sortSortable(['ping', '+']),
341+
sortSortable(['name', '+'])
342+
];
343+
314344
servers.sort((a, b) => {
315345
return sortChain(
316346
a,
317347
b,
318-
(a: Server, b: Server) => {
319-
const aPinned = this.isPinned(a);
320-
const bPinned = this.isPinned(b);
321-
322-
if (aPinned === bPinned) {
323-
return 0;
324-
} else if (aPinned && !bPinned) {
325-
return -1;
326-
} else if (!aPinned && bPinned) {
327-
return 1;
328-
}
329-
},
330-
sortSortable(this.sortOrder),
331-
sortSortable(['upvotePower', '-']),
332-
sortSortable(['ping', '+']),
333-
sortSortable(['name', '+'])
348+
...sortList
334349
);
335350
});
336351

@@ -358,14 +373,10 @@ export class ServersListComponent implements OnInit, OnChanges {
358373
changeObservable = this.changeSubject.asObservable();
359374

360375
ngOnChanges() {
361-
for (const server of (this.servers || [])) {
362-
if (!this.subscriptions[server.address]) {
363-
this.subscriptions[server.address] = server.onChanged.subscribe(a => this.changeSubject.next());
364-
}
376+
if (this.servers.length !== this.lastLength) {
377+
this.changeSubject.next();
378+
this.lastLength = this.servers.length;
365379
}
366-
367-
//this.sortAndFilterServers();
368-
this.changeSubject.next();
369380
}
370381

371382
svTrack(index: number, serverRow: Server) {

ext/cfx-ui/src/app/servers/server.ts

+15
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,19 @@ export class PinConfig {
128128
pinIfEmpty = false;
129129

130130
pinnedServers: string[] = [];
131+
}
132+
133+
export class PinConfigCached {
134+
public data: PinConfig;
135+
public pinnedServers: Set<string>;
136+
137+
constructor(pinConfig: PinConfig) {
138+
if (pinConfig) {
139+
this.data = pinConfig;
140+
} else {
141+
this.data = new PinConfig();
142+
}
143+
144+
this.pinnedServers = new Set<string>(this.data.pinnedServers);
145+
}
131146
}

0 commit comments

Comments
 (0)