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

FilesList: add download all button #105

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
301 changes: 134 additions & 167 deletions src/components/Dashboard/FilesList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from 'react';
import Link from 'next/link';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import Link from 'next/link';
dayjs.extend(relativeTime);

import firebase from 'firebase/app';
import download from '../../scripts/download';
import { extractJavaFilename } from '../../scripts/judge';

export type File = {
id: string;
Expand All @@ -26,10 +27,9 @@ export interface FilesListProps {
showPerms: boolean;
}

import { permissionLabels } from '../UserList/UserListItem';
import { useAtomValue } from 'jotai/utils';
import invariant from 'tiny-invariant';
import { useUserContext } from '../../context/UserContext';
import { permissionLabels } from '../UserList/UserListItem';

export const sharingPermissionLabels: Record<string, string> = {
READ_WRITE: 'Public Read & Write',
Expand Down Expand Up @@ -60,172 +60,139 @@ export default function FilesList(props: FilesListProps): JSX.Element {
ref.update({ hidden: !file.hidden });
};

const downloadAll = (files: File[]) => {
files.forEach(file => {
firebase
.database()
.ref('files/' + file.id)
.get()
.then((snap: firebase.database.DataSnapshot) => {
const fileData = snap.val();
console.log('fileData:');
console.log(fileData);
const code = 'TODO'; // extract from fileData?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: get code from YJS? idk how YJS works @thecodingwizard

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for legacy IDE, would want to iterate over editor-[lang] for each lang and obtain the code for each of them

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For YJS:

If we wanted to be really fancy, we could even make the YJS server API handler return a ZIP file containing all of that users' files, so we only have to make one API request to get all the files.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Firepad:

https://firepad.io/docs/#headless is probably what we want.

const fileNames = {
cpp: `${fileData.settings.workspaceName}.cpp`,
java: extractJavaFilename(code),
py: `${fileData.settings.workspaceName}.py`,
};
// @ts-ignore
download(fileNames[fileData.settings.language], code);
})
.catch(error => {
console.error(error);
});
});
};

const downloadAllButton = (
<div className="flex items-center space-x-4">
<button
className="inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-[#1E1E1E] focus:ring-indigo-500"
onClick={() => downloadAll(props.files)}
>
Download All Files
</button>
</div>
);

return (
<div className="flex flex-col">
<div className="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<table className="min-w-full divide-y divide-gray-600">
<thead>
<tr>
<th
scope="col"
className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-100 sm:pl-6 md:pl-0"
>
Name
</th>
<th
scope="col"
className="py-3.5 px-3 text-left text-sm font-semibold text-gray-100"
>
Last Accessed
</th>
<th
scope="col"
className="py-3.5 px-3 text-left text-sm font-semibold text-gray-100"
>
Created
</th>
<th
scope="col"
className="py-3.5 px-3 text-left text-sm font-semibold text-gray-100"
>
Owner
</th>
<th
scope="col"
className="py-3.5 px-3 text-left text-sm font-semibold text-gray-100"
>
Permissions
</th>
<th
scope="col"
className="relative py-3.5 pl-3 pr-4 sm:pr-6 md:pr-0"
>
<span className="sr-only">Edit</span>
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-700">
{props.files.map(file => (
<tr key={file.id}>
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium sm:pl-6 md:pl-0">
{file.hidden ? (
<span className="text-gray-400">
{file.title || '(Unnamed File)'} (Hidden)
</span>
) : (
<Link href={`/${file.id.substring(1)}`}>
<a className="text-gray-100 hover:text-white">
{file.title || '(Unnamed File)'}
</a>
</Link>
)}
</td>
<td className="whitespace-nowrap py-4 px-3 text-sm text-gray-400">
{dayjs(file.lastAccessTime).fromNow()}
</td>
<td className="whitespace-nowrap py-4 px-3 text-sm text-gray-400">
{formatCreationTime(file.creationTime)}
</td>
<td className="whitespace-nowrap py-4 px-3 text-sm text-gray-400">
{file.owner
? file.owner.id === firebaseUser.uid
? 'Me'
: file.owner.name
: ''}
</td>
<td className="whitespace-nowrap py-4 px-3 text-sm text-gray-400">
{file.lastPermission &&
file.lastPermission in permissionLabels
? permissionLabels[file.lastPermission]
: 'Unknown'}
</td>
<td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 md:pr-0">
<button
className="text-indigo-400 hover:text-indigo-100"
onClick={() => handleToggleHideFile(file)}
>
{file.hidden ? 'Unhide' : 'Hide'}
</button>
</td>
<>
{downloadAllButton}
<div className="flex flex-col">
<div className="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<table className="min-w-full divide-y divide-gray-600">
<thead>
<tr>
<th
scope="col"
className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-100 sm:pl-6 md:pl-0"
>
Name
</th>
<th
scope="col"
className="py-3.5 px-3 text-left text-sm font-semibold text-gray-100"
>
Last Accessed
</th>
<th
scope="col"
className="py-3.5 px-3 text-left text-sm font-semibold text-gray-100"
>
Created
</th>
<th
scope="col"
className="py-3.5 px-3 text-left text-sm font-semibold text-gray-100"
>
Owner
</th>
<th
scope="col"
className="py-3.5 px-3 text-left text-sm font-semibold text-gray-100"
>
Permissions
</th>
<th
scope="col"
className="relative py-3.5 pl-3 pr-4 sm:pr-6 md:pr-0"
>
<span className="sr-only">Edit</span>
</th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody className="divide-y divide-gray-700">
{props.files.map(file => (
<tr key={file.id}>
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium sm:pl-6 md:pl-0">
{file.hidden ? (
<span className="text-gray-400">
{file.title || '(Unnamed File)'} (Hidden)
</span>
) : (
<Link href={`/${file.id.substring(1)}`}>
<a className="text-gray-100 hover:text-white">
{file.title || '(Unnamed File)'}
</a>
</Link>
)}
</td>
<td className="whitespace-nowrap py-4 px-3 text-sm text-gray-400">
{dayjs(file.lastAccessTime).fromNow()}
</td>
<td className="whitespace-nowrap py-4 px-3 text-sm text-gray-400">
{formatCreationTime(file.creationTime)}
</td>
<td className="whitespace-nowrap py-4 px-3 text-sm text-gray-400">
{file.owner
? file.owner.id === firebaseUser.uid
? 'Me'
: file.owner.name
: ''}
</td>
<td className="whitespace-nowrap py-4 px-3 text-sm text-gray-400">
{file.lastPermission &&
file.lastPermission in permissionLabels
? permissionLabels[file.lastPermission]
: 'Unknown'}
</td>
<td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 md:pr-0">
<button
className="text-indigo-400 hover:text-indigo-100"
onClick={() => handleToggleHideFile(file)}
>
{file.hidden ? 'Unhide' : 'Hide'}
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
// <div className="mt-4 -mx-2">
// {props.files
// .sort((a, b) => (a.creationTime ?? 0) - (b.creationTime ?? 0))
// .reverse()
// .map(file => (
// <Link key={file.id} href={`/${file.id.substring(1)}`}>
// <a className="bg-gray-800 hover:bg-gray-700 px-4 py-3 rounded-lg inline-block w-full max-w-sm m-2">
// <div className="flex justify-between">
// <div className="text-gray-200">
// {file.title || 'Unnamed File'}
// </div>
// <button
// onClick={e => {
// e.preventDefault(); // not stopPropagation
// if (!firebaseUser) {
// alert('Firebase not loaded, please wait');
// return;
// }
// const confirmed = confirm(
// file.hidden ? 'Unhide this item?' : 'Hide this item?'
// );
// if (!confirmed) return;
// if (confirmed) {
// const ref = firebase
// .database()
// .ref('users')
// .child(firebaseUser.uid)
// .child(file.id);
// ref.update({ hidden: !file.hidden });
// }
// }}
// >
// {file.hidden ? (
// <SaveIcon
// className="h-5 w-5 text-gray-400 hover:text-gray-500 focus:text-gray-500 focus:outline-none"
// aria-hidden="true"
// />
// ) : (
// <TrashIcon
// className="h-5 w-5 text-gray-400 hover:text-gray-500 focus:text-gray-500 focus:outline-none"
// aria-hidden="true"
// />
// )}
// </button>
// </div>
// <div className="text-gray-400">
// Last Accessed: {dayjs(file.lastAccessTime).fromNow()}
// </div>
// <div className="text-gray-400">
// Created: {formatCreationTime(file.creationTime)}
// </div>
// {props.showPerms ? (
// <div className="text-gray-400">
// Permissions:{' '}
// {file.lastPermission &&
// file.lastPermission in permissionLabels
// ? permissionLabels[file.lastPermission]
// : 'Unknown'}
// </div>
// ) : (
// <div className="text-gray-400">
// Default Permissions:{' '}
// {file.lastDefaultPermission &&
// file.lastDefaultPermission in sharingPermissionLabels
// ? sharingPermissionLabels[file.lastDefaultPermission]
// : 'Unknown'}
// </div>
// )}
// </a>
// </Link>
// ))}
// </div>
</>
);
}