Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 110 additions & 36 deletions src/adapters/leetcode/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import {
import { sendRuntimeMessage } from "../../shared/runtime";
import type { RuntimeMessageResponse } from "../../core/types/messages";
import type { ExtensionSettings } from "../../core/types/domain";
import type { UploadJob } from "../../core/types/upload";
import type { ProblemNoteRequest, UploadJob } from "../../core/types/upload";
import type { PlatformAdapter } from "../types";
import { burstConfetti } from "../confetti";
import { uploadThroughBackground } from "../upload";
import { openSyncedActionsModal } from "../problemActionsModal";
import {
appendProblemNoteThroughBackground,
uploadThroughBackground,
} from "../upload";

const SUBMIT_BUTTON_SELECTOR = '[data-e2e-locator="console-submit-button"]';
const SUBMISSION_RESULT_SELECTOR = '[data-e2e-locator="submission-result"]';
Expand Down Expand Up @@ -68,6 +72,12 @@ type GraphQLResponse<T> = {
errors?: Array<{ message: string }>;
};

type SyncedProblemContext = {
settings: ExtensionSettings;
job: UploadJob;
repositoryUrl: string;
};

function isProblemPage(url: URL) {
return url.hostname.includes("leetcode.com") && url.pathname.includes("/problems/");
}
Expand Down Expand Up @@ -119,6 +129,28 @@ function escapePipe(value: string) {
return value.replace(/\|/g, "\\|");
}

function formatArchiveStamp(date = new Date()) {
const format = new Intl.DateTimeFormat("sv-SE", {
timeZone: "Asia/Seoul",
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
});

return format
.format(date)
.replace(" ", "_")
.replaceAll(":", "-");
}

function createArchiveFileName(extension: string, uniqueSuffix: string) {
return `${formatArchiveStamp()}_${uniqueSuffix}${extension}`;
}

function createProblemReadme(
details: LeetCodeSubmissionDetails,
submissionId: string
Expand Down Expand Up @@ -208,34 +240,37 @@ function renderStatusContent(marker: HTMLElement, text: string) {
marker.append(icon, label);
}

function setStatusLink(marker: HTMLElement, url?: string) {
function setStatusLink(
marker: HTMLElement,
action?: () => void,
title?: string
) {
marker.onclick = null;

if (!url) {
if (!action) {
marker.style.cursor = "default";
marker.removeAttribute("title");
return;
}

marker.style.cursor = "pointer";
marker.title = "Open synced commit";
marker.onclick = () => {
window.open(url, "_blank", "noopener,noreferrer");
};
marker.title = title ?? "Open synced actions";
marker.onclick = action;
}

function setInlineStatus(
text: string,
tone: "working" | "success" | "error",
url?: string
action?: () => void,
actionTitle?: string
) {
const marker = ensureStatusMarker();
if (!marker) {
return;
}

renderStatusContent(marker, text);
setStatusLink(marker, tone === "success" ? url : undefined);
setStatusLink(marker, tone === "success" ? action : undefined, actionTitle);

if (tone === "working") {
marker.style.background = "#1f2937";
Expand Down Expand Up @@ -434,6 +469,11 @@ function buildUploadJob(
content: details.code,
});

job = addUploadFile(job, {
path: `archives/${createArchiveFileName(extension, submissionId)}`,
content: details.code,
});

if (settings.platforms.leetcode.createProblemReadme) {
job = addUploadFile(job, {
path: "README.md",
Expand All @@ -454,42 +494,44 @@ function buildUploadJob(
function createSubmissionHandler() {
const handledSubmissionIds = new Set<string>();
let lastTriggerAt = 0;
let latestSyncContext: SyncedProblemContext | null = null;

return async function handleSubmissionTrigger() {
const settings = await getSettings();
try {
const settings = await getSettings();

if (!settings.platforms.leetcode.enabled || !settings.platforms.leetcode.autoUpload) {
return;
}
if (!settings.platforms.leetcode.enabled || !settings.platforms.leetcode.autoUpload) {
return;
}

if (!(await isExtensionEnabled())) {
return;
}
if (!(await isExtensionEnabled())) {
return;
}

const now = Date.now();
if (now - lastTriggerAt < 1500) {
return;
}
lastTriggerAt = now;
const now = Date.now();
if (now - lastTriggerAt < 1500) {
return;
}
lastTriggerAt = now;

const submissionId = await waitForSubmissionId();
if (!submissionId || handledSubmissionIds.has(submissionId)) {
return;
}
const submissionId = await waitForSubmissionId();
if (!submissionId || handledSubmissionIds.has(submissionId)) {
return;
}

const accepted = await waitForAcceptedState();
if (!accepted) {
clearInlineStatus();
return;
}
const accepted = await waitForAcceptedState();
if (!accepted) {
clearInlineStatus();
return;
}

handledSubmissionIds.add(submissionId);
try {
handledSubmissionIds.add(submissionId);
if (!(await isExtensionEnabled())) {
clearInlineStatus();
return;
}

latestSyncContext = null;
setInlineStatus("Syncing...", "working");
const details = await getSubmissionDetails(submissionId);

Expand All @@ -501,14 +543,46 @@ function createSubmissionHandler() {
const record = await Promise.all([uploadThroughBackground(job), wait(700)]).then(
([uploadRecord]) => uploadRecord
);
latestSyncContext = {
settings,
job,
repositoryUrl: `https://github.com/${record.repository}/tree/${record.branch}/${encodeURI(
job.directory
)}`,
};
setInlineStatus(
"Synced",
"success",
`https://github.com/${record.repository}/tree/${record.branch}/${encodeURI(
record.filePaths[0]?.split("/").slice(0, -1).join("/") ?? ""
)}`
() => {
const context = latestSyncContext;
if (!context) {
return;
}

openSyncedActionsModal({
locale: context.settings.locale,
themeMode: context.settings.themeMode,
title: context.job.title,
onOpenRepository: () => {
window.open(context.repositoryUrl, "_blank", "noopener,noreferrer");
},
onSaveNote: async (note: string) => {
const payload: ProblemNoteRequest = {
platform: context.job.platform,
problemId: context.job.problemId,
title: context.job.title,
directory: context.job.directory,
note,
};

await appendProblemNoteThroughBackground(payload);
},
});
},
"Open synced actions"
);
} catch {
latestSyncContext = null;
setInlineStatus("Sync failed", "error");
}
};
Expand Down
Loading