Skip to content

Commit 50977b0

Browse files
authored
fix(WhoIsActive): add query cache (qiuwenbaike#1673)
* fix(WhoIsActive): add query cache
1 parent a7ab6e9 commit 50977b0

File tree

11 files changed

+320
-237
lines changed

11 files changed

+320
-237
lines changed

dist/WhoIsActive/WhoIsActive.js

Lines changed: 171 additions & 135 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/WhoIsActive/modules/core.ts

Lines changed: 16 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,31 @@
1-
import * as OPTIONS from '../options.json';
2-
import {SYSTEM_SCRIPT_LIST} from './constant';
3-
import {api} from './api';
4-
import {getLastActiveMarker} from './getLastActiveMarker';
5-
import {uniqueArray} from 'ext.gadget.Util';
1+
import {appendLastActiveMarker} from './util/appendLastActiveMarker';
2+
import {appendLastActiveMarkerToUserPage} from './util/appendLastActiveMarkerToUserPage';
3+
import {getUserNamesAndElements} from './util/getUserNamesAndElements';
64

7-
const baseParams: ApiQueryUserContribsParams = {
8-
action: 'query',
9-
format: 'json',
10-
list: 'usercontribs',
11-
uclimit: 1,
12-
smaxage: 600,
13-
maxage: 600,
14-
};
15-
type Usercontribs = {
16-
usercontribs: Array<{
17-
timestamp: string;
18-
}>;
19-
};
20-
21-
const whoIsActive = async ($content: JQuery<HTMLElement>): Promise<void> => {
22-
const usernames: string[] = [];
23-
const $elements: JQuery<HTMLAnchorElement>[] = [];
24-
25-
const {wgFormattedNamespaces} = mw.config.get();
26-
27-
const {2: localizedUserNamespace} = wgFormattedNamespaces;
28-
for (const element of $content.find<HTMLAnchorElement>(
29-
[
30-
'a[title^="User:"]:not(.mw-changeslist-date):not([href*="undo"])',
31-
`a[title^="${localizedUserNamespace}:"]:not(.mw-changeslist-date):not([href*="undo"])`,
32-
].join(',')
33-
)) {
34-
const $element: JQuery<HTMLAnchorElement> = $(element);
35-
36-
const userRegex: RegExp = new RegExp(`((User)|(${localizedUserNamespace})):(.*?)(?=&|$)`);
37-
const usernameMatchArray: RegExpMatchArray | null = decodeURI($element.attr('href') ?? '').match(userRegex);
38-
if (!usernameMatchArray) {
39-
continue;
40-
}
41-
42-
let [username] = usernameMatchArray;
43-
username = username.replace(new RegExp(`^((User)|(${localizedUserNamespace})):`, 'i'), '');
44-
const index: number = username.indexOf('/');
45-
if (index === -1) {
46-
$element.data('username', username);
47-
usernames[usernames.length] = username;
48-
$elements[$elements.length] = $element;
49-
}
50-
}
5+
const whoIsActive = ($content: JQuery<HTMLElement>): void => {
6+
const {usernames, $elements} = getUserNamesAndElements($content);
517

528
if (!usernames.length || !$elements.length) {
539
return;
5410
}
5511

56-
// Replace Set with uniqueArray, avoiding core-js polyfilling
57-
for (const username of uniqueArray(usernames)) {
58-
if (SYSTEM_SCRIPT_LIST.includes(username)) {
59-
continue;
60-
}
61-
62-
const params: ApiQueryUserContribsParams = {
63-
...baseParams,
64-
ucuser: username,
65-
};
66-
67-
const result = await api.get(params);
68-
69-
const {usercontribs} = result['query'] as Usercontribs;
70-
if (!usercontribs.length) {
71-
return;
72-
}
73-
74-
const {timestamp} = usercontribs[0]!;
75-
76-
for (const $element of $elements) {
77-
if ($element.data('username') === username) {
78-
$(getLastActiveMarker(timestamp, true)).insertAfter($element);
79-
}
80-
}
81-
}
12+
void appendLastActiveMarker({usernames, $elements});
8213
};
8314

84-
const whoIsActiveUserPage = async (): Promise<void> => {
15+
const whoIsActiveUserPage = (): void => {
8516
const {wgAction, wgNamespaceNumber, wgPageName, wgRelevantUserName} = mw.config.get();
8617

87-
if (wgRelevantUserName && wgNamespaceNumber === 2 && wgAction === 'view') {
88-
const relevantUserPageName: string = new mw.Title(wgRelevantUserName, 2).toText();
89-
const pageName: string = new mw.Title(wgPageName).toText();
90-
if (relevantUserPageName !== pageName) {
91-
return;
92-
}
93-
94-
const params: ApiQueryUserContribsParams = {
95-
...baseParams,
96-
ucuser: wgRelevantUserName,
97-
};
98-
99-
const result = await api.get(params);
100-
101-
const {usercontribs} = result['query'] as Usercontribs;
102-
if (!usercontribs.length) {
103-
return;
104-
}
18+
if (!wgRelevantUserName || wgNamespaceNumber !== 2 || wgAction !== 'view') {
19+
return;
20+
}
10521

106-
const {timestamp} = usercontribs[0]!;
107-
for (const element of document.querySelectorAll<HTMLElement>(OPTIONS.mountPointSelector)) {
108-
element.prepend(getLastActiveMarker(timestamp, false));
109-
}
22+
const relevantUserPageName: string = new mw.Title(wgRelevantUserName, 2).toText();
23+
const pageName: string = new mw.Title(wgPageName).toText();
24+
if (relevantUserPageName !== pageName) {
25+
return;
11026
}
27+
28+
void appendLastActiveMarkerToUserPage(wgRelevantUserName);
11129
};
11230

11331
export {whoIsActive, whoIsActiveUserPage};

src/WhoIsActive/modules/i18n.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {TimeSpan} from './getLastActiveMarker';
1+
import type {TimeSpan} from './util/getLastActiveMarker';
22
import {localize} from 'ext.gadget.i18n';
33

44
const getI18nMessages = () => {

src/WhoIsActive/modules/types.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
type Usercontribs = {
2+
usercontribs: Array<{
3+
timestamp: string;
4+
}>;
5+
};
6+
7+
export type {Usercontribs};
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as OPTIONS from '../options.json';
1+
import * as OPTIONS from '../../options.json';
22
import {initMwApi} from 'ext.gadget.Util';
33

44
const api: mw.Api = initMwApi(`WhoIsActive/${OPTIONS.version}`);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {SYSTEM_SCRIPT_LIST} from '../constant';
2+
import {getLastActiveMarker} from './getLastActiveMarker';
3+
import {getUserContribsTimestamp} from './getUserContribsTimestamp';
4+
import {uniqueArray} from 'ext.gadget.Util';
5+
6+
const appendLastActiveMarker = async ({usernames, $elements}: {usernames: string[]; $elements: JQuery[]}) => {
7+
// Replace Set with uniqueArray, avoiding core-js polyfilling
8+
for (const username of uniqueArray(usernames)) {
9+
if (SYSTEM_SCRIPT_LIST.includes(username)) {
10+
continue;
11+
}
12+
13+
const timestamp = await getUserContribsTimestamp(username);
14+
15+
if (!timestamp) {
16+
continue;
17+
}
18+
19+
for (const $element of $elements) {
20+
if ($element.data('username') === username) {
21+
$(getLastActiveMarker(timestamp, true)).insertAfter($element);
22+
}
23+
}
24+
}
25+
};
26+
27+
export {appendLastActiveMarker};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import * as OPTIONS from '../../options.json';
2+
import {SYSTEM_SCRIPT_LIST} from '../constant';
3+
import {getLastActiveMarker} from './getLastActiveMarker';
4+
import {getUserContribsTimestamp} from './getUserContribsTimestamp';
5+
6+
const appendLastActiveMarkerToUserPage = async (username: string) => {
7+
if (SYSTEM_SCRIPT_LIST.includes(username)) {
8+
return;
9+
}
10+
11+
const timestamp = await getUserContribsTimestamp(username);
12+
13+
if (!timestamp) {
14+
return;
15+
}
16+
17+
for (const element of document.querySelectorAll<HTMLElement>(OPTIONS.mountPointSelector)) {
18+
element.prepend(getLastActiveMarker(timestamp, false));
19+
}
20+
};
21+
22+
export {appendLastActiveMarkerToUserPage};

src/WhoIsActive/modules/getLastActiveMarker.tsx renamed to src/WhoIsActive/modules/util/getLastActiveMarker.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'ext.gadget.React';
2-
import {getMessage} from './i18n';
2+
import {getMessage} from '../i18n';
33

44
type TimeSpan = 'ThisWeek' | 'ThisMonth' | 'ThisSeason' | 'ThisSemiyear' | 'ThisYear' | 'OverAYear';
55

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as OPTIONS from '../../options.json';
2+
import {Usercontribs} from '../types';
3+
import {api} from './api';
4+
5+
const getUserContribsTimestamp = async (ucuser: string) => {
6+
let timestamp: string;
7+
8+
if (mw.storage.get(OPTIONS.storageKey + ucuser)) {
9+
timestamp = mw.storage.get(OPTIONS.storageKey + ucuser) as string;
10+
} else {
11+
const params: ApiQueryUserContribsParams = {
12+
action: 'query',
13+
format: 'json',
14+
list: 'usercontribs',
15+
uclimit: 1,
16+
smaxage: 600,
17+
maxage: 600,
18+
ucuser,
19+
};
20+
21+
const result = await api.get(params);
22+
23+
const {usercontribs} = result['query'] as Usercontribs;
24+
if (!usercontribs.length) {
25+
return;
26+
}
27+
28+
({timestamp} = usercontribs[0]!);
29+
30+
// Cache for 10 minutes
31+
mw.storage.set(OPTIONS.storageKey + ucuser, timestamp, 10 * 60);
32+
}
33+
34+
return timestamp;
35+
};
36+
37+
export {getUserContribsTimestamp};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const getUserNamesAndElements = ($content: JQuery) => {
2+
const usernames: string[] = [];
3+
const $elements: JQuery<HTMLAnchorElement>[] = [];
4+
5+
const {wgFormattedNamespaces} = mw.config.get();
6+
7+
const {2: localizedUserNamespace} = wgFormattedNamespaces;
8+
for (const element of $content.find<HTMLAnchorElement>(
9+
[
10+
'a[title^="User:"]:not(.mw-changeslist-date):not([href*="undo"])',
11+
`a[title^="${localizedUserNamespace}:"]:not(.mw-changeslist-date):not([href*="undo"])`,
12+
].join(',')
13+
)) {
14+
const $element: JQuery<HTMLAnchorElement> = $(element);
15+
16+
const userRegex: RegExp = new RegExp(`((User)|(${localizedUserNamespace})):(.*?)(?=&|$)`);
17+
const usernameMatchArray: RegExpMatchArray | null = decodeURI($element.attr('href') ?? '').match(userRegex);
18+
if (!usernameMatchArray) {
19+
continue;
20+
}
21+
22+
let [username] = usernameMatchArray;
23+
username = username.replace(new RegExp(`^((User)|(${localizedUserNamespace})):`, 'i'), '');
24+
const index: number = username.indexOf('/');
25+
if (index === -1) {
26+
$element.data('username', username);
27+
usernames[usernames.length] = username;
28+
$elements[$elements.length] = $element;
29+
}
30+
}
31+
32+
return {usernames, $elements};
33+
};
34+
35+
export {getUserNamesAndElements};

0 commit comments

Comments
 (0)