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

Support file sharing feature #83

Merged
merged 2 commits into from
Dec 27, 2022
Merged
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
44 changes: 41 additions & 3 deletions client/src/components/MainPage/UploadFilesComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import React, { useCallback } from "react";
import styled from "styled-components";
import { useDropzone } from "react-dropzone";
import { Fragment } from "react";
import { generateFingerPrint, getSubtypeOfMIMEtypes } from "../../utils/commonUtil";
import FingerprintedFile from "../../utils/dataSchema/FingerprintedFile";
import { addFingerPrintedFiles, createTableSharingData, notifySharingData } from "../../utils/localApi";
import { DATATYPE_FILE } from "../../constants/constant";

const UploadFiles = styled.div`
display: flex;
Expand Down Expand Up @@ -51,11 +55,45 @@ const Description = styled.div`
function UploadFilesComponent(props) {
const { className } = props;

const onDrop = useCallback((acceptedFiles) => {
// TODO(young): handle files later
const onDrop = useCallback(async (acceptedFiles) => {
// store File object in somehwere; store file related data to db;
// display upload process - 0.3~0.5sec animation is okay
console.log("acceptedFiles:", acceptedFiles);
if (acceptedFiles?.length === 0) {
return;
}

const fingerprintedFiles = [];
for (let i = 0; i < acceptedFiles.length; i++) {
fingerprintedFiles.push(
new FingerprintedFile({
file: acceptedFiles[i],
// TODO(young): use generateSharingDataUUID later and unify terminology
// fingerprint (x), data UUID (o)
fingerprint: generateFingerPrint(),
})
);
}

// save to database
const sharingDataList = await Promise.all(fingerprintedFiles.map(fingerprintedFile => {
const dataID = fingerprintedFile.fingerprint;
const fileName = fingerprintedFile.file.name;
const fileType = getSubtypeOfMIMEtypes(fingerprintedFile.file.type) || "unknown";
const sizeInBytes = fingerprintedFile.file.size;

return createTableSharingData({ dataID, type: DATATYPE_FILE,
name: fileName, size: sizeInBytes, extension: fileType});
}));

// save in store
addFingerPrintedFiles(fingerprintedFiles);

// notify to other peers
if (sharingDataList?.length > 0) {
for (const sharingData of sharingDataList) {
await notifySharingData(sharingData);
}
}
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ const ActionButton = styled.div`

// TODO(young): implement onClick function later
function ActionButtonComponent(props) {
const { type } = props;
const { type, onClick } = props;

return (
<ActionButton type={type}>
<ActionButton type={type} onClick={type === "FILE" ? onClick : null}>
<DownloadArrowIcon />
</ActionButton>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import HandsUpIcon from "../../../assets/HandsUpIcon";
import {
checkHandsUpTableSharingData,
patchTableSharingDataByID,
requestDownloadFile,
selectTableSharingDataByID,
} from "../../../utils/localApi";
import { shallowEqual, useSelector } from "react-redux";
Expand Down Expand Up @@ -147,6 +148,7 @@ function renderAction(
isHandsUpRow,
onClick,
onClickHandsUp,
onClickDonwloadButton,
onCancelHandsUp
) {
if (isEditMode) {
Expand All @@ -159,8 +161,6 @@ function renderAction(
);
}

console.log("isHandsUpRow:", isHandsUpRow, isMyProfileRow);

const renderIcon = () => {
if (isMyProfileRow && isHandsUpRow) {
return <HandsUpActivateButtonComponent onClick={onCancelHandsUp} />;
Expand All @@ -171,6 +171,7 @@ function renderAction(
) : (
<ActionButtonComponent
type={dataType === "URL" ? "TEXT" : "FILE"}
onClick={onClickDonwloadButton}
/>
);
};
Expand Down Expand Up @@ -225,6 +226,12 @@ function ActivityRowComponent(props) {
event?.stopPropagation();
}

async function onClickDonwloadButton(event) {
event?.stopPropagation();
// TODO(young): fingerprint should be renamed to sharingDataID
await requestDownloadFile(senderID, { fingerprint: rowID});
}

async function onClickHandsUp(event) {
event?.stopPropagation();

Expand All @@ -242,6 +249,8 @@ function ActivityRowComponent(props) {
await patchTableSharingDataByID({ handsUp: false }, rowID);
}



return (
<ActivityRow
isSelected={isSelected}
Expand Down Expand Up @@ -284,7 +293,8 @@ function ActivityRowComponent(props) {
isHandsUpRow,
onClickDeleteButton,
onClickHandsUp,
onCancelHandsUp
onClickDonwloadButton,
onCancelHandsUp,
)}
</div>
</ActivityRow>
Expand Down
13 changes: 13 additions & 0 deletions client/src/utils/commonUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ function getRandomNumber(max) {
return Math.floor(Math.random() * max);
}

// ex) "image/png", "image/png;param=hoho"
function getSubtypeOfMIMEtypes(types) {
const regex = /(.*){1}\/([^;]*){1}(;.*)*/;
const matched = types.match(regex);

if (matched.length >= 3) {
return matched[2];
}

return null;
}

export {
getSizeString,
getCurrentTime,
Expand All @@ -105,4 +117,5 @@ export {
convertTimestampReadable,
generateUserProfile,
generateSharingDataUUID,
getSubtypeOfMIMEtypes,
};
1 change: 1 addition & 0 deletions client/src/utils/database/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const TABLE_SHARING_DATA = {

// file related fields
name: "name",
// size in bytes
size: "size",
extension: "extension",

Expand Down
15 changes: 9 additions & 6 deletions client/src/utils/downloadManager.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {
parsePeerChunk,
getMessagePacket,
sendErrorToPeer,
getMyUUID,
writeSystemMessage,
selectTableSharingDataByID,
} from "./localApi";
import {
HEADER_SIZE_IN_BYTES,
Expand Down Expand Up @@ -59,12 +59,15 @@ async function accumulateChunk(chunkWithHeader, uuid) {
};

if (!chunkStore[fingerprint].downloadWriter) {
const messagePacket = getMessagePacket(fingerprint);
const sharingData = await selectTableSharingDataByID(fingerprint);

const filename =
(messagePacket && messagePacket.data.message) ||
"localdrop_download_file";
const fileSize = (messagePacket && messagePacket.data.size) || 0;
if (!sharingData) {
// TODO(young): handle error case
return;
}

const filename = sharingData.name;
const fileSize = sharingData.size;
const options = { pathname: fingerprint, size: fileSize };

chunkStore[fingerprint].size = fileSize;
Expand Down
42 changes: 29 additions & 13 deletions client/src/utils/localApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
updateTableNotifications as updateTableNotificationsCounter,
updateTableSharingData as updateTableSharingDataCounter,
updateSenderID,
addFiles,
} from "../redux/action";
import { parseChunkAndHeader } from "./peerMessage";
import {
Expand Down Expand Up @@ -156,6 +157,10 @@ function addMessagePacket(message) {
store.dispatch(addMessage(message));
}

function addFingerPrintedFiles(files) {
store.dispatch(addFiles(files))
}

// createMyUserInfo generate my user info and update my UUID
async function createMyUserInfo(uuid) {
// create user!
Expand Down Expand Up @@ -215,6 +220,7 @@ async function transferFileToPeer(fingerprint, uuid) {
}
}

// TODO(young): deprecate messagePacket
function getMessagePacket(fingerprint) {
// TODO: Make O(1)

Expand Down Expand Up @@ -397,20 +403,23 @@ async function patchTableUsersByID({ name, profile }, userID) {
return result?.[0]?.rows;
}

async function createTableSharingData({ type, name, size, extension, text }) {
const id = generateSharingDataUUID();
async function createTableSharingData({ dataID, type, name, size, extension, text }) {
const id = dataID || generateSharingDataUUID();
const uploader_id = getMyUUID();
const uploaded_at = new Date().toISOString();
console.log(id, uploader_id, uploaded_at);

if (type === DATATYPE_FILE) {
console.log("shraing file is not supported yet!");
return;
}

const query = `INSERT INTO ${TABLE_SHARING_DATA.name} VALUES (
"${id}", NULL, 0, NULL, "${text}", "${DATATYPE_LINK}", 0, false,
"${uploader_id}", "${uploaded_at}");`;
const query = (() => {
if (type === DATATYPE_FILE) {
return `INSERT INTO ${TABLE_SHARING_DATA.name} VALUES(
"${id}", "${name}", ${size}, "${extension}", NULL, "${DATATYPE_FILE}", 0, false,
"${uploader_id}", "${uploaded_at}");`;
} else {
return `INSERT INTO ${TABLE_SHARING_DATA.name} VALUES (
"${id}", NULL, 0, NULL, "${text}", "${DATATYPE_LINK}", 0, false,
"${uploader_id}", "${uploaded_at}");`;
}
})();

console.log("query:", query);

Expand All @@ -424,6 +433,7 @@ async function createTableSharingData({ type, name, size, extension, text }) {
return data;
}

// TODO(young): unify insert/create sharing data or refactor them together
async function insertTableSharingData({ sharingData }) {
const id = sharingData[TABLE_SHARING_DATA.fields.id];
const name = sharingData[TABLE_SHARING_DATA.fields.name];
Expand Down Expand Up @@ -667,12 +677,12 @@ export {
addJoinedPeers,
deleteLeavedPeers,
addMessagePacket,
addFingerPrintedFiles,
createMyUserInfo,
getPeerUUID,
getMyUUID,
getFileToTransfer,
transferFileToPeer,
getMessagePacket,
parsePeerChunk,
writePeerChunk,
writeSystemMessage,
Expand All @@ -682,8 +692,7 @@ export {
updateSender,
// db interfaces
updateTableUsers,
updateTableSharingData,
updateTableCommentMetadata,

updateTableNotifications,
upsertTableUser,
deleteTableUserByID,
Expand All @@ -692,15 +701,22 @@ export {
selectTableUsersMyself,
selectTableUsersWithLatestSharingDataTypeExcludingMyself,
patchTableUsersByID,

createTableSharingData,
insertTableSharingData,
updateTableSharingData,
selectTableSharingDataByID,
selectTableSharingDataWithCommentCount,
selectTableSharingDataWithCommentCountOrderBy,
checkHandsUpTableSharingData,
patchTableSharingDataByID,
deleteTableSharingDataByIDs,

selectTableCommentsByDataID,

selectTableCommentMetadataByDataID,
updateTableCommentMetadata,

selectTableNotifications,
selecTableNotificationsWithUserAndSharingData,
};
1 change: 1 addition & 0 deletions client/src/utils/peerConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ function addMessageTypeEventListener(peerConnectionManager) {

peerConnectionManager.uuid = uuid;
// TODO(young): consider in case of calling this event handler more than twice.
// TODO(young): create my user info regardless of signaling server connection.
await createMyUserInfo(uuid);
});

Expand Down