Skip to content

Commit

Permalink
feat: add webdav create modal
Browse files Browse the repository at this point in the history
  • Loading branch information
yuanyxh committed May 7, 2024
1 parent 9ea79e4 commit 941999c
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 25 deletions.
6 changes: 3 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { MessageInstance } from 'antd/es/message/interface';
import type { HookAPI } from 'antd/es/modal/useModal';
import type { NotificationInstance } from 'antd/es/notification/interface';

import type { State } from '@/store';
import type { AppState } from '@/store';
import { useAppStore } from '@/store';

import {
Expand Down Expand Up @@ -80,9 +80,9 @@ const App: React.FC<IAppProps> = (props) => {
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');

if (hasLocalStorage('app')) {
setLanguage(getStorage<State>('app')?.settings?.language || 'zh-CN');
setLanguage(getStorage<AppState>('app')?.settings?.language || 'zh-CN');
setColorScheme(
getStorage<State>('app')?.settings?.colorScheme || 'light'
getStorage<AppState>('app')?.settings?.colorScheme || 'light'
);
} else {
setLanguage(language);
Expand Down
1 change: 1 addition & 0 deletions src/assets/svgs/mdi--web.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 10 additions & 7 deletions src/filehandle/FilePanelFactory.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

import { App } from 'antd';
Expand All @@ -20,25 +21,27 @@ class FilePanelFactory {

constructor() {
const show = (cb?: AsyncFunction) => {
if (!this._show && cb) {
return (this._show = cb);
if (cb) {
this._show = cb;
}

this.show();
};
const hide = (cb?: AsyncFunction) => {
if (!this._hide && cb) {
return (this._hide = cb);
if (cb) {
this._hide = cb;
}

this.hide();
};

this.root.render(
// antd App provider Modal, Message, Notification
<App>
<FilePanelContainer show={show} hide={hide} />
</App>
<StrictMode>
<App>
<FilePanelContainer show={show} hide={hide} />
</App>
</StrictMode>
);
}

Expand Down
122 changes: 121 additions & 1 deletion src/filehandle/components/FileContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { useContext, useMemo, useRef, useState } from 'react';

import type { InputProps } from 'antd';
import type { InputRef } from 'antd';
import { App, Input, Modal, Typography } from 'antd';
import { App, Form, Input, Modal, Typography } from 'antd';
import { ExclamationCircleFilled } from '@ant-design/icons';

import type { WebdavInfo } from '@/store';
import { useUserStore } from '@/store';

import { sleep, validateFileName } from '@/utils';

import { ContextMenu, Icon } from '@/components';
Expand Down Expand Up @@ -123,6 +126,101 @@ function AddFileModal({
);
}

function MountWebdavModal(props: { open: boolean; close(): void }) {
const { open, close } = props;

const { modal, message } = App.useApp();

const [form] = Form.useForm();

const { webdavs, addWebdav } = useUserStore();

const handleOk = () => {
form
.validateFields()
.then(() => {
const webdav: WebdavInfo = form.getFieldsValue();

if (webdavs.some((w) => w.name === webdav.name.trim())) {
modal.error({
title: '提示',
content: `已包含名称为 "${webdav.name.trim()}" 的挂载目录!`
});

return void 0;
}

addWebdav({ ...webdav, name: webdav.name.trim() });

message.success('已添加 webdav 目录。');

sleep(35, close);
})
.catch(() => {
/* empty */
});
};

const handleCancel = () => {
form.resetFields();
close();
};

return (
<Modal
title="挂载 webdav 目录"
style={{ top: '30vh' }}
open={open}
okText={'确认'}
cancelText={'取消'}
onOk={handleOk}
onCancel={handleCancel}
>
<Form
name="mount_webdav"
layout="vertical"
form={form}
style={{ maxWidth: 600 }}
autoComplete="off"
>
<Form.Item<WebdavInfo>
label="webdav 路径"
name="url"
rules={[{ required: true, message: 'Please input your webdav url!' }]}
>
<Input />
</Form.Item>

<Form.Item<WebdavInfo>
label="目录名称"
name="name"
rules={[
{ required: true, message: 'Please input your webdav locale name!' }
]}
>
<Input />
</Form.Item>

<Form.Item<WebdavInfo>
label="用户名"
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input />
</Form.Item>

<Form.Item<WebdavInfo>
label="密码"
name="password"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
</Form>
</Modal>
);
}

interface IFileItemProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
file: FileInfo;
}
Expand Down Expand Up @@ -178,16 +276,21 @@ const FileContent: React.FC<IFileContentProps> = (props) => {
current,
children,
fileHandles,
fileLinked,
enterDirectory,
create,
remove,
forceUpdate
} = useContext(FileSystemContext);

const root = fileLinked?.root?.value;

const titleRef = useRef<FileType>(0);
const sectionRef = useRef<HTMLElement>(null);

const [isModalOpen, setModalOpen] = useState(false);
const [isMountModalOpen, setMountModalOpen] = useState(false);

const [selection, setSelection] = useState<string[]>([]);

const contextMenu = fileHandles
Expand Down Expand Up @@ -218,6 +321,10 @@ const FileContent: React.FC<IFileContentProps> = (props) => {
setModalOpen(true);
};

const handleMountWebdav = () => {
setMountModalOpen(true);
};

const handleImportFile = () => {
importFile(current)
.then((value) => {
Expand Down Expand Up @@ -313,6 +420,14 @@ const FileContent: React.FC<IFileContentProps> = (props) => {
onHide={() => selection.length && setSelection([])}
menu={[
...contextMenu,
{
name: '挂载 webdav 目录',
icon: <Icon icon="mdi--web" color="var(--color-primary)" />,
style: {
display: current === root ? void 0 : 'none'
},
onClick: handleMountWebdav
},
{
name: '新建文件',
icon: <Icon icon="ph--file-fill" color="var(--color-primary)" />,
Expand Down Expand Up @@ -366,6 +481,11 @@ const FileContent: React.FC<IFileContentProps> = (props) => {
onOk={handleOk}
onCancel={handleCancel}
/>

<MountWebdavModal
open={isMountModalOpen}
close={() => setMountModalOpen(false)}
/>
</>
);
};
Expand Down
8 changes: 6 additions & 2 deletions src/filehandle/hooks/useFileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { useContext, useEffect, useMemo, useRef, useState } from 'react';

import { cloneDeep } from 'lodash-es';

import { useUserStore } from '@/store';

import { AppContext } from '@/App';

import FileLinkedList from '../FileLinkedList';
Expand Down Expand Up @@ -44,6 +46,8 @@ export interface FileSystem {
export function useFileSystem(): FileSystem {
const { message } = useContext(AppContext);

const { webdavs } = useUserStore();

const root =
useRef<DH>() as React.MutableRefObject<FileSystemDirectoryHandle>;
const fileLinked =
Expand All @@ -60,15 +64,15 @@ export function useFileSystem(): FileSystem {
if (!current) return;

getChildren(current).then((_children) => {
setChildren(_children);
setChildren([..._children]);
});
};

useMemo(() => {
if (!current) return void 0;

update();
}, [current]);
}, [current, webdavs]);

useMemo(() => {
if (!fileLinked.current) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@
}

&.active.changed > span::after {
content: '*';
color: var(--color-primary);
content: '*';
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/filehandle/md_editor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

import { createElementContainer } from '@/utils';
Expand All @@ -16,7 +17,11 @@ async function createMDHandleInstance(handle: DH | FH) {
root.unmount();
el.remove();
};
root.render(<MDHandle handle={handle} destroy={destroy} />);
root.render(
<StrictMode>
<MDHandle handle={handle} destroy={destroy} />
</StrictMode>
);
}

const mdHandler: FileHandle = {
Expand Down
4 changes: 4 additions & 0 deletions src/filehandle/utils/fileManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export interface FileInfo {
type: FileType;
icon: React.ReactNode;
handle: DH | FH;
/** this is webdav file? */
remote?: boolean;
/** webdav file full path */
fullPath?: string;
ext?: string;
}

Expand Down
2 changes: 2 additions & 0 deletions src/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { default as useAppStore } from './useAppStore';
export * from './useAppStore';
export { default as useUserStore } from './useUserStore';
export * from './useUserStore';
18 changes: 9 additions & 9 deletions src/store/useAppStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getStorage, setStorage } from '@/utils';

import { create } from 'zustand';

export interface State {
export interface AppState {
settings: {
language: string;
colorScheme: 'light' | 'dark';
Expand All @@ -14,23 +14,23 @@ export interface State {
};
}

export interface Actions {
export interface AppActions {
setLanguage(language: string): void;
setColorScheme(colorScheme: State['settings']['colorScheme']): void;
setColorScheme(colorScheme: AppState['settings']['colorScheme']): void;
setColorSchemeNonPersistent(
colorScheme: State['settings']['colorScheme']
colorScheme: AppState['settings']['colorScheme']
): void;
setEnableServiceWorkerCache(
enableServiceWorkerCache: State['settings']['enableServiceWorkerCache']
enableServiceWorkerCache: AppState['settings']['enableServiceWorkerCache']
): void;
setEnableNotification(
enableNotification: State['settings']['enableNotification']
enableNotification: AppState['settings']['enableNotification']
): void;
setFrontDesk(frontDesk: State['status']['frontDesk']): void;
setFrontDesk(frontDesk: AppState['status']['frontDesk']): void;
}

const useAppStore = create<State & Actions>((set) => ({
...getStorage<State>('app', {
const useAppStore = create<AppState & AppActions>((set) => ({
...getStorage<AppState>('app', {
settings: {
language: 'zh-CN',
colorScheme: 'light',
Expand Down
35 changes: 35 additions & 0 deletions src/store/useUserStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { getStorage, setStorage } from '@/utils';

import { create } from 'zustand';

export interface WebdavInfo {
url: string;
name: string;
username: string;
password: string;
}

export interface UserState {
webdavs: WebdavInfo[];
}

export interface UserActions {
addWebdav(webdav: WebdavInfo): void;
}

const useAppStore = create<UserState & UserActions>((set) => ({
...getStorage<UserState>('user', {
webdavs: []
}),
addWebdav(webdav) {
set((prev) => {
const webdavs = { webdavs: [...prev.webdavs, webdav] };

setStorage('user', webdavs);

return webdavs;
});
}
}));

export default useAppStore;
Loading

0 comments on commit 941999c

Please sign in to comment.