Skip to content

Commit

Permalink
renaming files work (still not as i like it)
Browse files Browse the repository at this point in the history
  • Loading branch information
slayernominee committed Jan 7, 2024
1 parent eeb59e0 commit 8cae541
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 104 deletions.
215 changes: 115 additions & 100 deletions app/dashboard/files/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,115 +4,130 @@ import { ColumnDef } from "@tanstack/react-table"
import { ArrowUpDown, MoreHorizontal } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { useRef } from "react"

// This type is used to define the shape of our data.
// You can use a Zod schema here if you want.
export type File = {
name: string,
type: "file" | "folder",
modified: number
name: string,
type: "file" | "folder",
modified: number
}

export const columns: ColumnDef<File>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox

export function getColumns(editableRowId: number, saveNewFileName: any) {

const editableKeyUpHandler = async (e: any) => {
e.preventDefault()
if (e.key == 'Enter') {
e.preventDefault()
saveNewFileName(e.target.innerText)
}
}

const columns: ColumnDef<File>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && "indeterminate")
}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: "name",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Name
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
cell: ({ row }) => {
const name: string = row.getValue("name")

return (
<div className="cursor-pointer h-full w-full">{ name }</div>
)
},

},
{
accessorKey: "type",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Type
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
}
},
{
accessorKey: "modified",
header: ({ column }) => {
return (
<div className="text-right">
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Modified
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
</div>
)
},
cell: ({ row }) => {
const modified = parseFloat(row.getValue("modified"))

let formatted: number | string = 0

if (modified >= 365 * 24 * 60 * 60 * 1000) { // 365d
formatted = Math.round(modified / 1000 / 60 / 60 / 24 / 365)
formatted = `${formatted}y`
} else if (modified >= 7 * 24 * 60 * 60 * 1000) { // 365d
formatted = Math.round(modified / 1000 / 60 / 60 / 24 / 7)
formatted = `${formatted}w`
} else if (modified >= 24 * 60 * 60 * 1000) { // 24h
formatted = Math.round(modified / 1000 / 60 / 60 / 24)
formatted = `${formatted}d`
} else if (modified >= 60 * 60 * 1000) { // 60min
formatted = Math.round(modified / 1000 / 60 / 60)
formatted = `${formatted}h`
} else if (modified >= 60 * 1000) { // 60s
formatted = Math.round(modified / 1000 / 60)
formatted = `${formatted}m`
} else {
formatted = Math.round(modified / 1000)
formatted = `${formatted}s`
}

return <div className="font-medium text-right pr-6">{formatted} ago</div>
},
},
]
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: "name",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Name
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
cell: ({ row }) => {
const name: string = row.getValue("name")

return (
<span className={`cursor-pointer w-full outline-none ${editableRowId == row.index ? 'border-b border-gray-500 text-gray-700' : ''} py-1 px-3`} onKeyUp={editableKeyUpHandler} onClick={(e) => {if (editableRowId == row.index) {e.preventDefault()} }} contentEditable={editableRowId == row.index}>{ name }</span>
)
},

},
{
accessorKey: "type",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Type
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
}
},
{
accessorKey: "modified",
header: ({ column }) => {
return (
<div className="text-right">
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Modified
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
</div>
)
},
cell: ({ row }) => {
const modified = parseFloat(row.getValue("modified"))

let formatted: number | string = 0

if (modified >= 365 * 24 * 60 * 60 * 1000) { // 365d
formatted = Math.round(modified / 1000 / 60 / 60 / 24 / 365)
formatted = `${formatted}y`
} else if (modified >= 7 * 24 * 60 * 60 * 1000) { // 365d
formatted = Math.round(modified / 1000 / 60 / 60 / 24 / 7)
formatted = `${formatted}w`
} else if (modified >= 24 * 60 * 60 * 1000) { // 24h
formatted = Math.round(modified / 1000 / 60 / 60 / 24)
formatted = `${formatted}d`
} else if (modified >= 60 * 60 * 1000) { // 60min
formatted = Math.round(modified / 1000 / 60 / 60)
formatted = `${formatted}h`
} else if (modified >= 60 * 1000) { // 60s
formatted = Math.round(modified / 1000 / 60)
formatted = `${formatted}m`
} else {
formatted = Math.round(modified / 1000)
formatted = `${formatted}s`
}

return <div className="font-medium text-right pr-6">{formatted} ago</div>
},
},
]

return columns
}
48 changes: 44 additions & 4 deletions app/dashboard/files/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button"
import Icon from '@mdi/react';
import { mdiDeleteOutline, mdiChevronUp, mdiFolderMoveOutline, mdiHomeOutline, mdiPencilOutline, mdiReload, mdiCloudDownloadOutline, mdiCloudUploadOutline, mdiContentCopy } from '@mdi/js';

import { File, columns } from "@/app/dashboard/files/columns"
import { File, getColumns } from "@/app/dashboard/files/columns"
import { DataTable } from "@/app/dashboard/files/data-table"

