Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add upload button for mobile #561

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions apps/web/app/api/assets/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ import { TRPCError } from "@trpc/server";

import type { ZUploadResponse } from "@hoarder/shared/types/uploads";
import { assets, AssetTypes } from "@hoarder/db/schema";
import {
newAssetId,
saveAsset,
SUPPORTED_UPLOAD_ASSET_TYPES,
} from "@hoarder/shared/assetdb";
import { newAssetId, saveAsset } from "@hoarder/shared/assetdb";
import { SUPPORTED_UPLOAD_ASSET_TYPES } from "@hoarder/shared/assetTypes";
import serverConfig from "@hoarder/shared/config";

const MAX_UPLOAD_SIZE_BYTES = serverConfig.maxAssetSizeMb * 1024 * 1024;
Expand Down
23 changes: 18 additions & 5 deletions apps/web/components/dashboard/UploadDropzone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,7 @@ function useUploadAssets({
};
}

export default function UploadDropzone({
children,
}: {
children: React.ReactNode;
}) {
export function useFileUploader() {
const [numUploading, setNumUploading] = useState(0);
const [numUploaded, setNumUploaded] = useState(0);
const uploadAssets = useUploadAssets({
Expand All @@ -102,6 +98,23 @@ export default function UploadDropzone({
},
});

return {
numUploading,
setNumUploading,
numUploaded,
setNumUploaded,
uploadAssets,
};
}

export default function UploadDropzone({
children,
}: {
children: React.ReactNode;
}) {
const { numUploading, setNumUploading, numUploaded, uploadAssets } =
useFileUploader();

const [isDragging, setDragging] = useState(false);
const onDrop = (acceptedFiles: File[]) => {
uploadAssets(acceptedFiles);
Expand Down
63 changes: 55 additions & 8 deletions apps/web/components/dashboard/bookmarks/EditorCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import {
} from "@/lib/userLocalSettings/bookmarksLayout";
import { cn, getOS } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";
import { FilePlus } from "lucide-react";
import { useForm } from "react-hook-form";
import { z } from "zod";

import { useCreateBookmarkWithPostHook } from "@hoarder/shared-react/hooks/bookmarks";
import { SUPPORTED_UPLOAD_ASSET_TYPES } from "@hoarder/shared/assetTypes";
import { BookmarkTypes } from "@hoarder/shared/types/bookmarks";

import { useUploadAsset } from "../UploadDropzone";
import { useFileUploader, useUploadAsset } from "../UploadDropzone";

function useFocusOnKeyPress(inputRef: React.RefObject<HTMLTextAreaElement>) {
useEffect(() => {
Expand Down Expand Up @@ -167,6 +169,16 @@ export default function EditorCard({ className }: { className?: string }) {
}
};

const uploadFile = useRef<HTMLInputElement>(null);

const { numUploading, setNumUploading, numUploaded, uploadAssets } =
useFileUploader();

const handleFileUpload = (acceptedFiles: File[]) => {
uploadAssets(acceptedFiles);
setNumUploading(acceptedFiles.length);
};

const OS = getOS();

return (
Expand Down Expand Up @@ -219,13 +231,48 @@ export default function EditorCard({ className }: { className?: string }) {
/>
</FormControl>
</FormItem>
<ActionButton loading={isPending} type="submit" variant="default">
{form.formState.dirtyFields.text
? demoMode
? "Submissions are disabled"
: `Save (${OS === "macos" ? "⌘" : "Ctrl"} + Enter)`
: "Save"}
</ActionButton>
<div className="flex gap-1">
<ActionButton
loading={isPending}
type="submit"
variant="default"
className="flex-1"
>
{form.formState.dirtyFields.text
? demoMode
? "Submissions are disabled"
: `Save (${OS === "macos" ? "⌘" : "Ctrl"} + Enter)`
: "Save"}
</ActionButton>

<div className="relative">
{numUploading > 0 && (
<span className="absolute -right-2 -top-3 inline-flex items-center rounded-full bg-blue-200 px-2.5 py-0.5 text-xs font-medium text-blue-800">
{numUploaded} / {numUploading}
</span>
)}

<input
type="file"
ref={uploadFile}
accept={Array.from(SUPPORTED_UPLOAD_ASSET_TYPES).join(",")}
multiple
hidden
onChange={(e) => {
if (e.target.files) {
handleFileUpload(Array.from(e.target.files));
}
}}
></input>
<ActionButton
loading={isPending}
variant="default"
onClick={() => uploadFile.current?.click()}
>
<FilePlus width={16} />
</ActionButton>
</div>
</div>

{multiUrlImportState && (
<MultipleChoiceDialog
Expand Down
8 changes: 5 additions & 3 deletions apps/workers/crawlerWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,17 @@ import {
} from "@hoarder/db/schema";
import { DequeuedJob, Runner } from "@hoarder/queue";
import {
ASSET_TYPES,
deleteAsset,
getAssetSize,
IMAGE_ASSET_TYPES,
newAssetId,
saveAsset,
saveAssetFromFile,
SUPPORTED_UPLOAD_ASSET_TYPES,
} from "@hoarder/shared/assetdb";
import {
ASSET_TYPES,
IMAGE_ASSET_TYPES,
SUPPORTED_UPLOAD_ASSET_TYPES,
} from "@hoarder/shared/assetTypes";
import serverConfig from "@hoarder/shared/config";
import logger from "@hoarder/shared/logger";
import {
Expand Down
29 changes: 29 additions & 0 deletions packages/shared/assetTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const enum ASSET_TYPES {
IMAGE_JPEG = "image/jpeg",
IMAGE_PNG = "image/png",
IMAGE_WEBP = "image/webp",
APPLICATION_PDF = "application/pdf",
TEXT_HTML = "text/html",
}

export const IMAGE_ASSET_TYPES: Set<string> = new Set<string>([
ASSET_TYPES.IMAGE_JPEG,
ASSET_TYPES.IMAGE_PNG,
ASSET_TYPES.IMAGE_WEBP,
]);

export const FILE_ASSET_TYPES: Set<string> = new Set<string>([
ASSET_TYPES.APPLICATION_PDF,
]);

// The assets that we allow the users to upload
export const SUPPORTED_UPLOAD_ASSET_TYPES: Set<string> = new Set<string>([
...IMAGE_ASSET_TYPES,
...FILE_ASSET_TYPES,
]);

// The assets that we support saving in the asset db
export const SUPPORTED_ASSET_TYPES: Set<string> = new Set<string>([
...SUPPORTED_UPLOAD_ASSET_TYPES,
ASSET_TYPES.TEXT_HTML,
]);
27 changes: 1 addition & 26 deletions packages/shared/assetdb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,11 @@ import * as path from "path";
import { Glob } from "glob";
import { z } from "zod";

import { SUPPORTED_ASSET_TYPES } from "./assetTypes";
import serverConfig from "./config";

const ROOT_PATH = path.join(serverConfig.dataDir, "assets");

export const enum ASSET_TYPES {
IMAGE_JPEG = "image/jpeg",
IMAGE_PNG = "image/png",
IMAGE_WEBP = "image/webp",
APPLICATION_PDF = "application/pdf",
TEXT_HTML = "text/html",
}

export const IMAGE_ASSET_TYPES: Set<string> = new Set<string>([
ASSET_TYPES.IMAGE_JPEG,
ASSET_TYPES.IMAGE_PNG,
ASSET_TYPES.IMAGE_WEBP,
]);

// The assets that we allow the users to upload
export const SUPPORTED_UPLOAD_ASSET_TYPES: Set<string> = new Set<string>([
...IMAGE_ASSET_TYPES,
ASSET_TYPES.APPLICATION_PDF,
]);

// The assets that we support saving in the asset db
export const SUPPORTED_ASSET_TYPES: Set<string> = new Set<string>([
...SUPPORTED_UPLOAD_ASSET_TYPES,
ASSET_TYPES.TEXT_HTML,
]);

function getAssetDir(userId: string, assetId: string) {
return path.join(ROOT_PATH, userId, assetId);
}
Expand Down