Skip to content

Commit

Permalink
Add recent uploads section.
Browse files Browse the repository at this point in the history
Similar to wormhole, this allows you to see links for uploads you have done in the past 24 hours. Useful in case you forget to copy the link.
  • Loading branch information
mjmay08 committed Jan 24, 2024
1 parent 3e22e28 commit 3956cd1
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 15 deletions.
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"bittorrent-tracker": "^10.0.11",
"config": "^3.3.9",
"cookie-parser": "^1.4.6",
"dexie": "^3.2.4",
"node-fetch": "^3.3.1",
"node-polyfill-webpack-plugin": "^2.0.1",
"qrcode": "^1.5.3",
Expand Down
78 changes: 75 additions & 3 deletions src/client/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ body {
margin: 0;
font-family: cursive;
color: white;
display: flex;
flex-direction: column;
}

@keyframes rotate {
Expand Down Expand Up @@ -49,7 +51,7 @@ body {
display: flex;
place-content: center;
align-items: center;
overflow: hidden;
overflow-y: auto;
}

/* This is just to transition when you change the viewport size. */
Expand All @@ -58,16 +60,23 @@ body {
}

#upload-view {
height: calc(100% - 40px);
width: 90%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow-y: auto;

#uploader {
max-width: 100%;
}

#upload-view-inner {
max-width: 100%;
height: 100%;
display: none;
flex-direction: column;
}
}

#password-input {
Expand Down Expand Up @@ -107,6 +116,69 @@ body {
}
}

#recent-rooms {
min-height: 150px;
overflow-y: auto;
border: white;
border-style: inset;
border-width: thin;
border-radius: 5px;
margin: 15px 0;
padding: 10px;
display: none;

#recent-rooms-header {
margin-bottom: 10px;
text-align: center;
}

.recent-room-item {
border-style: inset;
border-width: thin;
border-radius: 5px;
padding: 10px;
margin: 10px 0;

.row1 {
display: flex;

.files {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: calc(100% - 60px);
}

.remove-row {
margin-left: auto;
background-color: transparent;
border: none;
color: white;
cursor: pointer;
outline: none;
}
}

.row2 {
display: flex;
margin-top: 5px;

button {
margin-right: 30px;
cursor: pointer;
border: none;
border-radius: 3px;
background-color: rgb(255, 255, 255, 0.1);
color: white;
padding: 5px;
}
button:hover {
background-color: rgb(255, 255, 255, 0.5);
}
}
}
}

#upload-success {
display: none;
font-size: x-large;
Expand Down Expand Up @@ -142,7 +214,7 @@ body {
background-position: bottom;
width: 350px;
max-width: 80%;
height: 100px;
min-height: 100px;
}

#canvas {
Expand Down
18 changes: 12 additions & 6 deletions src/client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@
</head>
<body style="opacity: 0">
<div class="gradient"></div>
<div class="logo"></div>
<div id="upload-view">
<div class="logo"></div>
<!-- Upload view -->
<div id="password-input">
Password: <input type="password" id="password" />
<div class="tooltip">
<span class="tooltiptext">A password is required in order to upload files</span>
<div id="upload-view-inner">
<div id="password-input">
Password: <input type="password" id="password" />
<div class="tooltip">
<span class="tooltiptext">A password is required in order to upload files</span>
</div>
</div>
<div id="uploader"></div>
<div id="recent-rooms">
<div id="recent-rooms-header">Recent Uploads</div>
<div id="recent-room-list"></div>
</div>
</div>
<div id="uploader"></div>
<div id="upload-success">Successfully uploaded</div>
<!-- End upload view -->

Expand Down
80 changes: 76 additions & 4 deletions src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@ import '@uppy/webcam/dist/style.min.css';
import { Room } from './room';
import './index.css';
import QRCode from 'qrcode';
import { RoomDatabase } from './roomDb';

const apiBase = window.location.origin + '/api/';
let room: Room;
let uppy: Uppy;

const db = new RoomDatabase();