async function getData(path: string): Promise<File[]> {
Expand Down Expand Up @@ -35,26 +35,57 @@ async function deleteFile(path: string) {
return res
}



async function renameFile(oldName: string, newName: string) {
await fetch(process.env.NEXT_PUBLIC_API_URL + '/api/rename/' + oldName + "/" + newName, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
},
})
}

export default function Home() {
const [data, setData] = useState<File[]>([])
const [path, setPath] = useState("&.")
const [selected, setSelected] = useState<File[]>([])
const [columns, setColumns] = useState<any[]>([])

const refresh = async () => {
const res = await getData(path)
setData(res)
}

const saveNewFileName = async (newName: string) => {
newName = newName.replaceAll("/", "&")
newName = newName.replaceAll(".", "&.")
newName = path + "&" + newName
newName = newName.replace("\n", "")
let oldName = selected[0].getValue("name")
oldName.replaceAll("/", "&")
oldName.replaceAll(".", "&.")
oldName = path + "&" + oldName

renameFile(oldName, newName)

setColumns(getColumns(-1, saveNewFileName))

setSelected([])

setTimeout(async () => {
const res = await getData(path)
setData(res)
}, 20)
}

useEffect(() => {
setColumns(getColumns(-1, saveNewFileName))
refresh()
}, [])

const goToHome = async () => {
setPath("&.")
const res = await getData("&.")
setColumns(getColumns(-1, saveNewFileName))
setData(res)
}

Expand All @@ -64,6 +95,7 @@ export default function Home() {
const newPath = pathArr.join("&")
setPath(newPath)
const res = await getData(newPath)
setColumns(getColumns(-1, saveNewFileName))
setData(res)
}

Expand All @@ -78,6 +110,7 @@ export default function Home() {
// go in the directory
setPath(path + "&" + cell.getContext().row.original.name)
const res = await getData(path + "&" + cell.getContext().row.original.name)
setColumns(getColumns(-1, saveNewFileName))
setData(res)
} else {
// open the file? or download it?
Expand Down Expand Up @@ -111,6 +144,12 @@ export default function Home() {
setSelected([])
refresh()
}

const renameSelected = async () => {
let row_index = selected[0].id
setColumns(getColumns(row_index, saveNewFileName))
refresh()
}

const addToFileUpload = async (e: any) => {
e.preventDefault();
Expand Down Expand Up @@ -138,6 +177,7 @@ export default function Home() {
e.stopPropagation();
e.preventDefault();
}


return (
<Layout>
Expand Down Expand Up @@ -166,7 +206,7 @@ export default function Home() {
</Button>
<input className="hidden" id="fileUpload" type="file" multiple onChange={uploadFiles} />

<Button variant="ghost" disabled={selected.length != 1}><Icon path={mdiPencilOutline} size={1} /></Button>
<Button variant="ghost" disabled={selected.length != 1} onClick={renameSelected}><Icon path={mdiPencilOutline} size={1} /></Button>
<Button variant="ghost" disabled={selected.length == 0}><Icon path={mdiFolderMoveOutline} size={1} /></Button>
<Button variant="ghost" disabled={selected.length == 0}><Icon path={mdiContentCopy} size={1} /></Button>
<Button variant="ghost" disabled={selected.length == 0} onClick={deleteSelected}><Icon path={mdiDeleteOutline} size={1} /></Button>
Expand Down
35 changes: 35 additions & 0 deletions dash/src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,41 @@ async fn upload(MultipartForm(form): MultipartForm<UploadForm>, path: web::Path<
HttpResponse::Ok().body("Uploaded!")
}

#[post("/rename/{pathname}/{new_name}")]
pub async fn rename(path: web::Path<(String, String)>) -> impl Responder {
let (mut pathname, mut new_name) = path.into_inner();

/*if pathname.contains("..") || pathname.contains("~") || pathname.starts_with("-") || pathname.contains("//") || pathname.contains("\\") || pathname.contains(":") || pathname.contains("*") || pathname.contains("?") || pathname.contains("\"") || pathname.contains("<") || pathname.contains(">") || pathname.contains("|") {
return Ok(HttpResponse::BadRequest().body("Invalid Pathname!"));
}*/
// theoretically but since this only works with tokens the user can be trusted ...

pathname = pathname.replace("&.", ".");
pathname = pathname.replace("&", "/");

new_name = new_name.replace("&.", ".");
new_name = new_name.replace("&", "/");

let path = Path::new(&pathname);

if !path.exists() {
HttpResponse::BadRequest().body("File or Directory does not exist!");
} else if pathname == "." || pathname == "/" {
return HttpResponse::BadRequest().body("Server Direcotry!");
}

let new_path = Path::new(&new_name);

if new_path.exists() {
return HttpResponse::BadRequest().body("File or Directory already exists!");
}

std::fs::rename(path, new_path).unwrap();

HttpResponse::Ok().body(format!("Renamed {} to {}!", pathname, new_name))
}



#[derive(Serialize, Debug)]
struct FileInfo {
Expand Down
Loading

0 comments on commit 8cae541

Please sign in to comment.