Skip to content

Commit 9c01a79

Browse files
authored
refactor(MarkRights): split modules; rewrite code (qiuwenbaike#1648)
* refactor(MarkRights): split modules; rewrite code
1 parent 54722a9 commit 9c01a79

File tree

5 files changed

+294
-253
lines changed

5 files changed

+294
-253
lines changed

dist/MarkRights/MarkRights.js

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

src/MarkRights/modules/core.ts

Lines changed: 62 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,166 +1,85 @@
11
import {queryGlobalUserGroups, queryUserGroups} from './query';
2-
import type {UserRights} from './types';
3-
import {getMessage} from './i18n';
4-
import {uniqueArray} from 'ext.gadget.Util';
2+
import {appendUserRightsMark} from './util/appendUserRightsMark';
3+
import {generateUserLinks} from './util/generateUserLinks';
54

6-
const getUsername = (url: string): string => {
7-
if (!url) {
8-
return '';
9-
}
5+
const markUserRights = ($content: JQuery): void => {
6+
const {users, $userLinks} = generateUserLinks($content);
107

11-
const username: string | null = mw.util.getParamValue('title', url);
8+
const promises: (() => Promise<void>)[] = [];
129

13-
const decode = (string: string, replace: (_string: string) => string): string => {
14-
return decodeURIComponent(
15-
((): string => {
16-
try {
17-
return decodeURIComponent(replace(string));
18-
} catch {
19-
return replace(string).replace(/%(?!\d+)/g, '%25');
20-
}
21-
})()
22-
);
23-
};
24-
25-
if (username) {
26-
return decode(username, (string: string): string => {
27-
return string.replace('User:', '').replace(/_/g, ' ');
28-
});
29-
}
30-
const usernameMatch: RegExpMatchArray | null = url.match(/\/wiki\/User:(.+?)$/);
31-
if (usernameMatch?.[1]) {
32-
return decode(usernameMatch[1], (string: string): string => {
33-
return string.replace(/_/g, ' ');
34-
});
35-
}
36-
return '';
37-
};
38-
39-
const appendUserRightsMark = (
40-
$userLinks: JQuery<HTMLElement>,
41-
{userGroupMap, globalUserGroupMap}: {userGroupMap: Map<string, string[]>; globalUserGroupMap: Map<string, string[]>}
42-
): void => {
43-
for (const element of $userLinks) {
44-
const $element: JQuery = $(element);
45-
if ($element.parents('li').find('.gadgets-markrights').length) {
46-
continue;
47-
}
48-
if ($element.siblings('.gadgets-markrights').length) {
10+
for (let i = 0; i < users.length; i++) {
11+
const ususers = users.splice(0, 25);
12+
if (!ususers.length) {
4913
continue;
5014
}
51-
const username: string = getUsername($element.attr('href') ?? '');
52-
if (!username) {
53-
continue;
54-
}
55-
const groups = userGroupMap.get(username) ?? [];
56-
const globalGroups = globalUserGroupMap.get(username) ?? [];
57-
if (!groups) {
58-
continue;
59-
}
60-
const $sups: JQuery = $('<span>').addClass('gadgets-markrights');
61-
for (const group of uniqueArray([...groups, ...globalGroups])) {
62-
const className: string = `gadgets-markrights__${group}`;
63-
if ($sups.find('sup').hasClass(className)) {
64-
continue;
65-
}
66-
$sups.append(
67-
// The following classes are used here:
68-
// * gadget-markrights__qiuwen
69-
// * gadget-markrights__steward
70-
// * gadget-markrights__checkuser
71-
// * gadget-markrights__suppress
72-
// * gadget-markrights__sysop
73-
// * gadget-markrights__interface-admin
74-
// * gadget-markrights__templateeditor
75-
// * gadget-markrights__transwiki
76-
// * gadget-markrights__patroller
77-
// * gadget-markrights__autoreviewer
78-
// * gadget-markrights__senioreditor
79-
// * gadget-markrights__eventsponsor
80-
// * gadget-markrights__massmessage-sender
81-
// * gadget-markrights__confirmed
82-
// * gadget-markrights__autoconfirmed
83-
// * gadget-markrights__bot
84-
// * gadget-markrights__flood
85-
// * gadget-markrights__ipblock-exempt
86-
// * gadget-markrights__rnrsverify-exempt
87-
$('<sup>')
88-
.addClass(className)
89-
.attr({
90-
alt: getMessage(group as UserRights),
91-
title: getMessage(group as UserRights),
92-
})
93-
);
94-
}
95-
$element.after($sups);
96-
}
97-
};
9815

99-
const markUserRights = async ($content: JQuery): Promise<void> => {
100-
const $userLinks: JQuery = $content.find('a.mw-userlink:not(.mw-anonuserlink)');
101-
let users: string[] = [];
102-
const queue: Array<typeof users> = [];
103-
const userGroupMap: Map<string, string[]> = new Map();
104-
const globalUserGroupMap: Map<string, string[]> = new Map();
16+
promises[promises.length] = async (): Promise<void> => {
17+
const userGroups: Record<string, string[]> = {};
10518

106-
for (const element of $userLinks) {
107-
const $element: JQuery = $(element);
108-
if ($element.parents('li').find('.gadgets-markrights').length) {
109-
continue;
110-
}
111-
if ($element.siblings('.gadgets-markrights').length) {
112-
continue;
113-
}
114-
const {textContent} = element;
115-
const userLinkText: string | undefined = textContent?.toString();
116-
if (userLinkText) {
117-
users[users.length] = userLinkText; // Replace `[].push()` to avoid polyfilling core-js
118-
}
19+
try {
20+
const response = await queryUserGroups(ususers);
21+
const {users: queryUsers} = response['query'] as {
22+
users: {groups: string[]; name: string}[];
23+
};
11924

120-
// 用户名列表去重
121-
users = uniqueArray(users); // Replace `[...new Set()]` to avoid polyfilling core-js
25+
for (const user of queryUsers) {
26+
if (!user?.name || !user?.groups) {
27+
continue;
28+
}
12229

123-
if (users.length === 25) {
124-
queue[queue.length] = users; // Replace `[].push()` to avoid polyfilling core-js
125-
users = [];
126-
}
127-
}
128-
129-
if (users.length > 0) {
130-
queue[queue.length] = users; // Replace `[].push()` to avoid polyfilling core-js
131-
}
30+
const {name, groups} = user;
13231

133-
for (const ususers of queue) {
134-
try {
135-
const queryUserResponse = await queryUserGroups(ususers);
136-
const {users: queryUsers} = queryUserResponse['query'] as {
137-
users: {groups: string[]; name: string}[];
138-
};
32+
userGroups[name] ??= [];
13933

140-
for (const user of queryUsers) {
141-
if (!user || !user.groups) {
142-
continue;
34+
userGroups[name] = [
35+
...userGroups[name],
36+
...groups.filter((element) => {
37+
return element !== '*';
38+
}),
39+
];
14340
}
144-
userGroupMap.set(
145-
user.name,
146-
user.groups.filter((element) => {
147-
return element !== '*';
148-
})
149-
);
41+
} catch (error: unknown) {
42+
console.error('[MarkRights] Ajax error:', error);
15043
}
15144

45+
appendUserRightsMark($userLinks, userGroups);
46+
};
47+
48+
promises[promises.length] = async (): Promise<void> => {
49+
const userGroups: Record<string, string[]> = {};
50+
15251
for (const user of ususers) {
153-
const queryGlobalUserInfoResponse = await queryGlobalUserGroups(user);
154-
if (queryGlobalUserInfoResponse['query'] && queryGlobalUserInfoResponse['query'].globaluserinfo) {
155-
const {groups: globalgroups}: {groups: string[]} = queryGlobalUserInfoResponse['query']
156-
.globaluserinfo as {groups: string[]};
157-
globalUserGroupMap.set(user, globalgroups);
52+
try {
53+
const response = await queryGlobalUserGroups(user);
54+
const {globaluserinfo} = response['query'] as {
55+
globaluserinfo: {groups: string[]};
56+
};
57+
58+
if (!globaluserinfo?.groups) {
59+
continue;
60+
}
61+
62+
const {groups} = globaluserinfo;
63+
64+
userGroups[user] ??= [];
65+
66+
userGroups[user] = [...userGroups[user], ...groups];
67+
} catch (error: unknown) {
68+
console.error('[MarkRights] Ajax error:', error);
15869
}
15970
}
16071

161-
appendUserRightsMark($userLinks, {userGroupMap, globalUserGroupMap});
162-
} catch {}
72+
appendUserRightsMark($userLinks, userGroups);
73+
};
16374
}
75+
76+
void (async () => {
77+
for (const promise of promises) {
78+
try {
79+
await promise();
80+
} catch {}
81+
}
82+
})();
16483
};
16584

16685
export {markUserRights};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type {UserRights} from '../types';
2+
import {getMessage} from '../i18n';
3+
import {getUserName} from './getUserName';
4+
import {uniqueArray} from 'ext.gadget.Util';
5+
6+
const appendUserRightsMark = ($userLinks: JQuery<HTMLAnchorElement>[], userGroups: Record<string, string[]>): void => {
7+
for (const $element of $userLinks) {
8+
const username: string = getUserName($element.attr('href') ?? '');
9+
if (!username) {
10+
continue;
11+
}
12+
13+
userGroups ??= {};
14+
const groups = userGroups[username] ?? [];
15+
16+
let $sups: JQuery;
17+
if ($element.parents('li').find('.gadgets-markrights').length) {
18+
$sups = $element.siblings('.gadgets-markrights').eq(0);
19+
} else if ($element.siblings('.gadgets-markrights').length) {
20+
$sups = $element.parents('li').find('.gadgets-markrights').eq(0);
21+
} else {
22+
$sups = $('<span>').addClass('gadgets-markrights');
23+
$element.after($sups);
24+
}
25+
26+
for (const group of uniqueArray(groups)) {
27+
const className: string = `gadgets-markrights__${group}`;
28+
if ($sups.find('sup').hasClass(className)) {
29+
continue;
30+
}
31+
32+
$sups.append(
33+
// The following classes are used here:
34+
// * see ../types.d.ts
35+
// * for more information
36+
$('<sup>')
37+
.addClass(className)
38+
.attr({
39+
alt: getMessage(group as UserRights),
40+
title: getMessage(group as UserRights),
41+
})
42+
);
43+
}
44+
}
45+
};
46+
47+
export {appendUserRightsMark};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const generateUserLinks = ($content: JQuery): {users: string[]; $userLinks: JQuery<HTMLAnchorElement>[]} => {
2+
const users: string[] = [];
3+
const $userLinks: JQuery<HTMLAnchorElement>[] = [];
4+
const $elements = $content.find<HTMLAnchorElement>('a.mw-userlink:not(.mw-anonuserlink)');
5+
6+
for (const element of $elements) {
7+
const $element: JQuery<HTMLAnchorElement> = $(element);
8+
if (
9+
$element.parents('li').find('.gadgets-markrights').length ||
10+
$element.siblings('.gadgets-markrights').length
11+
) {
12+
continue;
13+
}
14+
15+
$userLinks[$userLinks.length] = $element;
16+
17+
const {textContent} = element;
18+
const user: string | undefined = textContent?.toString();
19+
20+
if (!user) {
21+
continue;
22+
}
23+
24+
users[users.length] = user;
25+
}
26+
27+
return {users, $userLinks};
28+
};
29+
30+
export {generateUserLinks};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const getUserName = (url: string): string => {
2+
if (!url) {
3+
return '';
4+
}
5+
6+
const username: string | null = mw.util.getParamValue('title', url);
7+
8+
const decode = (string: string, replace: (_string: string) => string): string => {
9+
return decodeURIComponent(
10+
((): string => {
11+
try {
12+
return decodeURIComponent(replace(string));
13+
} catch {
14+
return replace(string).replace(/%(?!\d+)/g, '%25');
15+
}
16+
})()
17+
);
18+
};
19+
20+
if (username) {
21+
return decode(username, (string: string): string => {
22+
return string.replace('User:', '').replace(/_/g, ' ');
23+
});
24+
}
25+
26+
const usernameMatch: RegExpMatchArray | null = url.match(/\/wiki\/User:(.+?)$/);
27+
28+
if (usernameMatch?.[1]) {
29+
return decode(usernameMatch[1], (string: string): string => {
30+
return string.replace(/_/g, ' ');
31+
});
32+
}
33+
34+
return '';
35+
};
36+
37+
export {getUserName};

0 commit comments

Comments
 (0)