if (window.location.pathname.length > 1) {
// Existing room. Try to load
const roomId = window.location.pathname.replace('/', '');
const key = window.location.hash.replace('#', '');
console.log('Existing room: ' + roomId);
console.log('Key: ' + key);
room = new Room();
room = new Room(db);
room.join(roomId, key).then(
() => {
room.getFiles().then(
Expand Down Expand Up @@ -68,7 +71,7 @@ if (window.location.pathname.length > 1) {
}

async function beforeUpload(): Promise<Room> {
room = new Room();
room = new Room(db);
await room.create();
room.afterFinalize(() => {
showShareView(room.id, room.keychain.keyB64);
Expand All @@ -86,7 +89,7 @@ async function setTusHeaders(req: HttpRequest, file: UppyFile) {

function showShareView(roomId, mainKey) {
// Set Share link
const shareURL = window.location.href + roomId + '#' + mainKey;
const shareURL = getShareLink(roomId, mainKey);
(<HTMLInputElement>document.getElementById('share-url')).value = shareURL;
// Set up copy link button
const copyLinkBtn = document.getElementById('copyLink');
Expand Down Expand Up @@ -118,6 +121,10 @@ function showShareView(roomId, mainKey) {
document.getElementById('share-view').style.display = 'flex';
}

function getShareLink(roomId: string, key: string): string {
return window.location.href + roomId + '#' + key;
}

/**
* Fetch any configuration information.
* Right now this just checks if a password is required for upload.
Expand Down Expand Up @@ -176,7 +183,9 @@ function getDashboardNote(config: SiaShareConfig): string {
}

function initializeDashboard(config: SiaShareConfig): void {
document.getElementById('upload-view-inner').style.display = 'flex';
showHidePasswordInput(config.passwordRequired);
getAllRecentRooms();
uppy = new Uppy({
allowMultipleUploadBatches: false,
restrictions: getUploadRestrictions(config),
Expand All @@ -189,7 +198,8 @@ function initializeDashboard(config: SiaShareConfig): void {
theme: 'dark',
hideRetryButton: true,
hideCancelButton: true,
note: getDashboardNote(config)
note: getDashboardNote(config),
height: 400
})
.use(Tus, {
endpoint: apiBase + 'tus/upload',
Expand All @@ -207,6 +217,7 @@ function initializeDashboard(config: SiaShareConfig): void {
document.getElementById('share-view').style.display = 'none';
} else {
showHidePasswordInput(false);
document.getElementById('upload-view-inner').style.display = 'none';
uppy.close();
document.getElementById('upload-success').style.display = 'flex';
}
Expand Down Expand Up @@ -254,6 +265,67 @@ function formatBytes(bytes, decimals = 2) {
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
}

async function getAllRecentRooms(): Promise<void> {
try {
document.getElementById('recent-rooms').style.display = 'none';
const oneDayAgo = new Date();
oneDayAgo.setDate(oneDayAgo.getDate() - 1);
// Delete rooms older than 24 hours to clear up indexeddb
await db.rooms.where('creationTime').below(oneDayAgo).delete();
// Fetch all recent rooms for display
const recentRooms = await db.rooms.where('creationTime').above(oneDayAgo).toArray();
const recentRoomsParent = document.getElementById('recent-room-list');
while (recentRoomsParent.firstChild) {
recentRoomsParent.removeChild(recentRoomsParent.lastChild);
}
recentRooms.forEach((room) => {
const roomDiv = document.createElement('div');
roomDiv.classList.add('recent-room-item');

const row1Div = document.createElement('div');
row1Div.classList.add('row1');
const filesDiv = document.createElement('div');
filesDiv.classList.add('files');
filesDiv.textContent = room.files.join(', ');
const deleteIcon = document.createElement('button');
deleteIcon.innerText = 'X';
deleteIcon.classList.add('remove-row');
deleteIcon.onclick = function () {
console.log(`Remove room ${room.id} from recent rooms`);
db.rooms.delete(room.id);
getAllRecentRooms();
};
row1Div.appendChild(filesDiv);
row1Div.appendChild(deleteIcon);

const row2Div = document.createElement('div');
row2Div.classList.add('row2');
const openLink = document.createElement('button');
openLink.innerText = 'Open';
openLink.onclick = function () {
window.open(getShareLink(room.id, room.key), '_blank').focus();
};
const copyLink = document.createElement('button');
copyLink.onclick = function () {
navigator.clipboard.writeText(getShareLink(room.id, room.key));
};
copyLink.innerText = 'Copy Link';
row2Div.appendChild(openLink);
row2Div.appendChild(copyLink);

roomDiv.appendChild(row1Div);
roomDiv.appendChild(row2Div);
recentRoomsParent.appendChild(roomDiv);
});
// Only show if there are recent rooms
if (recentRooms?.length) {
document.getElementById('recent-rooms').style.display = 'block';
}
} catch (ex) {
console.log(ex);
}
}

interface SiaShareConfig {
passwordRequired: boolean;
maxFileSize?: number;
Expand Down
20 changes: 19 additions & 1 deletion src/client/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import WebTorrent from 'webtorrent/dist/webtorrent.min.js';
import nodeToWebStream from 'readable-stream-node-to-web';
import parseTorrent from 'parse-torrent';
import base64 from 'base64-js';
import { RoomDatabase } from './roomDb';

export type RoomMetadataResponse = {
torrents: parseTorrent.Instance[];
Expand All @@ -22,6 +23,11 @@ export class Room {
private encoder = new TextEncoder();
private decoder = new TextDecoder();
private afterFinalizeCallback: () => void;
private db: RoomDatabase;

constructor(db: RoomDatabase) {
this.db = db;
}

async create() {
// Creating a new room
Expand All @@ -41,7 +47,7 @@ export class Room {
this.keychain.setAuthToken(this.writerAuthToken);
}

async finalize(encryptedTorrent: string) {
async finalize(encryptedTorrent: string, fileNames: string[]) {
await fetch(this.API_BASE + 'room/' + this.id, {
method: 'put',
headers: { 'Content-Type': 'application/json' },
Expand All @@ -51,6 +57,8 @@ export class Room {
})
});
this.afterFinalizeCallback();

this.saveRoomToIndexedDB(this.id, this.keychain.keyB64, fileNames);
}

public afterFinalize(callback: () => void) {
Expand Down Expand Up @@ -184,4 +192,14 @@ export class Room {
}
return metadata;
}

private saveRoomToIndexedDB(id: string, key: string, fileNames: string[]): void {
this.db
.transaction('rw', this.db.rooms, async () => {
await this.db.rooms.add({ id: id, key: key, files: fileNames, creationTime: new Date() });
})
.catch((e) => {
console.error(e);
});
}
}
Loading

0 comments on commit 3956cd1

Please sign in to comment.