the big refactor 2: ObjID

This commit is contained in:
Djeeberjr 2021-09-28 16:07:00 +02:00
parent dc4133a705
commit 3e8fbb01bb
22 changed files with 1096 additions and 952 deletions

View File

@ -10,7 +10,10 @@ generates:
- "typescript" - "typescript"
- "typescript-operations" - "typescript-operations"
- "typescript-react-apollo" - "typescript-react-apollo"
- add:
content: "import ObjID from './../types/ObjID'"
config: config:
withHooks: true withHooks: true
scalars: scalars:
DateTime: string DateTime: string
objID: ObjID

View File

@ -41,6 +41,7 @@
}, },
"devDependencies": { "devDependencies": {
"@craco/craco": "^6.2.0", "@craco/craco": "^6.2.0",
"@graphql-codegen/add": "^3.1.0",
"@graphql-codegen/cli": "1.21.7", "@graphql-codegen/cli": "1.21.7",
"@graphql-codegen/introspection": "1.18.2", "@graphql-codegen/introspection": "1.18.2",
"@graphql-codegen/typescript": "^1.23.0", "@graphql-codegen/typescript": "^1.23.0",

View File

@ -1,25 +1,36 @@
import React from "react" import React from "react"
import ObjID from "../types/ObjID"
import { ReactComponent as BreadcrumImage } from "./../assets/breadcrum.svg" import { ReactComponent as BreadcrumImage } from "./../assets/breadcrum.svg"
interface Props{ interface Props{
path: string path: ObjID
onDirClick?: (path: string) => void onDirClick?: (path: ObjID) => void
} }
const Breadcrum: React.FC<Props> = (props) => { const Breadcrum: React.FC<Props> = (props) => {
const parts = props.path.split("/").filter(e=>e.length > 0) const keyParts = props.path.key.split("/").filter(e=>e.length > 0)
return ( return (
<ul className="flex text-gray-500 dark:text-gray-400 text-lg"> <ul className="flex text-gray-500 dark:text-gray-400 text-lg ">
<li className="inline-flex items-center cursor-pointer"> <div className="inline-flex items-center cursor-pointer">
<a onClick={()=>{ <li>
props.onDirClick?.("/") <a>
}}> {props.path.bucket}
Root
</a> </a>
</li> </li>
</div>
<div className="inline-flex items-center cursor-pointer">
<BreadcrumImage className="h-5 w-auto text-gray-400" />
<li>
<a onClick={()=>{
props.onDirClick?.(new ObjID(props.path.bucket,"/"))
}}>
root
</a>
</li>
</div>
{parts.map((e,i,arr)=>{ {keyParts.map((e,i,arr)=>{
const last = i == arr.length - 1 const last = i == arr.length - 1
return <div key={e} className="inline-flex items-center cursor-pointer"> return <div key={e} className="inline-flex items-center cursor-pointer">
<BreadcrumImage className="h-5 w-auto text-gray-400" /> <BreadcrumImage className="h-5 w-auto text-gray-400" />
@ -28,7 +39,7 @@ const Breadcrum: React.FC<Props> = (props) => {
className={`${last?"text-blue-500":""}`} className={`${last?"text-blue-500":""}`}
onClick={()=>{ onClick={()=>{
if (!last){ if (!last){
props.onDirClick?.("/"+arr.slice(0,i-1).join("/")) props.onDirClick?.(new ObjID(props.path.bucket,"/"+arr.slice(0,i-1).join("/")))
} }
}}>{e}</a> }}>{e}</a>
</li> </li>

View File

@ -4,7 +4,6 @@ import { useContextMenu } from "react-contexify"
import { RouteComponentProps } from "react-router-dom" import { RouteComponentProps } from "react-router-dom"
import downloadFile from "../functions/downloadFile" import downloadFile from "../functions/downloadFile"
import genDownloadLink from "../functions/genDownloadLink" import genDownloadLink from "../functions/genDownloadLink"
import normalizeDirPath from "../functions/normalizeDirPath"
import uploadFile from "../functions/uploadFile" import uploadFile from "../functions/uploadFile"
import { useCopyMutation, useCreateDirMutation, useDeleteDirMutation, useDeleteFileMutation, useMoveMutation, useOpenDirQuery } from "../generated/graphql" import { useCopyMutation, useCreateDirMutation, useDeleteDirMutation, useDeleteFileMutation, useMoveMutation, useOpenDirQuery } from "../generated/graphql"
import Breadcrum from "./Breadcrum" import Breadcrum from "./Breadcrum"
@ -15,31 +14,17 @@ import FileOpen from "./FileOpen"
import FileUploadButton from "./FileUploadButton" import FileUploadButton from "./FileUploadButton"
import { ReactComponent as Spinner } from "./../assets/spinner.svg" import { ReactComponent as Spinner } from "./../assets/spinner.svg"
import FileBrowserList from "./FileBrowserList" import FileBrowserList from "./FileBrowserList"
import pathRename from "../functions/pathRename"
import MoreMenu from "./MoreMenu" import MoreMenu from "./MoreMenu"
import ObjID from "../types/ObjID"
function uriToPath(pathname:string) {
// strip the "/f" from e.g. "/f/dir1/dir2"
const path = pathname.substr(2)
if (!path.endsWith("/")){
return path + "/"
}
return path
}
function pathToUri(path:string) {
return (path.startsWith("/")?"/f":"/f/") + path
}
const FileBrowser: React.FC<RouteComponentProps> = (props) => { const FileBrowser: React.FC<RouteComponentProps> = (props) => {
const path = uriToPath(props.location.pathname) const path = ObjID.fromURI(props.location.pathname)
const [openFileId, setOpenFileId] = useState<ObjID>()
const [openFileId, setOpenFileId] = useState("")
const [showFile, setShowFile] = useState(false) const [showFile, setShowFile] = useState(false)
const [srcID,setSrcID] = useState("") const [srcID,setSrcID] = useState<ObjID>(path)
const [pasteAction,setPasteAction] = useState<Action>() const [pasteAction,setPasteAction] = useState<Action>()
const [editID,setEditID] = useState("") const [editID,setEditID] = useState<ObjID>(path)
const [editEnable,setEditEnable] = useState(false) const [editEnable,setEditEnable] = useState(false)
const [deleteMutation] = useDeleteFileMutation() const [deleteMutation] = useDeleteFileMutation()
@ -58,7 +43,7 @@ const FileBrowser: React.FC<RouteComponentProps> = (props) => {
const { data, loading, refetch: refetchDir } = useOpenDirQuery({ const { data, loading, refetch: refetchDir } = useOpenDirQuery({
variables:{ variables:{
path path: path
} }
}) })
@ -66,14 +51,14 @@ const FileBrowser: React.FC<RouteComponentProps> = (props) => {
const wait: Promise<boolean>[] = [] const wait: Promise<boolean>[] = []
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
const file = files[i] const file = files[i]
wait.push(uploadFile(file, path + file.name)) wait.push(uploadFile(file, new ObjID(path.bucket,path.key + file.name)))
} }
await Promise.all(wait) await Promise.all(wait)
refetchDir() refetchDir()
} }
async function onContextSelect(action:Action, id: string) { async function onContextSelect(action:Action, id: ObjID) {
switch (action) { switch (action) {
case Action.FileDelete: case Action.FileDelete:
await deleteMutation({variables:{id}}) await deleteMutation({variables:{id}})
@ -124,13 +109,13 @@ const FileBrowser: React.FC<RouteComponentProps> = (props) => {
> >
<div className="flex justify-between"> <div className="flex justify-between">
<Breadcrum path={path} onDirClick={(newPath)=>{ <Breadcrum path={path} onDirClick={(newPath)=>{
props.history.push(pathToUri(newPath)) props.history.push(newPath.toURI())
}}/> }}/>
<div className="ml-auto"> <div className="ml-auto">
<CreateDirButton <CreateDirButton
onPressed={async (dirName)=>{ onPressed={async (dirName)=>{
const fullPath = normalizeDirPath(path + dirName) const dirID = new ObjID(path.bucket,path.key + dirName)
await createDirMutation({variables:{path: fullPath}}) await createDirMutation({variables:{path: dirID}})
refetchDir() refetchDir()
}} }}
/> />
@ -147,11 +132,11 @@ const FileBrowser: React.FC<RouteComponentProps> = (props) => {
</div> </div>
<div> <div>
<FileBrowserList <FileBrowserList
directorys={data?.directorys || []} directorys={data?.directories || []}
files={data?.files || []} files={data?.files || []}
onDirClick={(e,path)=>{ onDirClick={(e,path)=>{
props.history.push(pathToUri(path)) props.history.push(path.toURI())
}} }}
onDirContext={(e,path)=>{ onDirContext={(e,path)=>{
@ -179,7 +164,7 @@ const FileBrowser: React.FC<RouteComponentProps> = (props) => {
// TODO: input check & error handling // TODO: input check & error handling
await moveMutation({variables:{ await moveMutation({variables:{
src:id, src:id,
dest: pathRename(id,newName) dest: id.rename(newName)
}}) }})
refetchDir() refetchDir()
} }

View File

@ -1,6 +1,6 @@
import React from "react" import React from "react"
import { Item, ItemParams, Menu, Separator } from "react-contexify" import { Item, ItemParams, Menu, Separator } from "react-contexify"
import ObjID from "../types/ObjID"
export const CONTEXT_MENU_FILE = "CONTEXT_MENU_FILE" export const CONTEXT_MENU_FILE = "CONTEXT_MENU_FILE"
export const CONTEXT_MENU_DIR = "CONTEXT_MENU_DIR" export const CONTEXT_MENU_DIR = "CONTEXT_MENU_DIR"
@ -16,12 +16,12 @@ export enum Action {
} }
interface Props { interface Props {
onSelect?: (action: Action, id: string)=>void onSelect?: (action: Action, id: ObjID)=>void
pasteActive?: boolean pasteActive?: boolean
} }
const FileBrowserContextMenu: React.FC<Props> = (props) => { const FileBrowserContextMenu: React.FC<Props> = (props) => {
function onClick({ props: itemProps, data }: ItemParams<{id:string}, Action>) { function onClick({ props: itemProps, data }: ItemParams<{id:ObjID}, Action>) {
if (itemProps?.id && data != null){ if (itemProps?.id && data != null){
props.onSelect?.(data,itemProps.id) props.onSelect?.(data,itemProps.id)
} }

View File

@ -1,19 +1,20 @@
import React from "react" import React from "react"
import { Directory, File } from "../generated/graphql" import { Directory, File } from "../generated/graphql"
import ObjID from "../types/ObjID"
import FileBrowserElement from "./FileBrowserElement" import FileBrowserElement from "./FileBrowserElement"
interface Props{ interface Props{
directorys: Directory[] directorys: Directory[]
files: File[] files: File[]
onFileContext?: (event: React.MouseEvent, id: string)=>void onFileContext?: (event: React.MouseEvent, id: ObjID)=>void
onDirContext?: (event: React.MouseEvent, path: string)=>void onDirContext?: (event: React.MouseEvent, path: ObjID)=>void
onFileClick?: (event: React.MouseEvent,id: string)=>void onFileClick?: (event: React.MouseEvent,id: ObjID)=>void
onDirClick?: (event: React.MouseEvent,path: string)=>void onDirClick?: (event: React.MouseEvent,path: ObjID)=>void
editId: string editId?: ObjID
editEnable: boolean editEnable: boolean
onRenameDone?: (id: string, changed: boolean, newName: string)=>void onRenameDone?: (id: ObjID, changed: boolean, newName: string)=>void
} }
const FileBrowserList: React.FC<Props> = (props) => { const FileBrowserList: React.FC<Props> = (props) => {
@ -28,7 +29,7 @@ const FileBrowserList: React.FC<Props> = (props) => {
</thead> </thead>
<tbody className="divide-y dark:divide-gray-900"> <tbody className="divide-y dark:divide-gray-900">
{ props.directorys.map(v => (<FileBrowserElement { props.directorys.map(v => (<FileBrowserElement
key={v.id} key={v.id.toString()}
dir={v} dir={v}
onClick={(e,dir)=>{ onClick={(e,dir)=>{
props.onDirClick?.(e,dir.id) props.onDirClick?.(e,dir.id)
@ -51,7 +52,7 @@ const FileBrowserList: React.FC<Props> = (props) => {
/>))} />))}
{ props.files.map(v => (<FileBrowserElement { props.files.map(v => (<FileBrowserElement
key={v.id} key={v.id.toString()}
file={v} file={v}
onClick={(e,file)=>{ onClick={(e,file)=>{
props.onFileClick?.(e,file.id) props.onFileClick?.(e,file.id)

View File

@ -4,9 +4,10 @@ import ImageOpener from "./ImageOpener"
import TextOpener from "./TextOpener" import TextOpener from "./TextOpener"
import Modal from "./Modal" import Modal from "./Modal"
import AudioOpener from "./AudioOpener" import AudioOpener from "./AudioOpener"
import ObjID from "../types/ObjID"
interface Props { interface Props {
id: string id?: ObjID
show: boolean show: boolean
onCloseClick?: ()=>void onCloseClick?: ()=>void
} }

View File

@ -1,5 +1,7 @@
function genDownloadLink(id:string): string { import ObjID from "../types/ObjID"
return `/api/file?id=${encodeURIComponent(id)}`
function genDownloadLink(id:ObjID): string {
return `/api/file?id=${encodeURIComponent(id.toString())}`
} }
export default genDownloadLink export default genDownloadLink

View File

@ -1,11 +0,0 @@
function pathRename(id:string, newFilename: string): string {
const isDir = id.endsWith("/")
const parts = id.split("/")
if (!parts.length)
throw new Error("Maleformed id")
parts[parts.length - (isDir?2:1)] = newFilename
return parts.join("/")
}
export default pathRename

View File

@ -1,5 +1,7 @@
async function uploadFile(file:File,id: string): Promise<boolean> { import ObjID from "../types/ObjID"
const res = await fetch(`/api/file?${new URLSearchParams({id:id}).toString()}`,{
async function uploadFile(file:File,id: ObjID): Promise<boolean> {
const res = await fetch(`/api/file?${new URLSearchParams({id:id.toString()}).toString()}`,{
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": file.type "Content-Type": file.type

View File

@ -1,3 +1,4 @@
import ObjID from "./../types/ObjID"
import { gql } from "@apollo/client" import { gql } from "@apollo/client"
import * as Apollo from "@apollo/client" import * as Apollo from "@apollo/client"
export type Maybe<T> = T | null; export type Maybe<T> = T | null;
@ -14,6 +15,11 @@ export type Scalars = {
Float: number; Float: number;
/** DateTime is a DateTime in ISO 8601 format */ /** DateTime is a DateTime in ISO 8601 format */
DateTime: string; DateTime: string;
/**
* String representing a bucket, key and version combination.
* Looks like this: "bucketName:/name/of/key" or "bucketName@version:/name/of/key"
*/
objID: ObjID;
}; };
@ -22,7 +28,7 @@ export type Directory = {
__typename?: "Directory"; __typename?: "Directory";
directorys?: Maybe<Array<Maybe<Directory>>>; directorys?: Maybe<Array<Maybe<Directory>>>;
files?: Maybe<Array<Maybe<File>>>; files?: Maybe<Array<Maybe<File>>>;
id: Scalars["ID"]; id: Scalars["objID"];
name?: Maybe<Scalars["String"]>; name?: Maybe<Scalars["String"]>;
parent?: Maybe<Directory>; parent?: Maybe<Directory>;
}; };
@ -33,7 +39,7 @@ export type File = {
contentType?: Maybe<Scalars["String"]>; contentType?: Maybe<Scalars["String"]>;
etag?: Maybe<Scalars["String"]>; etag?: Maybe<Scalars["String"]>;
/** The uniqe ID of the file. Represents the path and the s3 key. */ /** The uniqe ID of the file. Represents the path and the s3 key. */
id: Scalars["ID"]; id: Scalars["objID"];
lastModified?: Maybe<Scalars["DateTime"]>; lastModified?: Maybe<Scalars["DateTime"]>;
name?: Maybe<Scalars["String"]>; name?: Maybe<Scalars["String"]>;
parent?: Maybe<Directory>; parent?: Maybe<Directory>;
@ -61,64 +67,65 @@ export type RootMutation = {
export type RootMutationCopyArgs = { export type RootMutationCopyArgs = {
src: Scalars["ID"]; src: Scalars["objID"];
dest: Scalars["ID"]; dest: Scalars["objID"];
}; };
export type RootMutationCreateDirArgs = { export type RootMutationCreateDirArgs = {
path: Scalars["ID"]; path: Scalars["objID"];
}; };
export type RootMutationDeleteArgs = { export type RootMutationDeleteArgs = {
id: Scalars["ID"]; id: Scalars["objID"];
}; };
export type RootMutationDeleteDirArgs = { export type RootMutationDeleteDirArgs = {
path: Scalars["ID"]; path: Scalars["objID"];
}; };
export type RootMutationLoginArgs = { export type RootMutationLoginArgs = {
password: Scalars["String"];
username: Scalars["String"]; username: Scalars["String"];
password: Scalars["String"];
}; };
export type RootMutationMoveArgs = { export type RootMutationMoveArgs = {
src: Scalars["ID"]; src: Scalars["objID"];
dest: Scalars["ID"]; dest: Scalars["objID"];
}; };
export type RootQuery = { export type RootQuery = {
__typename?: "RootQuery"; __typename?: "RootQuery";
/** True if the user is authorized */ /** True if the user is authorized */
authorized: Scalars["Boolean"]; authorized: Scalars["Boolean"];
directorys: Array<Directory>; directories: Array<Directory>;
file?: Maybe<File>; file?: Maybe<File>;
files: Array<File>; files: Array<File>;
}; };
export type RootQueryDirectorysArgs = { export type RootQueryDirectoriesArgs = {
path: Scalars["String"]; path: Scalars["objID"];
}; };
export type RootQueryFileArgs = { export type RootQueryFileArgs = {
id: Scalars["ID"]; id: Scalars["objID"];
}; };
export type RootQueryFilesArgs = { export type RootQueryFilesArgs = {
path: Scalars["String"]; path: Scalars["objID"];
}; };
export type CopyMutationVariables = Exact<{ export type CopyMutationVariables = Exact<{
src: Scalars["ID"]; src: Scalars["objID"];
dest: Scalars["ID"]; dest: Scalars["objID"];
}>; }>;
@ -131,7 +138,7 @@ export type CopyMutation = (
); );
export type CreateDirMutationVariables = Exact<{ export type CreateDirMutationVariables = Exact<{
path: Scalars["ID"]; path: Scalars["objID"];
}>; }>;
@ -144,7 +151,7 @@ export type CreateDirMutation = (
); );
export type DeleteDirMutationVariables = Exact<{ export type DeleteDirMutationVariables = Exact<{
path: Scalars["ID"]; path: Scalars["objID"];
}>; }>;
@ -154,7 +161,7 @@ export type DeleteDirMutation = (
); );
export type DeleteFileMutationVariables = Exact<{ export type DeleteFileMutationVariables = Exact<{
id: Scalars["ID"]; id: Scalars["objID"];
}>; }>;
@ -164,7 +171,7 @@ export type DeleteFileMutation = (
); );
export type GetFileQueryVariables = Exact<{ export type GetFileQueryVariables = Exact<{
id: Scalars["ID"]; id: Scalars["objID"];
}>; }>;
@ -199,8 +206,8 @@ export type LoginMutation = (
); );
export type MoveMutationVariables = Exact<{ export type MoveMutationVariables = Exact<{
src: Scalars["ID"]; src: Scalars["objID"];
dest: Scalars["ID"]; dest: Scalars["objID"];
}>; }>;
@ -213,7 +220,7 @@ export type MoveMutation = (
); );
export type OpenDirQueryVariables = Exact<{ export type OpenDirQueryVariables = Exact<{
path: Scalars["String"]; path: Scalars["objID"];
}>; }>;
@ -222,7 +229,7 @@ export type OpenDirQuery = (
& { files: Array<( & { files: Array<(
{ __typename?: "File" } { __typename?: "File" }
& Pick<File, "id" | "name" | "size" | "lastModified"> & Pick<File, "id" | "name" | "size" | "lastModified">
)>, directorys: Array<( )>, directories: Array<(
{ __typename?: "Directory" } { __typename?: "Directory" }
& Pick<Directory, "id" | "name"> & Pick<Directory, "id" | "name">
)> } )> }
@ -230,7 +237,7 @@ export type OpenDirQuery = (
export const CopyDocument = gql` export const CopyDocument = gql`
mutation copy($src: ID!, $dest: ID!) { mutation copy($src: objID!, $dest: objID!) {
copy(src: $src, dest: $dest) { copy(src: $src, dest: $dest) {
id id
} }
@ -264,7 +271,7 @@ export type CopyMutationHookResult = ReturnType<typeof useCopyMutation>;
export type CopyMutationResult = Apollo.MutationResult<CopyMutation>; export type CopyMutationResult = Apollo.MutationResult<CopyMutation>;
export type CopyMutationOptions = Apollo.BaseMutationOptions<CopyMutation, CopyMutationVariables>; export type CopyMutationOptions = Apollo.BaseMutationOptions<CopyMutation, CopyMutationVariables>;
export const CreateDirDocument = gql` export const CreateDirDocument = gql`
mutation createDir($path: ID!) { mutation createDir($path: objID!) {
createDir(path: $path) { createDir(path: $path) {
id id
} }
@ -297,7 +304,7 @@ export type CreateDirMutationHookResult = ReturnType<typeof useCreateDirMutation
export type CreateDirMutationResult = Apollo.MutationResult<CreateDirMutation>; export type CreateDirMutationResult = Apollo.MutationResult<CreateDirMutation>;
export type CreateDirMutationOptions = Apollo.BaseMutationOptions<CreateDirMutation, CreateDirMutationVariables>; export type CreateDirMutationOptions = Apollo.BaseMutationOptions<CreateDirMutation, CreateDirMutationVariables>;
export const DeleteDirDocument = gql` export const DeleteDirDocument = gql`
mutation deleteDir($path: ID!) { mutation deleteDir($path: objID!) {
deleteDir(path: $path) deleteDir(path: $path)
} }
` `
@ -328,7 +335,7 @@ export type DeleteDirMutationHookResult = ReturnType<typeof useDeleteDirMutation
export type DeleteDirMutationResult = Apollo.MutationResult<DeleteDirMutation>; export type DeleteDirMutationResult = Apollo.MutationResult<DeleteDirMutation>;
export type DeleteDirMutationOptions = Apollo.BaseMutationOptions<DeleteDirMutation, DeleteDirMutationVariables>; export type DeleteDirMutationOptions = Apollo.BaseMutationOptions<DeleteDirMutation, DeleteDirMutationVariables>;
export const DeleteFileDocument = gql` export const DeleteFileDocument = gql`
mutation deleteFile($id: ID!) { mutation deleteFile($id: objID!) {
delete(id: $id) delete(id: $id)
} }
` `
@ -359,7 +366,7 @@ export type DeleteFileMutationHookResult = ReturnType<typeof useDeleteFileMutati
export type DeleteFileMutationResult = Apollo.MutationResult<DeleteFileMutation>; export type DeleteFileMutationResult = Apollo.MutationResult<DeleteFileMutation>;
export type DeleteFileMutationOptions = Apollo.BaseMutationOptions<DeleteFileMutation, DeleteFileMutationVariables>; export type DeleteFileMutationOptions = Apollo.BaseMutationOptions<DeleteFileMutation, DeleteFileMutationVariables>;
export const GetFileDocument = gql` export const GetFileDocument = gql`
query getFile($id: ID!) { query getFile($id: objID!) {
file(id: $id) { file(id: $id) {
id id
name name
@ -465,7 +472,7 @@ export type LoginMutationHookResult = ReturnType<typeof useLoginMutation>;
export type LoginMutationResult = Apollo.MutationResult<LoginMutation>; export type LoginMutationResult = Apollo.MutationResult<LoginMutation>;
export type LoginMutationOptions = Apollo.BaseMutationOptions<LoginMutation, LoginMutationVariables>; export type LoginMutationOptions = Apollo.BaseMutationOptions<LoginMutation, LoginMutationVariables>;
export const MoveDocument = gql` export const MoveDocument = gql`
mutation move($src: ID!, $dest: ID!) { mutation move($src: objID!, $dest: objID!) {
move(src: $src, dest: $dest) { move(src: $src, dest: $dest) {
id id
} }
@ -499,14 +506,14 @@ export type MoveMutationHookResult = ReturnType<typeof useMoveMutation>;
export type MoveMutationResult = Apollo.MutationResult<MoveMutation>; export type MoveMutationResult = Apollo.MutationResult<MoveMutation>;
export type MoveMutationOptions = Apollo.BaseMutationOptions<MoveMutation, MoveMutationVariables>; export type MoveMutationOptions = Apollo.BaseMutationOptions<MoveMutation, MoveMutationVariables>;
export const OpenDirDocument = gql` export const OpenDirDocument = gql`
query openDir($path: String!) { query openDir($path: objID!) {
files(path: $path) { files(path: $path) {
id id
name name
size size
lastModified lastModified
} }
directorys(path: $path) { directories(path: $path) {
id id
name name
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
mutation copy($src: ID!, $dest: ID!) { mutation copy($src: objID!, $dest: objID!) {
copy(src: $src,dest: $dest){ copy(src: $src,dest: $dest){
id id
} }

View File

@ -1,4 +1,4 @@
mutation createDir($path: ID!){ mutation createDir($path: objID!){
createDir(path:$path){ createDir(path:$path){
id id
} }

View File

@ -1,3 +1,3 @@
mutation deleteDir($path: ID!){ mutation deleteDir($path: objID!){
deleteDir(path:$path) deleteDir(path:$path)
} }

View File

@ -1,3 +1,3 @@
mutation deleteFile($id: ID!) { mutation deleteFile($id: objID!) {
delete(id:$id) delete(id:$id)
} }

View File

@ -1,4 +1,4 @@
query getFile($id: ID!){ query getFile($id: objID!){
file(id: $id){ file(id: $id){
id id
name name

View File

@ -1,4 +1,4 @@
mutation move($src: ID!, $dest: ID!) { mutation move($src: objID!, $dest: objID!) {
move(src: $src,dest: $dest){ move(src: $src,dest: $dest){
id id
} }

View File

@ -1,11 +1,11 @@
query openDir($path: String!) { query openDir($path: objID!) {
files(path:$path){ files(path:$path){
id id
name name
size size
lastModified lastModified
} }
directorys(path: $path){ directories(path: $path){
id id
name name
} }

View File

@ -4,11 +4,44 @@ import "./index.scss"
import App from "./App" import App from "./App"
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client" import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client"
import "react-contexify/dist/ReactContexify.css" import "react-contexify/dist/ReactContexify.css"
import ObjID from "./types/ObjID"
const client = new ApolloClient({ const client = new ApolloClient({
uri: "/api/graphql", uri: "/api/graphql",
cache: new InMemoryCache({ cache: new InMemoryCache({
typePolicies:{ typePolicies:{
File:{
fields:{
id:{
merge(_,incomming){
// HACK: i use the merge function to change the id from a string to ObjID object.
// afaik apollo does not yet support custom scalar types.
if (!incomming){
return incomming
}else if (incomming instanceof ObjID){
return incomming
}else{
return ObjID.fromString(incomming as string)
}
}
}
}
},
Directory:{
fields:{
id:{
merge(_,incomming){
if (!incomming){
return incomming
}else if (incomming instanceof ObjID){
return incomming
}else{
return ObjID.fromString(incomming as string)
}
}
}
}
},
Query: { Query: {
fields: { fields: {
files: { files: {

82
src/types/ObjID.ts Normal file
View File

@ -0,0 +1,82 @@
const stringRegex = /(.*?)(@(.*))?:(.*)/
class ObjID {
public bucket: string
public key: string
public version?: string
constructor(bucket: string, key: string, version?: string) {
this.bucket = bucket
if(!key){
this.key = "/"
}else{
this.key = key
}
if (version){
this.version = version
}
this.normalize()
}
public toString(): string {
if (this.version) {
return `${this.bucket}@${this.version}:${this.key}`
}else{
return `${this.bucket}:${this.key}`
}
}
public normalize(): void{
if (!this.key.startsWith("/")){
this.key = "/" + this.key
}
}
public isDirectory(): boolean {
return this.key.endsWith("/")
}
public toURI(): string {
return `/f/${this.bucket}${this.key}`
}
public toJSON(): string{
// HACK: toJSON is required so that apollo can parse the ObjID back to a string
// that can be used in gql query
return this.toString()
}
public rename(name: string): ObjID{
const parts = this.key.split("/")
parts[parts.length - (this.isDirectory()?2:1)] = name
return new ObjID(this.bucket,parts.join("/"))
}
public static fromString(from: string): ObjID{
const match = stringRegex.exec(from)
if (!match){
throw new Error("Failed to parse ObjID")
}
return new ObjID(match[1],match[4],match[3])
}
public static fromURI(uri: string): ObjID{
let uri2 = uri
if (!uri2.endsWith("/")){
uri2 += "/"
}
const parts = uri2.split("/")
const bucket = parts[2]
const key = parts.slice(3).join("/")
return new ObjID(bucket,key)
}
}
export default ObjID

View File

@ -1392,6 +1392,14 @@
minimatch "^3.0.4" minimatch "^3.0.4"
strip-json-comments "^3.1.1" strip-json-comments "^3.1.1"
"@graphql-codegen/add@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.1.0.tgz#cd02fd6d80a7f62839cb27160b62e48366a237c5"
integrity sha512-vRRHpuUFadYXnPrb5RNiIzm3Ao39UxjvrdX760lEfXh2qeG7YddM5QFC+ev2BH3X452R15gv/gf/rXy0+Hqm1A==
dependencies:
"@graphql-codegen/plugin-helpers" "^2.1.0"
tslib "~2.3.0"
"@graphql-codegen/cli@1.21.7": "@graphql-codegen/cli@1.21.7":
version "1.21.7" version "1.21.7"
resolved "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-1.21.7.tgz" resolved "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-1.21.7.tgz"
@ -1466,6 +1474,18 @@
lodash "~4.17.0" lodash "~4.17.0"
tslib "~2.3.0" tslib "~2.3.0"
"@graphql-codegen/plugin-helpers@^2.1.0":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.1.1.tgz#fc13e735763574ef308045bbb95c3e7201ec0027"
integrity sha512-7jjN9fekMQkpd7cRTbaBxgqt/hkR3CXeOUSsEyHFDDHKtvCrnev3iyc75IeWXpO9tOwDE8mVPTzEZnu4QukrNA==
dependencies:
"@graphql-tools/utils" "^8.1.1"
change-case-all "1.0.14"
common-tags "1.8.0"
import-from "4.0.0"
lodash "~4.17.0"
tslib "~2.3.0"
"@graphql-codegen/typescript-operations@1.18.4": "@graphql-codegen/typescript-operations@1.18.4":
version "1.18.4" version "1.18.4"
resolved "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-1.18.4.tgz" resolved "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-1.18.4.tgz"
@ -1720,6 +1740,13 @@
camel-case "4.1.2" camel-case "4.1.2"
tslib "~2.2.0" tslib "~2.2.0"
"@graphql-tools/utils@^8.1.1":
version "8.2.3"
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.2.3.tgz#9d7b9e7e116d11d26c2687f4d9cfb2b54568838b"
integrity sha512-RR+aiusf2gIfnPmrDIH1uA45QuPiHB54RD+BmWyMcl88tWAjeJtqZeWPqUTq/1EXrNeocJAJQqogHV4Fbbzx3A==
dependencies:
tslib "~2.3.0"
"@graphql-tools/wrap@^7.0.4": "@graphql-tools/wrap@^7.0.4":
version "7.0.8" version "7.0.8"
resolved "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-7.0.8.tgz" resolved "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-7.0.8.tgz"