8000 feat: daily problems · DGGua/vscode-leetcode@d130972 · GitHub
[go: up one dir, main page]

Skip to content

Commit d130972

Browse files
committed
feat: daily problems
1 parent d713ad5 commit d130972

8 files changed

+106
-7
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@
723723
"typescript": "^4.3.2"
724724
},
725725
"dependencies": {
726+
"axios": "^0.27.2",
726727
"fs-extra": "^10.0.0",
727728
"highlight.js": "^10.7.2",
728729
"lodash": "^4.17.21",

src/commands/list.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// Copyright (c) jdneo. All rights reserved.
22
// Licensed under the MIT license.
33

4+
import axios from "axios"
45
import * as vscode from "vscode";
56
import { leetCodeExecutor } from "../leetCodeExecutor";
67
import { leetCodeManager } from "../leetCodeManager";
7-
import { IProblem, ProblemState, UserStatus } from "../shared";
8+
import { DailyProblem, DailyProblemSet, IProblem, ProblemState, UserStatus } from "../shared";
89
import * as settingUtils from "../utils/settingUtils";
910
import { DialogType, promptForOpenOutputChannel } from "../utils/uiUtils";
1011

@@ -45,6 +46,32 @@ export async function listProblems(): Promise<IProblem[]> {
4546
}
4647
}
4748

