chore: use shortcut v2 api

This commit is contained in:
Steven
2023-11-21 23:33:34 +08:00
parent c449669793
commit 61b167ef66
32 changed files with 515 additions and 724 deletions

View File

@ -5,7 +5,7 @@ import * as api from "../helpers/api";
import Icon from "./Icon";
interface Props {
shortcutId: ShortcutId;
shortcutId: number;
className?: string;
}

View File

@ -7,10 +7,11 @@ import { Link } from "react-router-dom";
import { absolutifyLink } from "@/helpers/utils";
import useNavigateTo from "@/hooks/useNavigateTo";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import { useAppSelector } from "@/stores";
import useCollectionStore from "@/stores/v1/collection";
import useShortcutStore from "@/stores/v1/shortcut";
import useUserStore from "@/stores/v1/user";
import { Collection } from "@/types/proto/api/v2/collection_service";
import { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { showCommonDialog } from "./Alert";
import CreateCollectionDialog from "./CreateCollectionDrawer";
import Icon from "./Icon";
@ -28,7 +29,7 @@ const CollectionView = (props: Props) => {
const navigateTo = useNavigateTo();
const currentUser = useUserStore().getCurrentUser();
const collectionStore = useCollectionStore();
const { shortcutList } = useAppSelector((state) => state.shortcut);
const shortcutList = useShortcutStore().getShortcutList();
const [showEditDialog, setShowEditDialog] = useState<boolean>(false);
const shortcuts = collection.shortcutIds
.map((shortcutId) => shortcutList.find((shortcut) => shortcut?.id === shortcutId))

View File

@ -3,10 +3,11 @@ import { isUndefined } from "lodash-es";
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { useAppSelector } from "@/stores";
import useCollectionStore from "@/stores/v1/collection";
import useShortcutStore from "@/stores/v1/shortcut";
import { Collection } from "@/types/proto/api/v2/collection_service";
import { Visibility } from "@/types/proto/api/v2/common";
import { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { convertVisibilityFromPb } from "@/utils/visibility";
import useLoading from "../hooks/useLoading";
import Icon from "./Icon";
@ -27,7 +28,7 @@ const CreateCollectionDrawer: React.FC<Props> = (props: Props) => {
const { onClose, onConfirm, collectionId } = props;
const { t } = useTranslation();
const collectionStore = useCollectionStore();
const { shortcutList } = useAppSelector((state) => state.shortcut);
const shortcutList = useShortcutStore().getShortcutList();
const [state, setState] = useState<State>({
collectionCreate: Collection.fromPartial({
visibility: Visibility.PRIVATE,
@ -40,9 +41,9 @@ const CreateCollectionDrawer: React.FC<Props> = (props: Props) => {
const unselectedShortcuts = shortcutList
.filter((shortcut) => {
if (state.collectionCreate.visibility === Visibility.PUBLIC) {
return shortcut.visibility === "PUBLIC";
return shortcut.visibility === Visibility.PUBLIC;
} else if (state.collectionCreate.visibility === Visibility.WORKSPACE) {
return shortcut.visibility === "PUBLIC" || shortcut.visibility === "WORKSPACE";
return shortcut.visibility === Visibility.PUBLIC || shortcut.visibility === Visibility.WORKSPACE;
} else {
return true;
}

View File

@ -16,46 +16,42 @@ import { isUndefined, uniq } from "lodash-es";
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { useAppSelector } from "@/stores";
import useShortcutStore from "@/stores/v1/shortcut";
import { Visibility } from "@/types/proto/api/v2/common";
import { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { convertVisibilityFromPb } from "@/utils/visibility";
import useLoading from "../hooks/useLoading";
import { shortcutService } from "../services";
import Icon from "./Icon";
import ResourceNameInput from "./ResourceNameInput";
interface Props {
shortcutId?: ShortcutId;
shortcutId?: number;
initialShortcut?: Partial<Shortcut>;
onClose: () => void;
onConfirm?: () => void;
}
interface State {
shortcutCreate: ShortcutCreate;
shortcutCreate: Shortcut;
}
const visibilities: Visibility[] = ["PRIVATE", "WORKSPACE", "PUBLIC"];
const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
const { onClose, onConfirm, shortcutId, initialShortcut } = props;
const { t } = useTranslation();
const { shortcutList } = useAppSelector((state) => state.shortcut);
const [state, setState] = useState<State>({
shortcutCreate: {
name: "",
link: "",
title: "",
description: "",
visibility: "PRIVATE",
tags: [],
openGraphMetadata: {
shortcutCreate: Shortcut.fromPartial({
visibility: Visibility.PRIVATE,
ogMetadata: {
title: "",
description: "",
image: "",
},
...initialShortcut,
},
}),
});
const shortcutStore = useShortcutStore();
const [showOpenGraphMetadata, setShowOpenGraphMetadata] = useState<boolean>(false);
const shortcutList = shortcutStore.getShortcutList();
const [tag, setTag] = useState<string>("");
const tagSuggestions = uniq(shortcutList.map((shortcut) => shortcut.tags).flat());
const isCreating = isUndefined(shortcutId);
@ -64,7 +60,7 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
useEffect(() => {
if (shortcutId) {
const shortcut = shortcutService.getShortcutById(shortcutId);
const shortcut = shortcutStore.getShortcutById(shortcutId);
if (shortcut) {
setState({
...state,
@ -74,7 +70,7 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
title: shortcut.title,
description: shortcut.description,
visibility: shortcut.visibility,
openGraphMetadata: shortcut.openGraphMetadata,
ogMetadata: shortcut.ogMetadata,
}),
});
setTag(shortcut.tags.join(" "));
@ -121,7 +117,7 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
const handleVisibilityInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setPartialState({
shortcutCreate: Object.assign(state.shortcutCreate, {
visibility: e.target.value,
visibility: Number(e.target.value),
}),
});
};
@ -142,8 +138,8 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
const handleOpenGraphMetadataImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setPartialState({
shortcutCreate: Object.assign(state.shortcutCreate, {
openGraphMetadata: {
...state.shortcutCreate.openGraphMetadata,
ogMetadata: {
...state.shortcutCreate.ogMetadata,
image: e.target.value,
},
}),
@ -153,8 +149,8 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
const handleOpenGraphMetadataTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setPartialState({
shortcutCreate: Object.assign(state.shortcutCreate, {
openGraphMetadata: {
...state.shortcutCreate.openGraphMetadata,
ogMetadata: {
...state.shortcutCreate.ogMetadata,
title: e.target.value,
},
}),
@ -164,8 +160,8 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
const handleOpenGraphMetadataDescriptionChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setPartialState({
shortcutCreate: Object.assign(state.shortcutCreate, {
openGraphMetadata: {
...state.shortcutCreate.openGraphMetadata,
ogMetadata: {
...state.shortcutCreate.ogMetadata,
description: e.target.value,
},
}),
@ -188,18 +184,13 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
try {
if (shortcutId) {
await shortcutService.patchShortcut({
await shortcutStore.updateShortcut({
...state.shortcutCreate,
id: shortcutId,
name: state.shortcutCreate.name,
link: state.shortcutCreate.link,
title: state.shortcutCreate.title,
description: state.shortcutCreate.description,
visibility: state.shortcutCreate.visibility,
tags: tag.split(" ").filter(Boolean),
openGraphMetadata: state.shortcutCreate.openGraphMetadata,
});
} else {
await shortcutService.createShortcut({
await shortcutStore.createShortcut({
...state.shortcutCreate,
tags: tag.split(" ").filter(Boolean),
});
@ -281,13 +272,13 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
<span className="mb-2">Visibility</span>
<div className="w-full flex flex-row justify-start items-center text-base">
<RadioGroup orientation="horizontal" value={state.shortcutCreate.visibility} onChange={handleVisibilityInputChange}>
{visibilities.map((visibility) => (
<Radio key={visibility} value={visibility} label={t(`shortcut.visibility.${visibility.toLowerCase()}.self`)} />
))}
<Radio value={Visibility.PRIVATE} label={t(`shortcut.visibility.private.self`)} />
<Radio value={Visibility.WORKSPACE} label={t(`shortcut.visibility.workspace.self`)} />
<Radio value={Visibility.PUBLIC} label={t(`shortcut.visibility.public.self`)} />
</RadioGroup>
</div>
<p className="mt-3 text-sm text-gray-500 w-full bg-gray-100 border border-gray-200 dark:bg-zinc-800 dark:border-zinc-700 dark:text-gray-400 px-2 py-1 rounded-md">
{t(`shortcut.visibility.${state.shortcutCreate.visibility.toLowerCase()}.description`)}
{t(`shortcut.visibility.${convertVisibilityFromPb(state.shortcutCreate.visibility).toLowerCase()}.description`)}
</p>
</div>
<Divider className="text-gray-500">More</Divider>
@ -316,7 +307,7 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
type="text"
placeholder="https://the.link.to/the/image.png"
size="sm"
value={state.shortcutCreate.openGraphMetadata.image}
value={state.shortcutCreate.ogMetadata?.image}
onChange={handleOpenGraphMetadataImageChange}
/>
</div>
@ -327,7 +318,7 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
type="text"
placeholder="Slash - An open source, self-hosted bookmarks and link sharing platform"
size="sm"
value={state.shortcutCreate.openGraphMetadata.title}
value={state.shortcutCreate.ogMetadata?.title}
onChange={handleOpenGraphMetadataTitleChange}
/>
</div>
@ -338,7 +329,7 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
placeholder="An open source, self-hosted bookmarks and link sharing platform."
size="sm"
maxRows={3}
value={state.shortcutCreate.openGraphMetadata.description}
value={state.shortcutCreate.ogMetadata?.description}
onChange={handleOpenGraphMetadataDescriptionChange}
/>
</div>

View File

@ -1,4 +1,5 @@
import { useTranslation } from "react-i18next";
import { convertVisibilityFromPb } from "@/utils/visibility";
import useViewStore from "../stores/v1/view";
import Icon from "./Icon";
import VisibilityIcon from "./VisibilityIcon";
@ -32,7 +33,7 @@ const FilterView = () => {
onClick={() => viewStore.setFilter({ visibility: undefined })}
>
<VisibilityIcon className="w-4 h-auto mr-1" visibility={filter.visibility} />
{t(`shortcut.visibility.${filter.visibility.toLowerCase()}.self`)}
{t(`shortcut.visibility.${convertVisibilityFromPb(filter.visibility).toLowerCase()}.self`)}
<Icon.X className="w-4 h-auto ml-1" />
</button>
)}

View File

@ -3,6 +3,7 @@ import { QRCodeCanvas } from "qrcode.react";
import { useRef } from "react";
import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { absolutifyLink } from "../helpers/utils";
import Icon from "./Icon";

View File

@ -1,8 +1,9 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import useNavigateTo from "@/hooks/useNavigateTo";
import useShortcutStore from "@/stores/v1/shortcut";
import { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { Role } from "@/types/proto/api/v2/user_service";
import { shortcutService } from "../services";
import useUserStore from "../stores/v1/user";
import { showCommonDialog } from "./Alert";
import CreateShortcutDrawer from "./CreateShortcutDrawer";
@ -18,6 +19,7 @@ const ShortcutActionsDropdown = (props: Props) => {
const { shortcut } = props;
const { t } = useTranslation();
const navigateTo = useNavigateTo();
const shortcutStore = useShortcutStore();
const currentUser = useUserStore().getCurrentUser();
const [showEditDrawer, setShowEditDrawer] = useState<boolean>(false);
const [showQRCodeDialog, setShowQRCodeDialog] = useState<boolean>(false);
@ -29,7 +31,7 @@ const ShortcutActionsDropdown = (props: Props) => {
content: `Are you sure to delete shortcut \`${shortcut.name}\`? You cannot undo this action.`,
style: "danger",
onConfirm: async () => {
await shortcutService.deleteShortcutById(shortcut.id);
await shortcutStore.deleteShortcut(shortcut.id);
},
});
};

View File

@ -4,6 +4,8 @@ import copy from "copy-to-clipboard";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { convertVisibilityFromPb } from "@/utils/visibility";
import { absolutifyLink, getFaviconWithGoogleS2 } from "../helpers/utils";
import useViewStore from "../stores/v1/view";
import Icon from "./Icon";
@ -102,13 +104,18 @@ const ShortcutCard = (props: Props) => {
{shortcut.tags.length === 0 && <span className="text-gray-400 text-sm leading-4 italic">No tags</span>}
</div>
<div className="w-full flex mt-2 gap-2 overflow-x-auto">
<Tooltip title={t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.description`)} variant="solid" placement="top" arrow>
<Tooltip
title={t(`shortcut.visibility.${convertVisibilityFromPb(shortcut.visibility).toLowerCase()}.description`)}
variant="solid"
placement="top"
arrow
>
<div
className="w-auto px-2 leading-6 flex flex-row justify-start items-center flex-nowrap whitespace-nowrap border rounded-full cursor-pointer text-gray-500 dark:text-gray-400 text-sm dark:border-zinc-700"
onClick={() => viewStore.setFilter({ visibility: shortcut.visibility })}
>
<VisibilityIcon className="w-4 h-auto mr-1 opacity-60" visibility={shortcut.visibility} />
{t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.self`)}
{t(`shortcut.visibility.${convertVisibilityFromPb(shortcut.visibility).toLowerCase()}.self`)}
</div>
</Tooltip>
<Tooltip title="View count" variant="solid" placement="top" arrow>
@ -117,7 +124,7 @@ const ShortcutCard = (props: Props) => {
className="w-auto px-2 leading-6 flex flex-row justify-start items-center flex-nowrap whitespace-nowrap border rounded-full cursor-pointer text-gray-500 dark:text-gray-400 text-sm dark:border-zinc-700"
>
<Icon.BarChart2 className="w-4 h-auto mr-1 opacity-80" />
{t("shortcut.visits", { count: shortcut.view })}
{t("shortcut.visits", { count: shortcut.viewCount })}
</Link>
</Tooltip>
</div>

View File

@ -1,5 +1,6 @@
import classNames from "classnames";
import { Link } from "react-router-dom";
import { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { getFaviconWithGoogleS2 } from "../helpers/utils";
import Icon from "./Icon";
import ShortcutActionsDropdown from "./ShortcutActionsDropdown";

View File

@ -1,5 +1,6 @@
import classNames from "classnames";
import useNavigateTo from "@/hooks/useNavigateTo";
import { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import useViewStore from "../stores/v1/view";
import ShortcutCard from "./ShortcutCard";
import ShortcutView from "./ShortcutView";

View File

@ -1,13 +1,13 @@
import classNames from "classnames";
import { useTranslation } from "react-i18next";
import { useAppSelector } from "../stores";
import useShortcutStore from "@/stores/v1/shortcut";
import useViewStore from "../stores/v1/view";
import Icon from "./Icon";
const ShortcutsNavigator = () => {
const { t } = useTranslation();
const viewStore = useViewStore();
const { shortcutList } = useAppSelector((state) => state.shortcut);
const shortcutList = useShortcutStore().getShortcutList();
const tags = shortcutList.map((shortcut) => shortcut.tags).flat();
const currentTab = viewStore.filter.tab || `tab:all`;
const sortedTagMap = sortTags(tags);

View File

@ -1,3 +1,4 @@
import { Visibility } from "@/types/proto/api/v2/common";
import Icon from "./Icon";
interface Props {
@ -7,11 +8,11 @@ interface Props {
const VisibilityIcon = (props: Props) => {
const { visibility, className } = props;
if (visibility === "PRIVATE") {
if (visibility === Visibility.PRIVATE) {
return <Icon.Lock className={className || ""} />;
} else if (visibility === "WORKSPACE") {
} else if (visibility === Visibility.WORKSPACE) {
return <Icon.Building2 className={className || ""} />;
} else if (visibility === "PUBLIC") {
} else if (visibility === Visibility.PUBLIC) {
return <Icon.Globe2 className={className || ""} />;
}
return null;