Skip to content

Commit

Permalink
feat: 重写 URL Scheme 转换工具
Browse files Browse the repository at this point in the history
  • Loading branch information
FHU-yezi committed Jan 26, 2024
1 parent 4d7660d commit 8be65cb
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 94 deletions.
4 changes: 2 additions & 2 deletions frontend/src/pages/LPRecommendChecker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function handleCheck() {
});
}

function ResultBlock() {
function Result() {
const shouldFPRewardHighlight = result.value!.FPReward >= 35;
const shouldnextCanRecommendDateHighlight = result.value!.nextCanRecommendDate
? parseTime(result.value!.nextCanRecommendDate) >= dayjs()
Expand Down Expand Up @@ -109,7 +109,7 @@ export default function LPRecommendChecker() {
检测
</SolidButton>

{result.value && <ResultBlock />}
{result.value && <Result />}
</Column>
);
}
175 changes: 83 additions & 92 deletions frontend/src/pages/URLSchemeConvertor.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useClipboard } from "@mantine/hooks";
import type { Signal } from "@preact/signals";
import { signal } from "@preact/signals";
import {
Center,
Column,
Row,
SmallText,
SolidButton,
Text,
TextButton,
Expand All @@ -14,132 +14,123 @@ import { MdContentCopy, MdDone, MdOutlineArrowForward } from "react-icons/md";
import QRCode from "react-qr-code";
import { toastWarning } from "../utils/toastHelper";

interface JianshuURLType {
URLSchemePrefix: string;
URLPrefix: string;
slugLengthRange: { min: number; max: number };
interface UrlMappingItem {
name: string;
from: RegExp;
to: string;
}

const URLTypes = {
USER: {
URLSchemePrefix: "u",
URLPrefix: "u",
slugLengthRange: { min: 6, max: 12 },
const UrlMappingArray: Array<UrlMappingItem> = [
{
name: "user",
from: /^https:\/\/www\.jianshu\.com\/u\/([a-z0-9]{6,12})\/?$/,
to: "jianshu://u/$1",
},
ARTICLE: {
URLSchemePrefix: "notes",
URLPrefix: "p",
slugLengthRange: { min: 12, max: 12 },
{
name: "article",
from: /^https:\/\/www\.jianshu\.com\/p\/([a-z0-9]{12})\/?$/,
to: "jianshu://p/$1",
},
COLLECTION: {
URLSchemePrefix: "c",
URLPrefix: "c",
slugLengthRange: { min: 6, max: 12 },
{
name: "notebook",
from: /^https:\/\/www\.jianshu\.com\/nb\/(\d{7,8})\/?$/,
to: "jianshu://nb/$1",
},
NOTEBOOK: {
URLSchemePrefix: "nb",
URLPrefix: "nb",
slugLengthRange: { min: 7, max: 8 },
{
name: "collection",
from: /^https:\/\/www\.jianshu\.com\/c\/([a-z0-9]{6,12})\/?$/,
to: "jianshu://c/$1",
},
};
const URLTypesArray = [
URLTypes.USER,
URLTypes.ARTICLE,
URLTypes.COLLECTION,
URLTypes.NOTEBOOK,
];

const jianshuURL = signal("");
const hasResult = signal(false);
const result = signal<string | undefined>(undefined);
const inputUrl = signal("");
const urlScheme = signal<string | null>(null);

function isJianshuURL(url: Signal<string>) {
return url.value.startsWith("https://www.jianshu.com/");
function isJianshuUrl(url: string) {
return url.startsWith("https://www.jianshu.com/");
}

function getURLType(url: Signal<string>): JianshuURLType | "unknown" {
if (!isJianshuURL(url)) {
return "unknown";
function handleConvert() {
if (!isJianshuUrl(inputUrl.value)) {
toastWarning({ message: "请输入有效的简书链接" });
return;
}

let splitted;
let perfix;
let slug;
try {
splitted = url.value.split("/");
[, , , perfix, slug] = splitted;
} catch {
return "unknown";
}
for (const URLType of URLTypesArray) {
if (perfix === URLType.URLPrefix) {
if (
URLType.slugLengthRange.min <= slug.length &&
slug.length <= URLType.slugLengthRange.max
) {
return URLType;
}
return "unknown";
for (const urlMapping of UrlMappingArray) {
const replaceResult = inputUrl.value.replace(
urlMapping.from,
urlMapping.to,
);

// 匹配成功
if (replaceResult.startsWith("jianshu://")) {
urlScheme.value = replaceResult;
return;
}
}
return "unknown";

// 无匹配项
toastWarning({ message: "请输入有效的简书链接" });
}

function handleConvert() {
const urlType = getURLType(jianshuURL);
function Result() {
const clipboard = useClipboard();

if (urlType === "unknown") {
toastWarning({ message: "请输入有效的简书链接" });
return;
if (!urlScheme.value) {
return null;
}

hasResult.value = true;
result.value = jianshuURL.value.replace(
`https://www.jianshu.com/${urlType.URLPrefix}/`,
`jianshu://${urlType.URLSchemePrefix}/`,
return (
<Center>
<Column className="mt-12" gap="gap-2" itemsCenter>
<Text>{urlScheme.value}</Text>
<Row itemsCenter>
<TextButton
rightIcon={<MdOutlineArrowForward />}
onClick={() => window.open(urlScheme.value!)}
>
访问
</TextButton>
<TextButton
colorScheme={clipboard.copied ? "success" : undefined}
rightIcon={clipboard.copied ? <MdDone /> : <MdContentCopy />}
onClick={() => clipboard.copy(urlScheme.value)}
>
{clipboard.copied ? "已复制" : "复制"}
</TextButton>
</Row>

<Center className="rounded p-2 dark:bg-zinc-50">
<QRCode value={urlScheme.value} />
</Center>
<SmallText className="text-center" colorScheme="gray">
请使用简书 App 扫描二维码
</SmallText>
</Column>
</Center>
);
}

export default function URLSchemeConvertor() {
const clipboard = useClipboard();

return (
<Column>
<TextInput
id="jianshu-url"
label="简书链接"
value={jianshuURL}
value={inputUrl}
onEnter={handleConvert}
errorMessage={
inputUrl.value && !isJianshuUrl(inputUrl.value)
? "链接无效"
: undefined
}
selectAllOnFocus
/>
<SolidButton onClick={handleConvert} fullWidth>
转换
</SolidButton>

{result.value !== undefined && (
<Center>
<Column className="mt-12" gap="gap-4">
<Column gap="gap-2" itemsCenter>
<Text>{result.value}</Text>
<Row itemsCenter>
<TextButton
rightIcon={<MdOutlineArrowForward />}
onClick={() => window.open(result.value)}
>
访问
</TextButton>
<TextButton
className={!clipboard.copied ? undefined : "bg-green-100"}
rightIcon={!clipboard.copied ? <MdContentCopy /> : <MdDone />}
onClick={() => clipboard.copy(result.value)}
>
{!clipboard.copied ? "复制" : "复制成功"}
</TextButton>
</Row>
</Column>
<QRCode className="w-full" value={result.value} />
</Column>
</Center>
)}
{urlScheme.value && <Result />}
</Column>
);
}

0 comments on commit 8be65cb

Please sign in to comment.