49+
export async function listDailyProblems(context: vscode.ExtensionContext): Promise<DailyProblem[]> {
50+
try {
51+
const cookie = context.globalState.get<string>("cookie")
52+
if (leetCodeManager.getStatus() === UserStatus.SignedOut || cookie === undefined) {
53+
return [];
54+
}
55+
const date = new Date();
56+
const res = await axios.post<DailyProblemSet>(
57+
"https://leetcode.cn/graphql/", {
58+
"query": "\n query dailyQuestionRecords($year: Int!, $month: Int!) {\n dailyQuestionRecords(year: $year, month: $month) {\n date\n userStatus\n question {\n questionFrontendId\n title\n titleSlug\n translatedTitle\n }\n }\n}\n ",
59+
"variables": {
60+
"year": date.getFullYear(),
61+
"month": date.getMonth() + 1,
62+
}
63+
}, {
64+
headers: {
65+
cookie,
66+
},
67+
})
68+
return res.data.data.dailyQuestionRecords;
69+
} catch (e) {
70+
await promptForOpenOutputChannel("Failed to list daily Problems. Please open the output channel for details.", DialogType.error);
71+
return []
72+
}
73+
}
74+
4875
function parseProblemState(stateOutput: string): ProblemState {
4976
if (!stateOutput) {
5077
return ProblemState.Unknown;

src/explorer/LeetCodeNode.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { IProblem, ProblemState } from "../shared";
66

77
export class LeetCodeNode {
88

9-
constructor(private data: IProblem, private isProblemNode: boolean = true) { }
9+
constructor(protected data: IProblem, private isProblemNode: boolean = true) { }
1010

1111
public get locked(): boolean {
1212
return this.data.locked;
@@ -47,6 +47,10 @@ export class LeetCodeNode {
4747
return this.isProblemNode;
4848
}
4949

50+
public get dataClone(): IProblem {
51+
return Object.assign({}, this.data)
52+
}
53+
5054
public get previewCommand(): Command {
5155
return {
5256
title: "Preview Problem",
@@ -67,5 +71,11 @@ export class LeetCodeNode {
6771
query: `difficulty=${this.difficulty}`,
6872
});
6973
}
74+
}
7075

76+
export class LeetCodeDailyNode extends LeetCodeNode {
77+
78+
constructor(node: LeetCodeNode, public date: Date) {
79+
super(node.dataClone);
80+
}
7181
}

src/explorer/LeetCodeTreeDataProvider.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as vscode from "vscode";
77
import { leetCodeManager } from "../leetCodeManager";
88
import { Category, defaultProblem, ProblemState } from "../shared";
99
import { explorerNodeManager } from "./explorerNodeManager";
10-
import { LeetCodeNode } from "./LeetCodeNode";
10+
import { LeetCodeDailyNode, LeetCodeNode } from "./LeetCodeNode";
1111

1212
export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCodeNode> {
1313

@@ -46,7 +46,11 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
4646
}
4747

4848
return {
49-
label: element.isProblem ? `[${element.id}] ${element.name}` : element.name,
49+
label: element.isProblem ?
50+
`[${element instanceof LeetCodeDailyNode ?
51+
`${element.date.getMonth()}-${element.date.getDate()}` :
52+
element.id}] ${element.name}` :
53+
element.name,
5054
tooltip: this.getSubCategoryTooltip(element),
5155
collapsibleState: element.isProblem ? vscode.TreeItemCollapsibleState.None : vscode.TreeItemCollapsibleState.Collapsed,
5256
iconPath: this.parseIconPathFromProblemState(element),
@@ -69,6 +73,8 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
6973
return explorerNodeManager.getRootNodes();
7074
} else {
7175
switch (element.id) { // First-level
76+
case Category.Daily:
77+
return explorerNodeManager.getDailyNodes();
7278
case Category.All:
7379
return explorerNodeManager.getAllNodes();
7480
case Category.Favorite:

src/explorer/explorerNodeManager.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@
22
// Licensed under the MIT license.
33

44
import * as _ from "lodash";
5-
import { Disposable } from "vscode";
5+
import { Disposable, ExtensionContext } from "vscode";
66
import * as list from "../commands/list";
77
import { getSortingStrategy } from "../commands/plugin";
88
import { Category, defaultProblem, ProblemState, SortingStrategy } from "../shared";
99
import { shouldHideSolvedProblem } from "../utils/settingUtils";
10-
import { LeetCodeNode } from "./LeetCodeNode";
10+
import { LeetCodeDailyNode, LeetCodeNode } from "./LeetCodeNode";
1111

1212
class ExplorerNodeManager implements Disposable {
1313
private explorerNodeMap: Map<string, LeetCodeNode> = new Map<string, LeetCodeNode>();
14+
private dailyProblemMap: Map<Date, LeetCodeNode> = new Map<Date, LeetCodeNode>();
15+
private context: ExtensionContext;
1416
private companySet: Set<string> = new Set<string>();
1517
private tagSet: Set<string> = new Set<string>();
1618

19+
public initialize(context: ExtensionContext) {
20+
this.context = context;
21+
}
22+
1723
public async refreshCache(): Promise<void> {
1824
this.dispose();
1925
const shouldHideSolved: boolean = shouldHideSolvedProblem();
@@ -29,10 +35,26 @@ class ExplorerNodeManager implements Disposable {
2935
this.tagSet.add(tag);
3036
}
3137
}
38+
for (const dailyProblem of await list.listDailyProblems(this.context)) {
39+
const { date: dateStr, question } = dailyProblem
40+
const [year, month, day] = dateStr.split("-").map(s => Number.parseInt(s));
41+
const node = this.explorerNodeMap.get(question.questionFrontendId);
42+
if (!node) {
43+
continue;
44+
}
45+
const date = new Date(year, month, day);
46+
this.dailyProblemMap.set(date, new LeetCodeDailyNode(node, date));
47+
}
3248
}
3349

3450
public getRootNodes(): LeetCodeNode[] {
3551
return [
52+
Array.from(this.dailyProblemMap.entries())
53+
.sort((a, b) => b[0].getTime() - a[0].getTime())[0][1],
54+
new LeetCodeNode(Object.assign({}, defaultProblem, {
55+
id: Category.Daily,
56+
name: Category.Daily,
57+
}), false),
3658
new LeetCodeNode(Object.assign({}, defaultProblem, {
3759
id: Category.All,
3860
name: Category.All,
@@ -56,6 +78,12 @@ class ExplorerNodeManager implements Disposable {
5678
];
5779
}
5880

81+
public getDailyNodes(): LeetCodeNode[] {
82+
return Array.from(this.dailyProblemMap.entries())
83+
.sort((a, b) => b[0].getTime() - a[0].getTime())
84+
.map(pair => pair[1]);
85+
}
86+
5987
public getAllNodes(): LeetCodeNode[] {
6088
return this.applySortingStrategy(
6189
Array.from(this.explorerNodeMap.values()),

src/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
3737
});
3838

3939
leetCodeTreeDataProvider.initialize(context);
40+
leetCodeManager.initialize(context);
41+
explorerNodeManager.initialize(context);
4042

4143
context.subscriptions.push(
4244
leetCodeStatusBarController,

src/leetCodeManager.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@ class LeetCodeManager extends EventEmitter {
1616
private userStatus: UserStatus;
1717
private readonly successRegex: RegExp = /(?:.*)Successfully .*login as (.*)/i;
1818
private readonly failRegex: RegExp = /.*\[ERROR\].*/i;
19-
19+
private context : vscode.ExtensionContext;
2020
constructor() {
2121
super();
2222
this.currentUser = undefined;
2323
this.userStatus = UserStatus.SignedOut;
2424
}
2525

26+
public initialize(context:vscode.ExtensionContext){
27+
this.context = context;
28+
}
29+
2630
public async getLoginStatus(): Promise<void> {
2731
try {
2832
const result: string = await leetCodeExecutor.getUserInfo();
@@ -131,6 +135,9 @@ class LeetCodeManager extends EventEmitter {
131135
childProc.kill();
132136
return resolve(undefined);
133137
}
138+
if(isByCookie){
139+
this.context.globalState.update("cookie", pwd);
140+
}
134141
childProc.stdin?.write(`${pwd}\n`);
135142
});
136143
if (userName) {

src/shared.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,23 @@ export enum Endpoint {
7070
LeetCodeCN = "leetcode-cn",
7171
}
7272

73+
export interface DailyProblem {
74+
date: string,
75+
question: {
76+
questionFrontendId: string
77+
title: string
78+
titleSlug: string
79+
translatedTitle: string
80+
}
81+
userStatus: string
82+
}
83+
84+
export interface DailyProblemSet {
85+
data: {
86+
dailyQuestionRecords: DailyProblem[]
87+
}
88+
}
89+
7390
export interface IProblem {
7491
isFavorite: boolean;
7592
locked: boolean;
@@ -95,6 +112,7 @@ export const defaultProblem: IProblem = {
95112
};
96113

97114
export enum Category {
115+
Daily = "Daily",
98116
All = "All",
99117
Difficulty = "Difficulty",
100118
Tag = "Tag",

0 commit comments

Comments
 (0)
0