diff --git a/package.json b/package.json
index 895d395..191d56f 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
"@fontsource/roboto": "^5.0.8",
"@giscus/react": "^3.1.0",
"@marsidev/react-turnstile": "^1.1.0",
- "@mdxeditor/editor": "^3.4.0",
+ "@mdxeditor/editor": "^3.39.0",
"@mui/icons-material": "^6.0.0",
"@mui/lab": "^6.0.0-beta.30",
"@mui/material": "^6.4.6",
@@ -114,4 +114,4 @@
"prettier --write"
]
}
-}
\ No newline at end of file
+}
diff --git a/public/locales/en-US/application.json b/public/locales/en-US/application.json
index 3a259a9..765a648 100644
--- a/public/locales/en-US/application.json
+++ b/public/locales/en-US/application.json
@@ -479,6 +479,8 @@
"useCustomPassword": "Custom share link password",
"shareView": "Share view setting",
"shareViewDes": "If selected, other users can see your view setting (layout, sorting, etc.) saved on the server server when accessing this shared folder.",
+ "showReadme": "Show README file",
+ "showReadmeDes": "If selected, the <0>README.md0> file (case-sensitive) in the directory will be automatically displayed for visitors.",
"expireAfterDownload": "Expire after being downloaded",
"sharePassword": "Share password",
"randomlyGenerate": "Random",
diff --git a/public/locales/ja-JP/application.json b/public/locales/ja-JP/application.json
index 7b588ff..d7a2bf9 100644
--- a/public/locales/ja-JP/application.json
+++ b/public/locales/ja-JP/application.json
@@ -510,7 +510,9 @@
"unlinkOnly": "物理ファイルを保持",
"unlinkOnlyDes": "ファイル記録のみ削除、物理ファイルは削除されません",
"shareView": "共有ビュー設定",
- "shareViewDes": "チェックを入れると、他のユーザーがこの共有フォルダにアクセスした際にあなたのビュー設定(レイアウト、ソートなど)を見ることができます。"
+ "shareViewDes": "チェックを入れると、他のユーザーがこの共有フォルダにアクセスした際にあなたのビュー設定(レイアウト、ソートなど)を見ることができます。",
+ "showReadme": "README ファイルを表示",
+ "showReadmeDes": "チェックを入れると、他のユーザーがこの共有フォルダにアクセスした際にあなたのビュー設定(レイアウト、ソートなど)を見ることができます。"
},
"uploader": {
"fileCopyName": "コピー_",
diff --git a/public/locales/zh-CN/application.json b/public/locales/zh-CN/application.json
index 1b79938..398b56b 100644
--- a/public/locales/zh-CN/application.json
+++ b/public/locales/zh-CN/application.json
@@ -513,6 +513,8 @@
"unlinkOnlyDes": "仅删除文件记录,物理文件不会被删除",
"shareView": "分享视图设置",
"shareViewDes": "勾选后,其他用户访问此共享文件夹时可以看到你保存在服务器的视图设置(布局、排序等)。",
+ "showReadme": "显示 README 文件",
+ "showReadmeDes": "勾选后,会自动为访问者展示目录下的 <0>README.md0> (区分大小写) 文件。",
"viewSetting": "视图设置",
"saved": "已保存",
"notSet": "未设置",
diff --git a/public/locales/zh-TW/application.json b/public/locales/zh-TW/application.json
index fbe414b..95ed8ca 100644
--- a/public/locales/zh-TW/application.json
+++ b/public/locales/zh-TW/application.json
@@ -509,6 +509,8 @@
"unlinkOnlyDes": "僅刪除檔案記錄,物理檔案不會被刪除",
"shareView": "分享視圖設定",
"shareViewDes": "勾選後,其他使用者存取此共享資料夾時可以看到你保存在服務器的視圖設定(佈局、排序等)。",
+ "showReadme": "顯示 README 文件",
+ "showReadmeDes": "勾選後,會自動為訪問者展示目錄下的 <0>README.md0> (區分大小寫) 文件。",
"viewSetting": "視圖設定",
"saved": "已保存",
"notSet": "未設定",
diff --git a/src/App.tsx b/src/App.tsx
index 422088b..9389725 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,6 +7,7 @@ import { Suspense, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Outlet } from "react-router-dom";
import { useRegisterSW } from "virtual:pwa-register/react";
+import FileIconSnackbar from "./component/Common/Snackbar/FileIconSnackbar.tsx";
import LoadingSnackbar from "./component/Common/Snackbar/LoadingSnackbar.tsx";
import GlobalDialogs from "./component/Dialogs/GlobalDialogs.tsx";
import { GrowDialogTransition } from "./component/FileManager/Search/SearchPopup.tsx";
@@ -344,6 +345,7 @@ const AppContent = () => {
warning: StyledMaterialDesignContent,
loading: LoadingSnackbar,
default: StyledMaterialDesignContent,
+ file: FileIconSnackbar,
}}
>
diff --git a/src/api/explorer.ts b/src/api/explorer.ts
index d42303f..d63c214 100644
--- a/src/api/explorer.ts
+++ b/src/api/explorer.ts
@@ -87,6 +87,7 @@ export interface Share {
owner: User;
source_uri?: string;
password?: string;
+ show_readme?: boolean;
}
export enum PolicyType {
@@ -288,6 +289,7 @@ export interface ShareCreateService {
password?: string;
expire?: number;
share_view?: boolean;
+ show_readme?: boolean;
}
export interface CreateFileService {
diff --git a/src/component/Common/Snackbar/FileIconSnackbar.tsx b/src/component/Common/Snackbar/FileIconSnackbar.tsx
new file mode 100644
index 0000000..c3ffe9c
--- /dev/null
+++ b/src/component/Common/Snackbar/FileIconSnackbar.tsx
@@ -0,0 +1,82 @@
+import { Box } from "@mui/material";
+import MuiSnackbarContent from "@mui/material/SnackbarContent";
+import { CustomContentProps } from "notistack";
+import * as React from "react";
+import { forwardRef, useState } from "react";
+import { FileResponse } from "../../../api/explorer.ts";
+import FileTypeIcon from "../../FileManager/Explorer/FileTypeIcon.tsx";
+
+declare module "notistack" {
+ interface VariantOverrides {
+ file: {
+ file: FileResponse;
+ };
+ }
+}
+
+interface FileIconSnackbarProps extends CustomContentProps {
+ file: FileResponse;
+}
+
+const FileIconSnackbar = forwardRef((props, ref) => {
+ const [progress, setProgress] = useState(0);
+ const {
+ // You have access to notistack props and options 👇🏼
+ message,
+ action,
+ id,
+ file,
+ // as well as your own custom props 👇🏼
+ ...other
+ } = props;
+
+ let componentOrFunctionAction: React.ReactNode = undefined;
+ if (typeof action === "function") {
+ componentOrFunctionAction = action(id);
+ } else {
+ componentOrFunctionAction = action;
+ }
+
+ return (
+
+
+
+ {message}
+
+
+ {componentOrFunctionAction && (
+
+ {componentOrFunctionAction}
+
+ )}
+
+ }
+ />
+ );
+});
+
+export default FileIconSnackbar;
diff --git a/src/component/Common/Snackbar/snackbar.tsx b/src/component/Common/Snackbar/snackbar.tsx
index 325241e..2cc601f 100644
--- a/src/component/Common/Snackbar/snackbar.tsx
+++ b/src/component/Common/Snackbar/snackbar.tsx
@@ -1,14 +1,15 @@
-import { closeSnackbar, SnackbarKey } from "notistack";
import { Button } from "@mui/material";
-import { useTranslation } from "react-i18next";
-import { Response } from "../../../api/request.ts";
-import { useAppDispatch } from "../../../redux/hooks.ts";
+import { closeSnackbar, SnackbarKey } from "notistack";
import { useCallback } from "react";
+import { useTranslation } from "react-i18next";
+import { useNavigate } from "react-router-dom";
+import { FileResponse } from "../../../api/explorer.ts";
+import { Response } from "../../../api/request.ts";
+import { setBatchDownloadLogDialog, setShareReadmeOpen } from "../../../redux/globalStateSlice.ts";
+import { useAppDispatch } from "../../../redux/hooks.ts";
import { showAggregatedErrorDialog } from "../../../redux/thunks/dialog.ts";
import { navigateToPath } from "../../../redux/thunks/filemanager.ts";
import { FileManagerIndex } from "../../FileManager/FileManager.tsx";
-import { setBatchDownloadLogDialog } from "../../../redux/globalStateSlice.ts";
-import { useNavigate } from "react-router-dom";
export const DefaultCloseAction = (snackbarId: SnackbarKey | undefined) => {
const { t } = useTranslation();
@@ -42,6 +43,28 @@ export const ErrorListDetailAction = (error: Response) => (snackbarId: Snac
);
};
+export const OpenReadMeAction = (file: FileResponse) => (snackbarId: SnackbarKey | undefined) => {
+ const { t } = useTranslation();
+ const dispatch = useAppDispatch();
+ const navigate = useNavigate();
+
+ const Close = DefaultCloseAction(snackbarId);
+
+ const openReadMe = useCallback(() => {
+ dispatch(setShareReadmeOpen({ open: true, target: file }));
+ closeSnackbar(snackbarId);
+ }, [dispatch, file, snackbarId]);
+
+ return (
+ <>
+
+ {Close}
+ >
+ );
+};
+
export const ViewDstAction = (dst: string) => (snackbarId: SnackbarKey | undefined) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
diff --git a/src/component/FileManager/Dialogs/Share/ShareDialog.tsx b/src/component/FileManager/Dialogs/Share/ShareDialog.tsx
index 9fc1026..4578537 100644
--- a/src/component/FileManager/Dialogs/Share/ShareDialog.tsx
+++ b/src/component/FileManager/Dialogs/Share/ShareDialog.tsx
@@ -33,6 +33,7 @@ const shareToSetting = (share: ShareModel, t: TFunction): ShareSetting => {
password: share.password,
use_custom_password: true,
share_view: share.share_view,
+ show_readme: share.show_readme,
downloads: share.remain_downloads != undefined && share.remain_downloads > 0,
expires_val: expireOptions[2],
diff --git a/src/component/FileManager/Dialogs/Share/ShareSetting.tsx b/src/component/FileManager/Dialogs/Share/ShareSetting.tsx
index c329c48..26953af 100644
--- a/src/component/FileManager/Dialogs/Share/ShareSetting.tsx
+++ b/src/component/FileManager/Dialogs/Share/ShareSetting.tsx
@@ -18,9 +18,11 @@ import MuiAccordion from "@mui/material/Accordion";
import MuiAccordionDetails from "@mui/material/AccordionDetails";
import MuiAccordionSummary from "@mui/material/AccordionSummary";
import { useState } from "react";
-import { useTranslation } from "react-i18next";
+import { Trans, useTranslation } from "react-i18next";
import { FileResponse, FileType } from "../../../../api/explorer.ts";
+import { Code } from "../../../Admin/Common/Code.tsx";
import { FilledTextField, SmallFormControlLabel } from "../../../Common/StyledComponents.tsx";
+import BookInformation from "../../../Icons/BookInformation.tsx";
import ClockArrowDownload from "../../../Icons/ClockArrowDownload.tsx";
import Eye from "../../../Icons/Eye.tsx";
import TableSettingsOutlined from "../../../Icons/TableSettings.tsx";
@@ -76,6 +78,7 @@ export interface ShareSetting {
use_custom_password?: boolean;
password?: string;
share_view?: boolean;
+ show_readme?: boolean;
downloads?: boolean;
expires?: boolean;
@@ -129,7 +132,7 @@ const ShareSettingContent = ({ setting, file, editing, onSettingChange }: ShareS
setExpanded(isExpanded ? panel : undefined);
};
- const handleCheck = (prop: "is_private" | "share_view" | "expires" | "downloads") => () => {
+ const handleCheck = (prop: "is_private" | "share_view" | "show_readme" | "expires" | "downloads") => () => {
if (!setting[prop]) {
handleExpand(prop)(null, true);
}
@@ -198,20 +201,38 @@ const ShareSettingContent = ({ setting, file, editing, onSettingChange }: ShareS
{file?.type == FileType.folder && (
-
-
- {t("application:modals.shareViewDes")}
-
+ <>
+
+
+ {t("application:modals.shareViewDes")}
+
+
+
+
+ ]} />
+
+
+ >
)}