mirror of
				https://github.com/usememos/memos.git
				synced 2025-06-05 22:09:59 +02:00 
			
		
		
		
	chore: use axios instead of fetch
				
					
				
			This commit is contained in:
		| @@ -23,7 +23,6 @@ | |||||||
|     ], |     ], | ||||||
|     "@typescript-eslint/no-empty-interface": ["off"], |     "@typescript-eslint/no-empty-interface": ["off"], | ||||||
|     "@typescript-eslint/no-explicit-any": ["off"], |     "@typescript-eslint/no-explicit-any": ["off"], | ||||||
|     "react/react-in-jsx-scope": "off", |     "react/react-in-jsx-scope": "off" | ||||||
|     "@typescript-eslint/no-namespace": "off" |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "memos", |   "name": "memos", | ||||||
|   "version": "2.0.6", |   "version": "0.0.1", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "dev": "vite", |     "dev": "vite", | ||||||
|     "build": "tsc && vite build", |     "build": "tsc && vite build", | ||||||
| @@ -9,6 +9,7 @@ | |||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@reduxjs/toolkit": "^1.8.1", |     "@reduxjs/toolkit": "^1.8.1", | ||||||
|  |     "axios": "^0.27.2", | ||||||
|     "lodash-es": "^4.17.21", |     "lodash-es": "^4.17.21", | ||||||
|     "react": "^18.1.0", |     "react": "^18.1.0", | ||||||
|     "react-dom": "^18.1.0", |     "react-dom": "^18.1.0", | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { useEffect, useState } from "react"; | import { useEffect, useState } from "react"; | ||||||
| import api from "../helpers/api"; | import * as api from "../helpers/api"; | ||||||
| import Only from "./common/OnlyWhen"; | import Only from "./common/OnlyWhen"; | ||||||
| import { showDialog } from "./Dialog"; | import { showDialog } from "./Dialog"; | ||||||
| import "../less/about-site-dialog.less"; | import "../less/about-site-dialog.less"; | ||||||
| @@ -11,7 +11,10 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => { | |||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     try { |     try { | ||||||
|       api.getSystemStatus().then(({ profile }) => { |       api.getSystemStatus().then(({ data }) => { | ||||||
|  |         const { | ||||||
|  |           data: { profile }, | ||||||
|  |         } = data; | ||||||
|         setProfile(profile); |         setProfile(profile); | ||||||
|       }); |       }); | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { IMAGE_URL_REG } from "../helpers/consts"; | import { IMAGE_URL_REG } from "../helpers/consts"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import { formatMemoContent } from "./Memo"; | import { formatMemoContent } from "./Memo"; | ||||||
| import Only from "./common/OnlyWhen"; | import Only from "./common/OnlyWhen"; | ||||||
| import "../less/daily-memo.less"; | import "../less/daily-memo.less"; | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ import toImage from "../labs/html2image"; | |||||||
| import useToggle from "../hooks/useToggle"; | import useToggle from "../hooks/useToggle"; | ||||||
| import useLoading from "../hooks/useLoading"; | import useLoading from "../hooks/useLoading"; | ||||||
| import { DAILY_TIMESTAMP } from "../helpers/consts"; | import { DAILY_TIMESTAMP } from "../helpers/consts"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import { showDialog } from "./Dialog"; | import { showDialog } from "./Dialog"; | ||||||
| import showPreviewImageDialog from "./PreviewImageDialog"; | import showPreviewImageDialog from "./PreviewImageDialog"; | ||||||
| import DailyMemo from "./DailyMemo"; | import DailyMemo from "./DailyMemo"; | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { IMAGE_URL_REG } from "../helpers/consts"; | import { IMAGE_URL_REG } from "../helpers/consts"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import useToggle from "../hooks/useToggle"; | import useToggle from "../hooks/useToggle"; | ||||||
| import { memoService } from "../services"; | import { memoService } from "../services"; | ||||||
| import Only from "./common/OnlyWhen"; | import Only from "./common/OnlyWhen"; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import { memo } from "react"; | |||||||
| import { escape } from "lodash-es"; | import { escape } from "lodash-es"; | ||||||
| import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG, UNKNOWN_ID } from "../helpers/consts"; | import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG, UNKNOWN_ID } from "../helpers/consts"; | ||||||
| import { parseMarkedToHtml, parseRawTextToHtml } from "../helpers/marked"; | import { parseMarkedToHtml, parseRawTextToHtml } from "../helpers/marked"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import useToggle from "../hooks/useToggle"; | import useToggle from "../hooks/useToggle"; | ||||||
| import { editorStateService, memoService } from "../services"; | import { editorStateService, memoService } from "../services"; | ||||||
| import Only from "./common/OnlyWhen"; | import Only from "./common/OnlyWhen"; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { useState, useEffect, useCallback } from "react"; | import { useState, useEffect, useCallback } from "react"; | ||||||
| import { IMAGE_URL_REG, MEMO_LINK_REG, UNKNOWN_ID } from "../helpers/consts"; | import { IMAGE_URL_REG, MEMO_LINK_REG, UNKNOWN_ID } from "../helpers/consts"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import { editorStateService, memoService } from "../services"; | import { editorStateService, memoService } from "../services"; | ||||||
| import { parseHtmlToRawText } from "../helpers/marked"; | import { parseHtmlToRawText } from "../helpers/marked"; | ||||||
| import { formatMemoContent } from "./Memo"; | import { formatMemoContent } from "./Memo"; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useRef } from "react"; | |||||||
| import { editorStateService, locationService, memoService, resourceService } from "../services"; | import { editorStateService, locationService, memoService, resourceService } from "../services"; | ||||||
| import { useAppSelector } from "../store"; | import { useAppSelector } from "../store"; | ||||||
| import { UNKNOWN_ID } from "../helpers/consts"; | import { UNKNOWN_ID } from "../helpers/consts"; | ||||||
| import { storage } from "../helpers/storage"; | import * as storage from "../helpers/storage"; | ||||||
| import useToggle from "../hooks/useToggle"; | import useToggle from "../hooks/useToggle"; | ||||||
| import toastHelper from "./Toast"; | import toastHelper from "./Toast"; | ||||||
| import Editor, { EditorRefActions } from "./Editor/Editor"; | import Editor, { EditorRefActions } from "./Editor/Editor"; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { useAppSelector } from "../store"; | import { useAppSelector } from "../store"; | ||||||
| import { locationService, shortcutService } from "../services"; | import { locationService, shortcutService } from "../services"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import { getTextWithMemoType } from "../helpers/filter"; | import { getTextWithMemoType } from "../helpers/filter"; | ||||||
| import "../less/memo-filter.less"; | import "../less/memo-filter.less"; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; | |||||||
| import { locationService, memoService, shortcutService } from "../services"; | import { locationService, memoService, shortcutService } from "../services"; | ||||||
| import { useAppSelector } from "../store"; | import { useAppSelector } from "../store"; | ||||||
| import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG } from "../helpers/consts"; | import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG } from "../helpers/consts"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import { checkShouldShowMemoWithFilters } from "../helpers/filter"; | import { checkShouldShowMemoWithFilters } from "../helpers/filter"; | ||||||
| import Memo from "./Memo"; | import Memo from "./Memo"; | ||||||
| import toastHelper from "./Toast"; | import toastHelper from "./Toast"; | ||||||
| @@ -79,6 +79,7 @@ const MemoList: React.FC<Props> = () => { | |||||||
|   const pinnedMemos = shownMemos.filter((m) => m.pinned); |   const pinnedMemos = shownMemos.filter((m) => m.pinned); | ||||||
|   const unpinnedMemos = shownMemos.filter((m) => !m.pinned); |   const unpinnedMemos = shownMemos.filter((m) => !m.pinned); | ||||||
|   const sortedMemos = pinnedMemos.concat(unpinnedMemos).filter((m) => m.rowStatus === "NORMAL"); |   const sortedMemos = pinnedMemos.concat(unpinnedMemos).filter((m) => m.rowStatus === "NORMAL"); | ||||||
|  |   console.log(memos.length, sortedMemos.length); | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     memoService |     memoService | ||||||
|   | |||||||
| @@ -14,7 +14,6 @@ const MemoTrashDialog: React.FC<Props> = (props: Props) => { | |||||||
|   const [deletedMemos, setDeletedMemos] = useState<Memo[]>([]); |   const [deletedMemos, setDeletedMemos] = useState<Memo[]>([]); | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     memoService.fetchAllMemos(); |  | ||||||
|     memoService |     memoService | ||||||
|       .fetchDeletedMemos() |       .fetchDeletedMemos() | ||||||
|       .then((result) => { |       .then((result) => { | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { useEffect, useRef, useState } from "react"; | import { useEffect, useRef, useState } from "react"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import { showDialog } from "./Dialog"; | import { showDialog } from "./Dialog"; | ||||||
| import "../less/preview-image-dialog.less"; | import "../less/preview-image-dialog.less"; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import React, { useEffect, useState } from "react"; | import React, { useEffect, useState } from "react"; | ||||||
| import { isEmpty } from "lodash-es"; | import { isEmpty } from "lodash-es"; | ||||||
| import api from "../../helpers/api"; | import * as api from "../../helpers/api"; | ||||||
| import toastHelper from "../Toast"; | import toastHelper from "../Toast"; | ||||||
| import "../../less/settings/member-section.less"; | import "../../less/settings/member-section.less"; | ||||||
|  |  | ||||||
| @@ -23,7 +23,7 @@ const PreferencesSection: React.FC<Props> = () => { | |||||||
|   }, []); |   }, []); | ||||||
|  |  | ||||||
|   const fetchUserList = async () => { |   const fetchUserList = async () => { | ||||||
|     const data = await api.getUserList(); |     const { data } = (await api.getUserList()).data; | ||||||
|     setUserList(data); |     setUserList(data); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { memoService } from "../../services"; | import { memoService } from "../../services"; | ||||||
| import utils from "../../helpers/utils"; | import * as utils from "../../helpers/utils"; | ||||||
| import toastHelper from "../Toast"; | import toastHelper from "../Toast"; | ||||||
| import "../../less/settings/preferences-section.less"; | import "../../less/settings/preferences-section.less"; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react"; | |||||||
| import { userService } from "../services"; | import { userService } from "../services"; | ||||||
| import toImage from "../labs/html2image"; | import toImage from "../labs/html2image"; | ||||||
| import { ANIMATION_DURATION, IMAGE_URL_REG } from "../helpers/consts"; | import { ANIMATION_DURATION, IMAGE_URL_REG } from "../helpers/consts"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import { showDialog } from "./Dialog"; | import { showDialog } from "./Dialog"; | ||||||
| import { formatMemoContent } from "./Memo"; | import { formatMemoContent } from "./Memo"; | ||||||
| import Only from "./common/OnlyWhen"; | import Only from "./common/OnlyWhen"; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import { useEffect } from "react"; | |||||||
| import { locationService, shortcutService } from "../services"; | import { locationService, shortcutService } from "../services"; | ||||||
| import { useAppSelector } from "../store"; | import { useAppSelector } from "../store"; | ||||||
| import { UNKNOWN_ID } from "../helpers/consts"; | import { UNKNOWN_ID } from "../helpers/consts"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import useToggle from "../hooks/useToggle"; | import useToggle from "../hooks/useToggle"; | ||||||
| import useLoading from "../hooks/useLoading"; | import useLoading from "../hooks/useLoading"; | ||||||
| import toastHelper from "./Toast"; | import toastHelper from "./Toast"; | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { useAppSelector } from "../store"; | import { useAppSelector } from "../store"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import showDailyMemoDiaryDialog from "./DailyMemoDiaryDialog"; | import showDailyMemoDiaryDialog from "./DailyMemoDiaryDialog"; | ||||||
| import showSettingDialog from "./SettingDialog"; | import showSettingDialog from "./SettingDialog"; | ||||||
| import showMemoTrashDialog from "./MemoTrashDialog"; | import showMemoTrashDialog from "./MemoTrashDialog"; | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ import { useAppSelector } from "../store"; | |||||||
| import { locationService, memoService } from "../services"; | import { locationService, memoService } from "../services"; | ||||||
| import useToggle from "../hooks/useToggle"; | import useToggle from "../hooks/useToggle"; | ||||||
| import Only from "./common/OnlyWhen"; | import Only from "./common/OnlyWhen"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import "../less/tag-list.less"; | import "../less/tag-list.less"; | ||||||
|  |  | ||||||
| interface Tag { | interface Tag { | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; | |||||||
| import { useAppSelector } from "../store"; | import { useAppSelector } from "../store"; | ||||||
| import { locationService } from "../services"; | import { locationService } from "../services"; | ||||||
| import { DAILY_TIMESTAMP } from "../helpers/consts"; | import { DAILY_TIMESTAMP } from "../helpers/consts"; | ||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import "../less/usage-heat-map.less"; | import "../less/usage-heat-map.less"; | ||||||
|  |  | ||||||
| const tableConfig = { | const tableConfig = { | ||||||
|   | |||||||
| @@ -1,209 +1,99 @@ | |||||||
|  | import axios from "axios"; | ||||||
|  |  | ||||||
| type ResponseObject<T> = { | type ResponseObject<T> = { | ||||||
|   data: T; |   data: T; | ||||||
|   error?: string; |   error?: string; | ||||||
|   message?: string; |   message?: string; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| type RequestConfig = { | export function getSystemStatus() { | ||||||
|   method: string; |   return axios.get<ResponseObject<SystemStatus>>("/api/status"); | ||||||
|   url: string; |  | ||||||
|   data?: any; |  | ||||||
|   dataType?: "json" | "file"; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| async function request<T>(config: RequestConfig): Promise<T> { |  | ||||||
|   const { method, url, data, dataType } = config; |  | ||||||
|   const requestConfig: RequestInit = { |  | ||||||
|     method, |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   if (data !== undefined) { |  | ||||||
|     if (dataType === "file") { |  | ||||||
|       requestConfig.body = data; |  | ||||||
|     } else { |  | ||||||
|       requestConfig.headers = { |  | ||||||
|         "Content-Type": "application/json", |  | ||||||
|       }; |  | ||||||
|       requestConfig.body = JSON.stringify(data); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   const response = await fetch(url, requestConfig); |  | ||||||
|   const responseData = (await response.json()) as ResponseObject<T>; |  | ||||||
|  |  | ||||||
|   if (responseData.error || responseData.message) { |  | ||||||
|     throw new Error(responseData.error || responseData.message); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return responseData.data; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| namespace api { | export function login(email: string, password: string) { | ||||||
|   export function getSystemStatus() { |   return axios.post<ResponseObject<User>>("/api/auth/login", { | ||||||
|     return request<SystemStatus>({ |     email, | ||||||
|       method: "GET", |     password, | ||||||
|       url: "/api/status", |   }); | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function login(email: string, password: string) { |  | ||||||
|     return request<User>({ |  | ||||||
|       method: "POST", |  | ||||||
|       url: "/api/auth/login", |  | ||||||
|       data: { |  | ||||||
|         email, |  | ||||||
|         password, |  | ||||||
|       }, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function signup(email: string, password: string, role: UserRole) { |  | ||||||
|     return request<User>({ |  | ||||||
|       method: "POST", |  | ||||||
|       url: "/api/auth/signup", |  | ||||||
|       data: { |  | ||||||
|         email, |  | ||||||
|         password, |  | ||||||
|         role, |  | ||||||
|         name: email, |  | ||||||
|       }, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function signout() { |  | ||||||
|     return request({ |  | ||||||
|       method: "POST", |  | ||||||
|       url: "/api/auth/logout", |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function createUser(userCreate: UserCreate) { |  | ||||||
|     return request<User[]>({ |  | ||||||
|       method: "POST", |  | ||||||
|       url: "/api/user", |  | ||||||
|       data: userCreate, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function getUser() { |  | ||||||
|     return request<User>({ |  | ||||||
|       method: "GET", |  | ||||||
|       url: "/api/user/me", |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function getUserList() { |  | ||||||
|     return request<User[]>({ |  | ||||||
|       method: "GET", |  | ||||||
|       url: "/api/user", |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function patchUser(userPatch: UserPatch) { |  | ||||||
|     return request<User>({ |  | ||||||
|       method: "PATCH", |  | ||||||
|       url: "/api/user/me", |  | ||||||
|       data: userPatch, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function getMyMemos() { |  | ||||||
|     return request<Memo[]>({ |  | ||||||
|       method: "GET", |  | ||||||
|       url: "/api/memo", |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function getMyArchivedMemos() { |  | ||||||
|     return request<Memo[]>({ |  | ||||||
|       method: "GET", |  | ||||||
|       url: "/api/memo?rowStatus=ARCHIVED", |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function createMemo(memoCreate: MemoCreate) { |  | ||||||
|     return request<Memo>({ |  | ||||||
|       method: "POST", |  | ||||||
|       url: "/api/memo", |  | ||||||
|       data: memoCreate, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function patchMemo(memoPatch: MemoPatch) { |  | ||||||
|     return request<Memo>({ |  | ||||||
|       method: "PATCH", |  | ||||||
|       url: `/api/memo/${memoPatch.id}`, |  | ||||||
|       data: { |  | ||||||
|         memoPatch, |  | ||||||
|       }, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function pinMemo(memoId: MemoId) { |  | ||||||
|     return request({ |  | ||||||
|       method: "POST", |  | ||||||
|       url: `/api/memo/${memoId}/organizer`, |  | ||||||
|       data: { |  | ||||||
|         pinned: true, |  | ||||||
|       }, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function unpinMemo(memoId: MemoId) { |  | ||||||
|     return request({ |  | ||||||
|       method: "POST", |  | ||||||
|       url: `/api/memo/${memoId}/organizer`, |  | ||||||
|       data: { |  | ||||||
|         pinned: false, |  | ||||||
|       }, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function deleteMemo(memoId: MemoId) { |  | ||||||
|     return request({ |  | ||||||
|       method: "DELETE", |  | ||||||
|       url: `/api/memo/${memoId}`, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function getMyShortcuts() { |  | ||||||
|     return request<Shortcut[]>({ |  | ||||||
|       method: "GET", |  | ||||||
|       url: "/api/shortcut", |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function createShortcut(shortcutCreate: ShortcutCreate) { |  | ||||||
|     return request<Shortcut>({ |  | ||||||
|       method: "POST", |  | ||||||
|       url: "/api/shortcut", |  | ||||||
|       data: shortcutCreate, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function patchShortcut(shortcutPatch: ShortcutPatch) { |  | ||||||
|     return request<Shortcut>({ |  | ||||||
|       method: "PATCH", |  | ||||||
|       url: `/api/shortcut/${shortcutPatch.id}`, |  | ||||||
|       data: shortcutPatch, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function deleteShortcutById(shortcutId: ShortcutId) { |  | ||||||
|     return request({ |  | ||||||
|       method: "DELETE", |  | ||||||
|       url: `/api/shortcut/${shortcutId}`, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function uploadFile(formData: FormData) { |  | ||||||
|     return request<Resource>({ |  | ||||||
|       method: "POST", |  | ||||||
|       url: "/api/resource", |  | ||||||
|       data: formData, |  | ||||||
|       dataType: "file", |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| export default api; | export function signup(email: string, password: string, role: UserRole) { | ||||||
|  |   return axios.post<ResponseObject<User>>("/api/auth/signup", { | ||||||
|  |     email, | ||||||
|  |     password, | ||||||
|  |     role, | ||||||
|  |     name: email, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function signout() { | ||||||
|  |   return axios.post("/api/auth/logout"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function createUser(userCreate: UserCreate) { | ||||||
|  |   return axios.post<ResponseObject<User>>("/api/user", userCreate); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getUser() { | ||||||
|  |   return axios.get<ResponseObject<User>>("/api/user/me"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getUserList() { | ||||||
|  |   return axios.get<ResponseObject<User[]>>("/api/user"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function patchUser(userPatch: UserPatch) { | ||||||
|  |   return axios.patch<ResponseObject<User>>("/api/user/me", userPatch); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getMyMemos() { | ||||||
|  |   return axios.get<ResponseObject<Memo[]>>("/api/memo"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getMyArchivedMemos() { | ||||||
|  |   return axios.get<ResponseObject<Memo[]>>("/api/memo?rowStatus=ARCHIVED"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function createMemo(memoCreate: MemoCreate) { | ||||||
|  |   return axios.post<ResponseObject<Memo>>("/api/memo", memoCreate); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function patchMemo(memoPatch: MemoPatch) { | ||||||
|  |   return axios.patch<ResponseObject<Memo>>(`/api/memo/${memoPatch.id}`, memoPatch); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function pinMemo(memoId: MemoId) { | ||||||
|  |   return axios.post(`/api/memo/${memoId}/organizer`, { | ||||||
|  |     pinned: true, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function unpinMemo(memoId: MemoId) { | ||||||
|  |   return axios.post(`/api/memo/${memoId}/organizer`, { | ||||||
|  |     pinned: false, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function deleteMemo(memoId: MemoId) { | ||||||
|  |   return axios.delete(`/api/memo/${memoId}`); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getMyShortcuts() { | ||||||
|  |   return axios.get<ResponseObject<Shortcut[]>>("/api/shortcut"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function createShortcut(shortcutCreate: ShortcutCreate) { | ||||||
|  |   return axios.post<ResponseObject<Shortcut>>("/api/shortcut", shortcutCreate); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function patchShortcut(shortcutPatch: ShortcutPatch) { | ||||||
|  |   return axios.patch<ResponseObject<Shortcut>>(`/api/shortcut/${shortcutPatch.id}`, shortcutPatch); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function deleteShortcutById(shortcutId: ShortcutId) { | ||||||
|  |   return axios.delete(`/api/shortcut/${shortcutId}`); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function uploadFile(formData: FormData) { | ||||||
|  |   return axios.post<ResponseObject<Resource>>("/api/resource", formData); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -14,52 +14,50 @@ type StorageKey = keyof StorageData; | |||||||
| /** | /** | ||||||
|  * storage helper |  * storage helper | ||||||
|  */ |  */ | ||||||
| export namespace storage { | export function get(keys: StorageKey[]): Partial<StorageData> { | ||||||
|   export function get(keys: StorageKey[]): Partial<StorageData> { |   const data: Partial<StorageData> = {}; | ||||||
|     const data: Partial<StorageData> = {}; |  | ||||||
|  |  | ||||||
|     for (const key of keys) { |   for (const key of keys) { | ||||||
|       try { |     try { | ||||||
|         const stringifyValue = localStorage.getItem(key); |       const stringifyValue = localStorage.getItem(key); | ||||||
|         if (stringifyValue !== null) { |       if (stringifyValue !== null) { | ||||||
|           const val = JSON.parse(stringifyValue); |         const val = JSON.parse(stringifyValue); | ||||||
|           data[key] = val; |         data[key] = val; | ||||||
|         } |  | ||||||
|       } catch (error: any) { |  | ||||||
|         console.error("Get storage failed in ", key, error); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return data; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function set(data: Partial<StorageData>) { |  | ||||||
|     for (const key in data) { |  | ||||||
|       try { |  | ||||||
|         const stringifyValue = JSON.stringify(data[key as StorageKey]); |  | ||||||
|         localStorage.setItem(key, stringifyValue); |  | ||||||
|       } catch (error: any) { |  | ||||||
|         console.error("Save storage failed in ", key, error); |  | ||||||
|       } |       } | ||||||
|  |     } catch (error: any) { | ||||||
|  |       console.error("Get storage failed in ", key, error); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   export function remove(keys: StorageKey[]) { |   return data; | ||||||
|     for (const key of keys) { | } | ||||||
|       try { |  | ||||||
|         localStorage.removeItem(key); | export function set(data: Partial<StorageData>) { | ||||||
|       } catch (error: any) { |   for (const key in data) { | ||||||
|         console.error("Remove storage failed in ", key, error); |     try { | ||||||
|       } |       const stringifyValue = JSON.stringify(data[key as StorageKey]); | ||||||
|  |       localStorage.setItem(key, stringifyValue); | ||||||
|  |     } catch (error: any) { | ||||||
|  |       console.error("Save storage failed in ", key, error); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | } | ||||||
|   export function emitStorageChangedEvent() { |  | ||||||
|     const iframeEl = document.createElement("iframe"); | export function remove(keys: StorageKey[]) { | ||||||
|     iframeEl.style.display = "none"; |   for (const key of keys) { | ||||||
|     document.body.appendChild(iframeEl); |     try { | ||||||
|  |       localStorage.removeItem(key); | ||||||
|     iframeEl.contentWindow?.localStorage.setItem("t", Date.now().toString()); |     } catch (error: any) { | ||||||
|     iframeEl.remove(); |       console.error("Remove storage failed in ", key, error); | ||||||
|   } |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function emitStorageChangedEvent() { | ||||||
|  |   const iframeEl = document.createElement("iframe"); | ||||||
|  |   iframeEl.style.display = "none"; | ||||||
|  |   document.body.appendChild(iframeEl); | ||||||
|  |  | ||||||
|  |   iframeEl.contentWindow?.localStorage.setItem("t", Date.now().toString()); | ||||||
|  |   iframeEl.remove(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,223 +1,219 @@ | |||||||
| namespace utils { | export function getNowTimeStamp(): number { | ||||||
|   export function getNowTimeStamp(): number { |   return Date.now(); | ||||||
|     return Date.now(); | } | ||||||
|  |  | ||||||
|  | export function getOSVersion(): "Windows" | "MacOS" | "Linux" | "Unknown" { | ||||||
|  |   const appVersion = navigator.userAgent; | ||||||
|  |   let detectedOS: "Windows" | "MacOS" | "Linux" | "Unknown" = "Unknown"; | ||||||
|  |  | ||||||
|  |   if (appVersion.indexOf("Win") != -1) { | ||||||
|  |     detectedOS = "Windows"; | ||||||
|  |   } else if (appVersion.indexOf("Mac") != -1) { | ||||||
|  |     detectedOS = "MacOS"; | ||||||
|  |   } else if (appVersion.indexOf("Linux") != -1) { | ||||||
|  |     detectedOS = "Linux"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   export function getOSVersion(): "Windows" | "MacOS" | "Linux" | "Unknown" { |   return detectedOS; | ||||||
|     const appVersion = navigator.userAgent; | } | ||||||
|     let detectedOS: "Windows" | "MacOS" | "Linux" | "Unknown" = "Unknown"; |  | ||||||
|  |  | ||||||
|     if (appVersion.indexOf("Win") != -1) { | export function getTimeStampByDate(t: Date | number | string): number { | ||||||
|       detectedOS = "Windows"; |   if (typeof t === "string") { | ||||||
|     } else if (appVersion.indexOf("Mac") != -1) { |     t = t.replaceAll("-", "/"); | ||||||
|       detectedOS = "MacOS"; |   } | ||||||
|     } else if (appVersion.indexOf("Linux") != -1) { |   const d = new Date(t); | ||||||
|       detectedOS = "Linux"; |  | ||||||
|  |   return d.getTime(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getDateStampByDate(t: Date | number | string): number { | ||||||
|  |   const d = new Date(getTimeStampByDate(t)); | ||||||
|  |  | ||||||
|  |   return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getDateString(t: Date | number | string): string { | ||||||
|  |   const d = new Date(getTimeStampByDate(t)); | ||||||
|  |  | ||||||
|  |   const year = d.getFullYear(); | ||||||
|  |   const month = d.getMonth() + 1; | ||||||
|  |   const date = d.getDate(); | ||||||
|  |  | ||||||
|  |   return `${year}/${month}/${date}`; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getDataStringWithTs(ts: number): string { | ||||||
|  |   return getDateTimeString(ts * 1000); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getTimeString(t: Date | number | string): string { | ||||||
|  |   const d = new Date(getTimeStampByDate(t)); | ||||||
|  |  | ||||||
|  |   const hours = d.getHours(); | ||||||
|  |   const mins = d.getMinutes(); | ||||||
|  |  | ||||||
|  |   const hoursStr = hours < 10 ? "0" + hours : hours; | ||||||
|  |   const minsStr = mins < 10 ? "0" + mins : mins; | ||||||
|  |  | ||||||
|  |   return `${hoursStr}:${minsStr}`; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // For example: 2021-4-8 17:52:17 | ||||||
|  | export function getDateTimeString(t: Date | number | string): string { | ||||||
|  |   const d = new Date(getTimeStampByDate(t)); | ||||||
|  |  | ||||||
|  |   const year = d.getFullYear(); | ||||||
|  |   const month = d.getMonth() + 1; | ||||||
|  |   const date = d.getDate(); | ||||||
|  |   const hours = d.getHours(); | ||||||
|  |   const mins = d.getMinutes(); | ||||||
|  |   const secs = d.getSeconds(); | ||||||
|  |  | ||||||
|  |   const monthStr = month < 10 ? "0" + month : month; | ||||||
|  |   const dateStr = date < 10 ? "0" + date : date; | ||||||
|  |   const hoursStr = hours < 10 ? "0" + hours : hours; | ||||||
|  |   const minsStr = mins < 10 ? "0" + mins : mins; | ||||||
|  |   const secsStr = secs < 10 ? "0" + secs : secs; | ||||||
|  |  | ||||||
|  |   return `${year}/${monthStr}/${dateStr} ${hoursStr}:${minsStr}:${secsStr}`; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function dedupe<T>(data: T[]): T[] { | ||||||
|  |   return Array.from(new Set(data)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function dedupeObjectWithId<T extends { id: string | number }>(data: T[]): T[] { | ||||||
|  |   const idSet = new Set<string | number>(); | ||||||
|  |   const result = []; | ||||||
|  |  | ||||||
|  |   for (const d of data) { | ||||||
|  |     if (!idSet.has(d.id)) { | ||||||
|  |       idSet.add(d.id); | ||||||
|  |       result.push(d); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return detectedOS; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   export function getTimeStampByDate(t: Date | number | string): number { |   return result; | ||||||
|     if (typeof t === "string") { | } | ||||||
|       t = t.replaceAll("-", "/"); |  | ||||||
|  | export function debounce(fn: FunctionType, delay: number) { | ||||||
|  |   let timer: number | null = null; | ||||||
|  |  | ||||||
|  |   return () => { | ||||||
|  |     if (timer) { | ||||||
|  |       clearTimeout(timer); | ||||||
|  |       timer = setTimeout(fn, delay); | ||||||
|  |     } else { | ||||||
|  |       timer = setTimeout(fn, delay); | ||||||
|     } |     } | ||||||
|     const d = new Date(t); |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|     return d.getTime(); | export function throttle(fn: FunctionType, delay: number) { | ||||||
|   } |   let valid = true; | ||||||
|  |  | ||||||
|   export function getDateStampByDate(t: Date | number | string): number { |   return () => { | ||||||
|     const d = new Date(getTimeStampByDate(t)); |     if (!valid) { | ||||||
|  |       return false; | ||||||
|     return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function getDateString(t: Date | number | string): string { |  | ||||||
|     const d = new Date(getTimeStampByDate(t)); |  | ||||||
|  |  | ||||||
|     const year = d.getFullYear(); |  | ||||||
|     const month = d.getMonth() + 1; |  | ||||||
|     const date = d.getDate(); |  | ||||||
|  |  | ||||||
|     return `${year}/${month}/${date}`; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function getDataStringWithTs(ts: number): string { |  | ||||||
|     return getDateTimeString(ts * 1000); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function getTimeString(t: Date | number | string): string { |  | ||||||
|     const d = new Date(getTimeStampByDate(t)); |  | ||||||
|  |  | ||||||
|     const hours = d.getHours(); |  | ||||||
|     const mins = d.getMinutes(); |  | ||||||
|  |  | ||||||
|     const hoursStr = hours < 10 ? "0" + hours : hours; |  | ||||||
|     const minsStr = mins < 10 ? "0" + mins : mins; |  | ||||||
|  |  | ||||||
|     return `${hoursStr}:${minsStr}`; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // For example: 2021-4-8 17:52:17 |  | ||||||
|   export function getDateTimeString(t: Date | number | string): string { |  | ||||||
|     const d = new Date(getTimeStampByDate(t)); |  | ||||||
|  |  | ||||||
|     const year = d.getFullYear(); |  | ||||||
|     const month = d.getMonth() + 1; |  | ||||||
|     const date = d.getDate(); |  | ||||||
|     const hours = d.getHours(); |  | ||||||
|     const mins = d.getMinutes(); |  | ||||||
|     const secs = d.getSeconds(); |  | ||||||
|  |  | ||||||
|     const monthStr = month < 10 ? "0" + month : month; |  | ||||||
|     const dateStr = date < 10 ? "0" + date : date; |  | ||||||
|     const hoursStr = hours < 10 ? "0" + hours : hours; |  | ||||||
|     const minsStr = mins < 10 ? "0" + mins : mins; |  | ||||||
|     const secsStr = secs < 10 ? "0" + secs : secs; |  | ||||||
|  |  | ||||||
|     return `${year}/${monthStr}/${dateStr} ${hoursStr}:${minsStr}:${secsStr}`; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function dedupe<T>(data: T[]): T[] { |  | ||||||
|     return Array.from(new Set(data)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function dedupeObjectWithId<T extends { id: string | number }>(data: T[]): T[] { |  | ||||||
|     const idSet = new Set<string | number>(); |  | ||||||
|     const result = []; |  | ||||||
|  |  | ||||||
|     for (const d of data) { |  | ||||||
|       if (!idSet.has(d.id)) { |  | ||||||
|         idSet.add(d.id); |  | ||||||
|         result.push(d); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|  |     valid = false; | ||||||
|  |     setTimeout(() => { | ||||||
|  |       fn(); | ||||||
|  |       valid = true; | ||||||
|  |     }, delay); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|     return result; | export function transformObjectToParamsString(object: KVObject): string { | ||||||
|   } |   const params = []; | ||||||
|  |   const keys = Object.keys(object).sort(); | ||||||
|  |  | ||||||
|   export function debounce(fn: FunctionType, delay: number) { |   for (const key of keys) { | ||||||
|     let timer: number | null = null; |     const val = object[key]; | ||||||
|  |     if (val) { | ||||||
|     return () => { |  | ||||||
|       if (timer) { |  | ||||||
|         clearTimeout(timer); |  | ||||||
|         timer = setTimeout(fn, delay); |  | ||||||
|       } else { |  | ||||||
|         timer = setTimeout(fn, delay); |  | ||||||
|       } |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function throttle(fn: FunctionType, delay: number) { |  | ||||||
|     let valid = true; |  | ||||||
|  |  | ||||||
|     return () => { |  | ||||||
|       if (!valid) { |  | ||||||
|         return false; |  | ||||||
|       } |  | ||||||
|       valid = false; |  | ||||||
|       setTimeout(() => { |  | ||||||
|         fn(); |  | ||||||
|         valid = true; |  | ||||||
|       }, delay); |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function transformObjectToParamsString(object: KVObject): string { |  | ||||||
|     const params = []; |  | ||||||
|     const keys = Object.keys(object).sort(); |  | ||||||
|  |  | ||||||
|     for (const key of keys) { |  | ||||||
|       const val = object[key]; |  | ||||||
|       if (val) { |  | ||||||
|         if (typeof val === "object") { |  | ||||||
|           params.push(...transformObjectToParamsString(val).split("&")); |  | ||||||
|         } else { |  | ||||||
|           params.push(`${key}=${val}`); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return params.join("&"); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function transformParamsStringToObject(paramsString: string): KVObject { |  | ||||||
|     const object: KVObject = {}; |  | ||||||
|     const params = paramsString.split("&"); |  | ||||||
|  |  | ||||||
|     for (const p of params) { |  | ||||||
|       const [key, val] = p.split("="); |  | ||||||
|       if (key && val) { |  | ||||||
|         object[key] = val; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return object; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   export function filterObjectNullKeys(object: KVObject): KVObject { |  | ||||||
|     if (!object) { |  | ||||||
|       return {}; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const finalObject: KVObject = {}; |  | ||||||
|     const keys = Object.keys(object).sort(); |  | ||||||
|  |  | ||||||
|     for (const key of keys) { |  | ||||||
|       const val = object[key]; |  | ||||||
|       if (typeof val === "object") { |       if (typeof val === "object") { | ||||||
|         const temp = filterObjectNullKeys(JSON.parse(JSON.stringify(val))); |         params.push(...transformObjectToParamsString(val).split("&")); | ||||||
|         if (temp && Object.keys(temp).length > 0) { |  | ||||||
|           finalObject[key] = temp; |  | ||||||
|         } |  | ||||||
|       } else { |       } else { | ||||||
|         if (val) { |         params.push(`${key}=${val}`); | ||||||
|           finalObject[key] = val; |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return finalObject; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   export async function copyTextToClipboard(text: string) { |   return params.join("&"); | ||||||
|     if (navigator.clipboard && navigator.clipboard.writeText) { | } | ||||||
|       try { |  | ||||||
|         await navigator.clipboard.writeText(text); | export function transformParamsStringToObject(paramsString: string): KVObject { | ||||||
|       } catch (error: unknown) { |   const object: KVObject = {}; | ||||||
|         console.warn("Copy to clipboard failed.", error); |   const params = paramsString.split("&"); | ||||||
|  |  | ||||||
|  |   for (const p of params) { | ||||||
|  |     const [key, val] = p.split("="); | ||||||
|  |     if (key && val) { | ||||||
|  |       object[key] = val; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return object; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function filterObjectNullKeys(object: KVObject): KVObject { | ||||||
|  |   if (!object) { | ||||||
|  |     return {}; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const finalObject: KVObject = {}; | ||||||
|  |   const keys = Object.keys(object).sort(); | ||||||
|  |  | ||||||
|  |   for (const key of keys) { | ||||||
|  |     const val = object[key]; | ||||||
|  |     if (typeof val === "object") { | ||||||
|  |       const temp = filterObjectNullKeys(JSON.parse(JSON.stringify(val))); | ||||||
|  |       if (temp && Object.keys(temp).length > 0) { | ||||||
|  |         finalObject[key] = temp; | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       console.warn("Copy to clipboard failed, methods not supports."); |       if (val) { | ||||||
|  |         finalObject[key] = val; | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   export function getImageSize(src: string): Promise<{ width: number; height: number }> { |   return finalObject; | ||||||
|     return new Promise((resolve) => { | } | ||||||
|       const imgEl = new Image(); |  | ||||||
|  |  | ||||||
|       imgEl.onload = () => { | export async function copyTextToClipboard(text: string) { | ||||||
|         const { width, height } = imgEl; |   if (navigator.clipboard && navigator.clipboard.writeText) { | ||||||
|  |     try { | ||||||
|         if (width > 0 && height > 0) { |       await navigator.clipboard.writeText(text); | ||||||
|           resolve({ width, height }); |     } catch (error: unknown) { | ||||||
|         } else { |       console.warn("Copy to clipboard failed.", error); | ||||||
|           resolve({ width: 0, height: 0 }); |     } | ||||||
|         } |   } else { | ||||||
|       }; |     console.warn("Copy to clipboard failed, methods not supports."); | ||||||
|  |  | ||||||
|       imgEl.onerror = () => { |  | ||||||
|         resolve({ width: 0, height: 0 }); |  | ||||||
|       }; |  | ||||||
|  |  | ||||||
|       imgEl.className = "hidden"; |  | ||||||
|       imgEl.src = src; |  | ||||||
|       document.body.appendChild(imgEl); |  | ||||||
|       imgEl.remove(); |  | ||||||
|     }); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| export default utils; | export function getImageSize(src: string): Promise<{ width: number; height: number }> { | ||||||
|  |   return new Promise((resolve) => { | ||||||
|  |     const imgEl = new Image(); | ||||||
|  |  | ||||||
|  |     imgEl.onload = () => { | ||||||
|  |       const { width, height } = imgEl; | ||||||
|  |  | ||||||
|  |       if (width > 0 && height > 0) { | ||||||
|  |         resolve({ width, height }); | ||||||
|  |       } else { | ||||||
|  |         resolve({ width: 0, height: 0 }); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     imgEl.onerror = () => { | ||||||
|  |       resolve({ width: 0, height: 0 }); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     imgEl.className = "hidden"; | ||||||
|  |     imgEl.src = src; | ||||||
|  |     document.body.appendChild(imgEl); | ||||||
|  |     imgEl.remove(); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { useEffect, useState } from "react"; | import { useEffect, useState } from "react"; | ||||||
| import api from "../helpers/api"; | import * as api from "../helpers/api"; | ||||||
| import { validate, ValidatorConfig } from "../helpers/validator"; | import { validate, ValidatorConfig } from "../helpers/validator"; | ||||||
| import useLoading from "../hooks/useLoading"; | import useLoading from "../hooks/useLoading"; | ||||||
| import { locationService, userService } from "../services"; | import { locationService, userService } from "../services"; | ||||||
| @@ -23,7 +23,8 @@ const Signin: React.FC<Props> = () => { | |||||||
|   const actionBtnLoadingState = useLoading(false); |   const actionBtnLoadingState = useLoading(false); | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     api.getSystemStatus().then((status) => { |     api.getSystemStatus().then(({ data }) => { | ||||||
|  |       const { data: status } = data; | ||||||
|       setSiteOwner(status.owner); |       setSiteOwner(status.owner); | ||||||
|       if (status.profile.mode === "dev") { |       if (status.profile.mode === "dev") { | ||||||
|         setEmail("demo@usememos.com"); |         setEmail("demo@usememos.com"); | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import utils from "../helpers/utils"; | import * as utils from "../helpers/utils"; | ||||||
| import store from "../store"; | import store from "../store"; | ||||||
| import { setQuery, setPathname, Query } from "../store/modules/location"; | import { setQuery, setPathname, Query } from "../store/modules/location"; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import api from "../helpers/api"; | import * as api from "../helpers/api"; | ||||||
| import { TAG_REG } from "../helpers/consts"; | import { TAG_REG } from "../helpers/consts"; | ||||||
| import { createMemo, patchMemo, setMemos, setTags } from "../store/modules/memo"; | import { createMemo, patchMemo, setMemos, setTags } from "../store/modules/memo"; | ||||||
| import store from "../store"; | import store from "../store"; | ||||||
| @@ -17,7 +17,7 @@ const memoService = { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   fetchAllMemos: async () => { |   fetchAllMemos: async () => { | ||||||
|     const data = await api.getMyMemos(); |     const { data } = (await api.getMyMemos()).data; | ||||||
|     const memos = data.filter((m) => m.rowStatus !== "ARCHIVED").map((m) => convertResponseModelMemo(m)); |     const memos = data.filter((m) => m.rowStatus !== "ARCHIVED").map((m) => convertResponseModelMemo(m)); | ||||||
|     store.dispatch(setMemos(memos)); |     store.dispatch(setMemos(memos)); | ||||||
|  |  | ||||||
| @@ -25,7 +25,7 @@ const memoService = { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   fetchDeletedMemos: async () => { |   fetchDeletedMemos: async () => { | ||||||
|     const data = await api.getMyArchivedMemos(); |     const { data } = (await api.getMyArchivedMemos()).data; | ||||||
|     const deletedMemos = data.map((m) => { |     const deletedMemos = data.map((m) => { | ||||||
|       return convertResponseModelMemo(m); |       return convertResponseModelMemo(m); | ||||||
|     }); |     }); | ||||||
| @@ -60,13 +60,13 @@ const memoService = { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   createMemo: async (memoCreate: MemoCreate) => { |   createMemo: async (memoCreate: MemoCreate) => { | ||||||
|     const data = await api.createMemo(memoCreate); |     const { data } = (await api.createMemo(memoCreate)).data; | ||||||
|     const memo = convertResponseModelMemo(data); |     const memo = convertResponseModelMemo(data); | ||||||
|     store.dispatch(createMemo(memo)); |     store.dispatch(createMemo(memo)); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   patchMemo: async (memoPatch: MemoPatch): Promise<Memo> => { |   patchMemo: async (memoPatch: MemoPatch): Promise<Memo> => { | ||||||
|     const data = await api.patchMemo(memoPatch); |     const { data } = (await api.patchMemo(memoPatch)).data; | ||||||
|     const memo = convertResponseModelMemo(data); |     const memo = convertResponseModelMemo(data); | ||||||
|     store.dispatch(patchMemo(memo)); |     store.dispatch(patchMemo(memo)); | ||||||
|     return memo; |     return memo; | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import api from "../helpers/api"; | import * as api from "../helpers/api"; | ||||||
|  |  | ||||||
| const resourceService = { | const resourceService = { | ||||||
|   /** |   /** | ||||||
| @@ -6,7 +6,7 @@ const resourceService = { | |||||||
|    * @param file file |    * @param file file | ||||||
|    * @returns resource: id, filename |    * @returns resource: id, filename | ||||||
|    */ |    */ | ||||||
|   async upload(file: File) { |   async upload(file: File): Promise<Resource> { | ||||||
|     const { name: filename, size } = file; |     const { name: filename, size } = file; | ||||||
|  |  | ||||||
|     if (size > 64 << 20) { |     if (size > 64 << 20) { | ||||||
| @@ -15,7 +15,7 @@ const resourceService = { | |||||||
|  |  | ||||||
|     const formData = new FormData(); |     const formData = new FormData(); | ||||||
|     formData.append("file", file, filename); |     formData.append("file", file, filename); | ||||||
|     const data = await api.uploadFile(formData); |     const { data } = (await api.uploadFile(formData)).data; | ||||||
|  |  | ||||||
|     return data; |     return data; | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import api from "../helpers/api"; | import * as api from "../helpers/api"; | ||||||
| import store from "../store/"; | import store from "../store/"; | ||||||
| import { createShortcut, deleteShortcut, patchShortcut, setShortcuts } from "../store/modules/shortcut"; | import { createShortcut, deleteShortcut, patchShortcut, setShortcuts } from "../store/modules/shortcut"; | ||||||
|  |  | ||||||
| @@ -16,8 +16,8 @@ const shortcutService = { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   getMyAllShortcuts: async () => { |   getMyAllShortcuts: async () => { | ||||||
|     const rawData = await api.getMyShortcuts(); |     const { data } = (await api.getMyShortcuts()).data; | ||||||
|     const shortcuts = rawData.map((s) => convertResponseModelShortcut(s)); |     const shortcuts = data.map((s) => convertResponseModelShortcut(s)); | ||||||
|     store.dispatch(setShortcuts(shortcuts)); |     store.dispatch(setShortcuts(shortcuts)); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
| @@ -32,13 +32,13 @@ const shortcutService = { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   createShortcut: async (shortcutCreate: ShortcutCreate) => { |   createShortcut: async (shortcutCreate: ShortcutCreate) => { | ||||||
|     const data = await api.createShortcut(shortcutCreate); |     const { data } = (await api.createShortcut(shortcutCreate)).data; | ||||||
|     const shortcut = convertResponseModelShortcut(data); |     const shortcut = convertResponseModelShortcut(data); | ||||||
|     store.dispatch(createShortcut(shortcut)); |     store.dispatch(createShortcut(shortcut)); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   patchShortcut: async (shortcutPatch: ShortcutPatch) => { |   patchShortcut: async (shortcutPatch: ShortcutPatch) => { | ||||||
|     const data = await api.patchShortcut(shortcutPatch); |     const { data } = (await api.patchShortcut(shortcutPatch)).data; | ||||||
|     const shortcut = convertResponseModelShortcut(data); |     const shortcut = convertResponseModelShortcut(data); | ||||||
|     store.dispatch(patchShortcut(shortcut)); |     store.dispatch(patchShortcut(shortcut)); | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import api from "../helpers/api"; | import * as api from "../helpers/api"; | ||||||
| import store from "../store"; | import store from "../store"; | ||||||
| import { setUser, patchUser } from "../store/modules/user"; | import { setUser, patchUser } from "../store/modules/user"; | ||||||
|  |  | ||||||
| @@ -16,7 +16,7 @@ const userService = { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   doSignIn: async () => { |   doSignIn: async () => { | ||||||
|     const user = await api.getUser(); |     const { data: user } = (await api.getUser()).data; | ||||||
|     if (user) { |     if (user) { | ||||||
|       store.dispatch(setUser(convertResponseModelUser(user))); |       store.dispatch(setUser(convertResponseModelUser(user))); | ||||||
|     } else { |     } else { | ||||||
| @@ -31,7 +31,7 @@ const userService = { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   patchUser: async (userPatch: UserPatch): Promise<void> => { |   patchUser: async (userPatch: UserPatch): Promise<void> => { | ||||||
|     const data = await api.patchUser(userPatch); |     const { data } = (await api.patchUser(userPatch)).data; | ||||||
|     const user = convertResponseModelUser(data); |     const user = convertResponseModelUser(data); | ||||||
|     store.dispatch(patchUser(user)); |     store.dispatch(patchUser(user)); | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -589,6 +589,11 @@ array.prototype.flatmap@^1.2.5: | |||||||
|     es-abstract "^1.19.2" |     es-abstract "^1.19.2" | ||||||
|     es-shim-unscopables "^1.0.0" |     es-shim-unscopables "^1.0.0" | ||||||
|  |  | ||||||
|  | asynckit@^0.4.0: | ||||||
|  |   version "0.4.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" | ||||||
|  |   integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== | ||||||
|  |  | ||||||
| autoprefixer@^10.4.2: | autoprefixer@^10.4.2: | ||||||
|   version "10.4.4" |   version "10.4.4" | ||||||
|   resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.4.tgz#3e85a245b32da876a893d3ac2ea19f01e7ea5a1e" |   resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.4.tgz#3e85a245b32da876a893d3ac2ea19f01e7ea5a1e" | ||||||
| @@ -601,6 +606,14 @@ autoprefixer@^10.4.2: | |||||||
|     picocolors "^1.0.0" |     picocolors "^1.0.0" | ||||||
|     postcss-value-parser "^4.2.0" |     postcss-value-parser "^4.2.0" | ||||||
|  |  | ||||||
|  | axios@^0.27.2: | ||||||
|  |   version "0.27.2" | ||||||
|  |   resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" | ||||||
|  |   integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== | ||||||
|  |   dependencies: | ||||||
|  |     follow-redirects "^1.14.9" | ||||||
|  |     form-data "^4.0.0" | ||||||
|  |  | ||||||
| balanced-match@^1.0.0: | balanced-match@^1.0.0: | ||||||
|   version "1.0.2" |   version "1.0.2" | ||||||
|   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" |   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" | ||||||
| @@ -716,6 +729,13 @@ color-name@^1.1.4, color-name@~1.1.4: | |||||||
|   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" |   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" | ||||||
|   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== |   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== | ||||||
|  |  | ||||||
|  | combined-stream@^1.0.8: | ||||||
|  |   version "1.0.8" | ||||||
|  |   resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" | ||||||
|  |   integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== | ||||||
|  |   dependencies: | ||||||
|  |     delayed-stream "~1.0.0" | ||||||
|  |  | ||||||
| concat-map@0.0.1: | concat-map@0.0.1: | ||||||
|   version "0.0.1" |   version "0.0.1" | ||||||
|   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" |   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" | ||||||
| @@ -785,6 +805,11 @@ defined@^1.0.0: | |||||||
|   resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" |   resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" | ||||||
|   integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= |   integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= | ||||||
|  |  | ||||||
|  | delayed-stream@~1.0.0: | ||||||
|  |   version "1.0.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" | ||||||
|  |   integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= | ||||||
|  |  | ||||||
| detective@^5.2.0: | detective@^5.2.0: | ||||||
|   version "5.2.0" |   version "5.2.0" | ||||||
|   resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" |   resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" | ||||||
| @@ -1234,6 +1259,20 @@ flatted@^3.1.0: | |||||||
|   resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" |   resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" | ||||||
|   integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== |   integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== | ||||||
|  |  | ||||||
|  | follow-redirects@^1.14.9: | ||||||
|  |   version "1.15.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.0.tgz#06441868281c86d0dda4ad8bdaead2d02dca89d4" | ||||||
|  |   integrity sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ== | ||||||
|  |  | ||||||
|  | form-data@^4.0.0: | ||||||
|  |   version "4.0.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" | ||||||
|  |   integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== | ||||||
|  |   dependencies: | ||||||
|  |     asynckit "^0.4.0" | ||||||
|  |     combined-stream "^1.0.8" | ||||||
|  |     mime-types "^2.1.12" | ||||||
|  |  | ||||||
| fraction.js@^4.2.0: | fraction.js@^4.2.0: | ||||||
|   version "4.2.0" |   version "4.2.0" | ||||||
|   resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" |   resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" | ||||||
| @@ -1665,6 +1704,18 @@ micromatch@^4.0.4: | |||||||
|     braces "^3.0.2" |     braces "^3.0.2" | ||||||
|     picomatch "^2.3.1" |     picomatch "^2.3.1" | ||||||
|  |  | ||||||
|  | mime-db@1.52.0: | ||||||
|  |   version "1.52.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" | ||||||
|  |   integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== | ||||||
|  |  | ||||||
|  | mime-types@^2.1.12: | ||||||
|  |   version "2.1.35" | ||||||
|  |   resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" | ||||||
|  |   integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== | ||||||
|  |   dependencies: | ||||||
|  |     mime-db "1.52.0" | ||||||
|  |  | ||||||
| mime@^1.4.1: | mime@^1.4.1: | ||||||
|   version "1.6.0" |   version "1.6.0" | ||||||
|   resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" |   resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user