Skip to content

Commit 2e9e93c

Browse files
committed
core&ui: problem_main: support sort
1 parent e8573d7 commit 2e9e93c

10 files changed

Lines changed: 46 additions & 16 deletions

File tree

packages/hydrooj/src/handler/home.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ class HomeSecurityHandler extends Handler {
310310
userName: `${this.user.uname}(${this.user.mail})`,
311311
attestationType: 'direct',
312312
excludeCredentials: this.user._authenticators.map((c) => ({
313-
id: isoBase64URL.fromBuffer(c.credentialID.buffer),
313+
id: isoBase64URL.fromBuffer(new Uint8Array(c.credentialID.buffer)),
314314
type: 'public-key',
315315
})),
316316
authenticatorSelection: {

packages/hydrooj/src/handler/problem.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,12 @@ export class ProblemMainHandler extends Handler {
105105
@param('limit', Types.PositiveInt, true)
106106
@param('pjax', Types.Boolean)
107107
@param('quick', Types.Boolean)
108-
async get(domainId: string, page = 1, q = '', limit: number, pjax = false, quick = false) {
108+
@param('sort', Types.Range(['default', 'recent']), true)
109+
async get(domainId: string, page = 1, q = '', limit: number, pjax = false, quick = false, sortStrategy = 'default') {
109110
this.response.template = 'problem_main.html';
110111
if (!limit || limit > this.ctx.setting.get('pagination.problem') || page > 1) limit = this.ctx.setting.get('pagination.problem');
111112
this.queryContext.query = buildQuery(this.user);
113+
if (sortStrategy === 'recent') this.queryContext.hint = 'basic';
112114
// eslint-disable-next-line ts/no-shadow
113115
const query = this.queryContext.query;
114116
const psdict = {};
@@ -145,11 +147,15 @@ export class ProblemMainHandler extends Handler {
145147
}
146148
const sort = this.queryContext.sort;
147149
await this.ctx.parallel('problem/list', query, this, sort);
150+
const sortKey = ({
151+
default: { sort: 1, docId: 1 },
152+
recent: { docId: -1 },
153+
} as const)[sortStrategy];
148154
let [pdocs, ppcount, pcount] = this.queryContext.fail
149155
? [[], 0, 0]
150156
: await this.paginate(
151157
problem.getMulti(domainId, query, quick ? ['title', 'pid', 'domainId', 'docId'] : undefined)
152-
.sort({ sort: 1, docId: 1 }).hint(this.queryContext.hint),
158+
.sort(sortKey).hint(this.queryContext.hint),
153159
sort.length ? 1 : page, limit,
154160
);
155161
if (total) {
@@ -169,7 +175,7 @@ export class ProblemMainHandler extends Handler {
169175
title: this.renderTitle(this.translate('problem_main')),
170176
fragments: (await Promise.all([
171177
this.renderHTML('partials/problem_list.html', {
172-
page, ppcount, pcount, pdocs, psdict, qs: q,
178+
page, ppcount, pcount, pdocs, psdict, qs: q, sort: sortStrategy,
173179
}),
174180
this.renderHTML('partials/problem_stat.html', { pcount, pcountRelation: this.queryContext.pcountRelation }),
175181
this.renderHTML('partials/problem_lucky.html', { qs: q }),
@@ -184,6 +190,7 @@ export class ProblemMainHandler extends Handler {
184190
pdocs,
185191
psdict,
186192
qs: q,
193+
sort: sortStrategy,
187194
};
188195
}
189196
}

packages/hydrooj/src/handler/user.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class UserWebauthnHandler extends Handler {
158158
if (!udoc._id) throw new UserNotFoundError(uname || 'user');
159159
if (!udoc.authn) throw new AuthOperationError('authn', 'disabled');
160160
allowCredentials = udoc._authenticators.map((authenticator) => ({
161-
id: isoBase64URL.fromBuffer(authenticator.credentialID.buffer),
161+
id: isoBase64URL.fromBuffer(new Uint8Array(authenticator.credentialID.buffer)),
162162
}));
163163
uid = udoc._id;
164164
}
@@ -194,8 +194,8 @@ class UserWebauthnHandler extends Handler {
194194
expectedRPID: this.getAuthnHost(),
195195
credential: {
196196
...authenticator,
197-
id: isoBase64URL.fromBuffer(authenticator.credentialID.buffer),
198-
publicKey: authenticator.credentialPublicKey.buffer,
197+
id: isoBase64URL.fromBuffer(new Uint8Array(authenticator.credentialID.buffer)),
198+
publicKey: new Uint8Array(authenticator.credentialPublicKey.buffer),
199199
},
200200
}).catch(() => null);
201201
if (!verification?.verified) throw new ValidationError('authenticator');

packages/hydrooj/src/model/document.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,9 +414,9 @@ export async function apply(ctx: Context) {
414414
coll.deleteMany({ domainId }),
415415
collStatus.deleteMany({ domainId }),
416416
]));
417-
await db.clearIndexes(coll, ['tag', 'hidden']);
417+
await ctx.db.clearIndexes(coll, ['tag', 'hidden']);
418418
const onlyFor = (docType: number) => ({ partialFilterExpression: { docType } });
419-
await db.ensureIndexes(
419+
await ctx.db.ensureIndexes(
420420
coll,
421421
{ key: { domainId: 1, docType: 1, docId: 1 }, name: 'basic', unique: true },
422422
{ key: { domainId: 1, docType: 1, owner: 1, docId: -1 }, name: 'owner' },

packages/ui-default/components/autocomplete/components/ProblemSelectAutoComplete.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const ProblemSelectAutoComplete = forwardRef<AutoCompleteHandle<ProblemDoc>, Aut
99
ref={ref as any}
1010
cacheKey={`problem-${UiContext.domainId}`}
1111
queryItems={async (query) => {
12-
const { pdocs } = await request.get(`/d/${UiContext.domainId}/p`, { q: query, quick: true });
12+
const { pdocs } = await request.get(`/d/${UiContext.domainId}/p`, { q: query, quick: true, sort: query ? 'default' : 'recent' });
1313
return pdocs;
1414
}}
1515
fetchItems={(ids) => api('problems', { ids: ids.map((i) => +i) }, ['docId', 'pid', 'title'])}
@@ -29,7 +29,7 @@ const ProblemSelectAutoComplete = forwardRef<AutoCompleteHandle<ProblemDoc>, Aut
2929
listStyle: {},
3030
multi: false,
3131
selectedKeys: [],
32-
allowEmptyQuery: false,
32+
allowEmptyQuery: true,
3333
freeSolo: false,
3434
freeSoloConverter: (input) => input,
3535
...props,

packages/ui-default/locales/en.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ problem_import: Import Problem
2727
problem_main: Problem Set
2828
problem_solution: Problem Solution
2929
problem_submit: Problem Submit
30+
problem_type.communication: 'Type: Communication'
3031
problem_type.default: 'Type: Default'
3132
problem_type.interactive: 'Type: Interactive'
32-
problem_type.communication: 'Type: Communication'
3333
problem_type.objective: 'Type: Objective'
3434
problem_type.remote_judge: 'Type: RemoteJudge'
3535
problem_type.submit_answer: 'Type: SubmitAnswer'
@@ -54,6 +54,8 @@ setting_info: Personal Info
5454
setting_preference: Preference
5555
setting_privacy: Privacy
5656
setting_usage: Usage Preference
57+
sort::default: Default
58+
sort::recent: Recent
5759
tasks_list: Task list
5860
timeago_locale: en_US
5961
training_create: Training Create

packages/ui-default/locales/zh.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,8 @@ Sorry, there are no training plans.: 目前没有训练计划。
877877
Sorry, there are no users in this domain.: 对不起,此域中没有用户。
878878
Sorry: 对不起
879879
Sort by: 排序
880+
sort::default: 默认排序
881+
sort::recent: 最新题目
880882
Source type: 来源类型
881883
Source: 来源
882884
Standard mode: 标准模式

packages/ui-default/pages/problem_main.page.styl

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@
204204
color: #000000
205205

206206
.search > button
207-
width: 40px
207+
width: 37px
208208
height: 36px
209209
border: 1px solid #00B4CC
210210
background: #00B4CC
@@ -215,6 +215,16 @@
215215

216216
.search > div
217217
position: absolute
218-
right: 50px
219-
top: 8px
220-
font-size: rem(17px)
218+
right: 170px
219+
top: 10px
220+
font-size: rem(17px)
221+
222+
.search-sort
223+
position: absolute
224+
border: none
225+
top: 1px
226+
height: 34px
227+
right: 37px
228+
color: #000000
229+
cursor: pointer
230+
width: 120px

packages/ui-default/pages/problem_main.page.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,12 @@ function updateSelection() {
8585

8686
function loadQuery() {
8787
const q = $('[name="q"]').val().toString();
88+
const sort = $('[name="sort"]').val().toString();
8889
const url = new URL(window.location.href);
8990
if (!q) url.searchParams.delete('q');
9091
else url.searchParams.set('q', q);
92+
if (sort && sort !== 'default') url.searchParams.set('sort', sort);
93+
else url.searchParams.delete('sort');
9194
url.searchParams.delete('page');
9295
pjax.request({ url: url.toString() });
9396
}
@@ -443,6 +446,7 @@ const page = new NamedPage(['problem_main'], () => {
443446
});
444447
$('#searchForm').on('submit', inputChanged);
445448
$('#searchForm').find('input').on('input', _.debounce(inputChanged, 500));
449+
$('#searchForm').find('select[name="sort"]').on('change', inputChanged);
446450
$('.dialog-button').on('click', (ev) => {
447451
categoryDialog.clear().open();
448452
ev.preventDefault();

packages/ui-default/templates/problem_main.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
<div class="search">
1111
<input name="q" type="text" value="{{ qs }}" placeholder="{{ _('Search') }}">
1212
{% include "partials/problem_stat.html" %}
13+
<select name="sort" class="search-sort">
14+
{%- for sortType in ["default", "recent"] -%}
15+
<option value="{{ sortType }}" {{ "selected" if sort == sortType else "" }}>{{ _('sort::' + sortType) }}</option>
16+
{%- endfor -%}
17+
</select>
1318
<button type="submit">
1419
<span class="icon icon-search"></span>
1520
</button>

0 commit comments

Comments
 (0)