Browse Source

hook multifile to ui

Emily 6 years ago
parent
commit
c9ae76b209
10 changed files with 179 additions and 137 deletions
  1. 8 0
      app/api.js
  2. 4 0
      app/archive.js
  3. 100 83
      app/base.css
  4. 7 16
      app/dragManager.js
  5. 48 11
      app/fileManager.js
  6. 1 0
      app/fileReceiver.js
  7. 2 0
      app/fileSender.js
  8. 7 1
      app/main.css
  9. 2 0
      app/ownedFile.js
  10. 0 26
      app/pages/completed/index.js

+ 8 - 0
app/api.js

@@ -65,6 +65,14 @@ export async function fileInfo(id, owner_token) {
   throw new Error(response.status);
 }
 
+export async function hasPassword(id) {
+  const response = await fetch(`/api/exists/${id}`);
+  if (response.ok) {
+    return response.json();
+  }
+  throw new Error(response.status);
+}
+
 export async function metadata(id, keychain) {
   const result = await fetchWithAuthAndRetry(
     `/api/metadata/${id}`,

+ 4 - 0
app/archive.js

@@ -17,6 +17,10 @@ export default class Archive {
     return this.files.reduce((total, file) => total + file.size, 0);
   }
 
+  get numFiles() {
+    return this.files.length;
+  }
+
   get manifest() {
     return {
       files: this.files.map(file => ({

+ 100 - 83
app/base.css

@@ -1,14 +1,16 @@
 :root {
   --pageBGColor: #fff;
-  --primaryControlBGColor: #0297f8;
+  --lightControlBGColor: #e6e6e6;
+  --primaryControlBGColor: #0a84ff;
   --primaryControlFGColor: #fff;
-  --primaryControlHoverColor: #0287e8;
+  --primaryControlHoverColor: #0473e2;
   --inputTextColor: #737373;
   --errorColor: #d70022;
   --linkColor: #0094fb;
   --textColor: #0c0c0d;
+  --lightBorderColor: rgba(12, 12, 12, 0.2);
   --lightTextColor: #737373;
-  --successControlBGColor: #05a700;
+  --successControlBGColor: #12bc00;
   --successControlFGColor: #fff;
 }
 
@@ -41,17 +43,21 @@ a {
 
 .main {
   display: flex;
-  flex-direction: row;
   flex: auto;
-  padding: 0 20px;
+  padding: 0 25px;
   box-sizing: border-box;
+  min-height: 500px;
+  max-height: 630px;
+  height: 100px;
 }
 
 .stripedBox {
-  width: 480px;
+  flex: none;
+  position: relative;
+  width: 400px;
   background-color: white;
   border-radius: 6px;
-  box-shadow: 0 0 0 3px rgba(155, 155, 155, 0.4);
+  box-shadow: 0 0 0 3px rgba(12, 12, 13, 0.2);
   background-image: repeating-linear-gradient(
     45deg,
     white,
@@ -68,8 +74,11 @@ a {
 .mainContent {
   height: 100%;
   background-color: white;
+  box-sizing: border-box;
   margin: 0 10px;
-  padding: 1px 10px 0 10px; /* top wtf? */
+  padding: 10px 10px 28px;
+  display: flex;
+  flex-direction: column;
 }
 
 .spacer {
@@ -77,7 +86,8 @@ a {
 }
 
 .uploads {
-  flex: auto;
+  flex: 0 0 262px;
+  position: relative;
 }
 
 .noscript {
@@ -87,13 +97,19 @@ a {
 }
 
 .btn {
-  font-size: 15px;
+  display: block;
+  width: 100%;
+  height: 70px;
+  line-height: 70px;
+  font-size: 21px;
   font-weight: 500;
-  color: var(--primaryControlFGColor);
-  cursor: pointer;
+  text-transform: uppercase;
   text-align: center;
+  letter-spacing: 0.56px;
+  color: var(--primaryControlFGColor);
   background: var(--primaryControlBGColor);
-  border: 1px solid var(--primaryControlBGColor);
+  cursor: pointer;
+  border: 0;
   border-radius: 5px;
 }
 
@@ -101,75 +117,52 @@ a {
   background-color: var(--primaryControlHoverColor);
 }
 
-.btn--cancel {
-  color: var(--errorColor);
-  background: var(--pageBGColor);
-  font-size: 15px;
-  border: 0;
-  cursor: pointer;
-  text-decoration: underline;
+.btn--stripes {
+  background: repeating-linear-gradient(
+    -65deg,
+    #7c7c7c 0,
+    #7c7c7c 17px,
+    #737373 17px,
+    #737373 30px
+  );
+  background-size: 300% 300%;
+  animation: barberpole 12s linear infinite;
 }
 
-.btn--cancel:disabled {
-  text-decoration: none;
-  cursor: auto;
-}
+@keyframes barberpole {
+  0% {
+    background-position: 100% 0%;
+  }
 
-.btn--cancel:hover {
-  background-color: var(--pageBGColor);
+  100% {
+    background-position: 0% 0%;
+  }
 }
 
 .input {
-  flex: 2 0 auto;
-  border: 1px solid var(--primaryControlBGColor);
-  border-radius: 6px 0 0 6px;
+  border: 1px solid var(--lightBorderColor);
   font-size: 20px;
   color: var(--inputTextColor);
   font-family: 'SF Pro Text', sans-serif;
-  letter-spacing: 0;
-  line-height: 23px;
   font-weight: 300;
-  height: 46px;
   padding-left: 10px;
   padding-right: 10px;
 }
 
-.input--error {
-  border-color: var(--errorColor);
-}
-
 .input--noBtn {
   border-radius: 6px;
 }
 
-.inputBtn {
-  flex: auto;
-  background: var(--primaryControlBGColor);
-  border-radius: 0 6px 6px 0;
-  border: 1px solid var(--primaryControlBGColor);
-  color: var(--primaryControlFGColor);
-  cursor: pointer;
-
-  /* Force flat button look */
-  /* stylelint-disable-next-line plugin/no-unsupported-browser-features */
-  appearance: none;
-  font-size: 15px;
-  padding-bottom: 3px;
-  padding-left: 10px;
-  padding-right: 10px;
-  white-space: nowrap;
-}
-
-.inputBtn:disabled {
-  cursor: auto;
+.input--error {
+  border-color: var(--errorColor);
 }
 
-.inputBtn:hover {
-  background-color: var(--primaryControlHoverColor);
+.inputBtn.inputError {
+  background-color: var(--errorColor);
 }
 
-.inputBtn--hidden {
-  display: none;
+.inputBtn.inputError:hover {
+  background-color: var(--errorColor);
 }
 
 .cursor--pointer {
@@ -188,15 +181,15 @@ a {
 }
 
 .link--action {
-  text-decoration: underline;
+  font-weight: 500;
+  font-size: 14px;
   text-align: center;
 }
 
 .page {
-  margin: 0 auto 30px;
+  height: 100%;
+  margin: 0;
   display: flex;
-  justify-content: center;
-  align-items: center;
   flex-direction: column;
   text-align: center;
 }
@@ -223,6 +216,13 @@ a {
   animation: fadeout 200ms linear;
 }
 
+.goBackButton {
+  position: absolute;
+  top: 0;
+  left: 0;
+  margin: 18px;
+}
+
 @keyframes fadeout {
   0% {
     opacity: 1;
@@ -250,6 +250,7 @@ a {
 
 .error {
   color: var(--errorColor);
+  font-weight: 600;
 }
 
 .title {
@@ -264,34 +265,50 @@ a {
 }
 
 .description {
-  font-size: 15px;
-  line-height: 23px;
-  max-width: 630px;
-  text-align: center;
-  margin: 0 auto 60px;
-  color: var(--textColor);
-  width: 92%;
+  font-size: 13px;
+  text-align: left;
+  margin: 14px auto;
+  color: var(--lightTextColor);
+  width: 95%;
+}
+
+.visible {
+  visibility: visible !important;
+}
+
+.noDisplay {
+  display: none;
 }
 
-@media (max-device-width: 768px), (max-width: 768px) {
+.flexible {
+  flex: 1;
+}
+
+@media (max-device-width: 750px), (max-width: 750px) {
   .description {
     margin: 0 auto 25px;
   }
-}
 
-@media (max-device-width: 520px), (max-width: 520px) {
-  .input {
-    font-size: 22px;
-    padding: 10px 10px;
-    border-radius: 6px 6px 0 0;
+  .main {
+    flex-direction: column;
+    min-height: 700px;
+  }
+
+  .spacer {
+    flex: none;
+    height: 0;
+  }
+
+  .stripedBox {
+    max-height: 550px;
+    flex: 1;
   }
 
-  .inputBtn {
-    border-radius: 0 0 6px 6px;
-    flex: 0 1 65px;
+  .uploads {
+    flex: none;
   }
 
-  .input--noBtn {
-    border-radius: 6px;
+  .footer {
+    margin: 15px;
   }
 }

+ 7 - 16
app/dragManager.js

@@ -1,12 +1,12 @@
-/* global MAXFILESIZE */
-import Archive from './archive';
-import { bytes } from './utils';
+import { checkSize } from './utils';
 
 export default function(state, emitter) {
   emitter.on('DOMContentLoaded', () => {
     document.body.addEventListener('dragover', event => {
       if (state.route === '/') {
         event.preventDefault();
+        const files = document.querySelector('.uploadedFilesWrapper');
+        files.classList.add('uploadArea--noEvents');
       }
     });
     document.body.addEventListener('drop', event => {
@@ -15,21 +15,12 @@ export default function(state, emitter) {
         document
           .querySelector('.uploadArea')
           .classList.remove('uploadArea--dragging');
+
         const target = event.dataTransfer;
-        if (target.files.length === 0) {
-          return;
-        }
-        const file = new Archive(target.files);
 
-        if (file.size === 0) {
-          return;
-        }
-        if (file.size > MAXFILESIZE) {
-          // eslint-disable-next-line no-alert
-          alert(state.translate('fileTooBig', { size: bytes(MAXFILESIZE) }));
-          return;
-        }
-        emitter.emit('upload', { file, type: 'drop' });
+        checkSize(target.files, state.files);
+
+        emitter.emit('addFiles', { files: target.files });
       }
     });
   });

+ 48 - 11
app/fileManager.js

@@ -1,17 +1,14 @@
 import FileSender from './fileSender';
 import FileReceiver from './fileReceiver';
-import {
-  copyToClipboard,
-  delay,
-  fadeOut,
-  openLinksInNewTab,
-  percent
-} from './utils';
+import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils';
 import * as metrics from './metrics';
+import { hasPassword } from './api';
+import Archive from './archive';
 
 export default function(state, emitter) {
   let lastRender = 0;
   let updateTitle = false;
+  state.files = [];
 
   function render() {
     emitter.emit('render');
@@ -64,6 +61,16 @@ export default function(state, emitter) {
     metrics.changedDownloadLimit(file);
   });
 
+  emitter.on('removeUpload', async ({ file }) => {
+    for (let i = 0; i < state.files.length; i++) {
+      if (state.files[i] === file) {
+        state.files.splice(i, 1);
+        render();
+        return;
+      }
+    }
+  });
+
   emitter.on('delete', async ({ file, location }) => {
     try {
       metrics.deletedUpload({
@@ -85,11 +92,22 @@ export default function(state, emitter) {
     state.transfer.cancel();
   });
 
-  emitter.on('upload', async ({ file, type }) => {
+  emitter.on('addFiles', async ({ files }) => {
+    for (let i = 0; i < files.length; i++) {
+      state.files.push(files[i]);
+    }
+    render();
+  });
+
+  //TODO: hook up to multi-file upload functionality
+  emitter.on('upload', async ({ files, type, dlCount, password }) => {
+    const file = new Archive(files);
+
     const size = file.size;
     const sender = new FileSender(file);
     sender.on('progress', updateProgress);
     sender.on('encrypting', render);
+    sender.on('complete', render);
     state.transfer = sender;
     state.uploading = true;
     render();
@@ -98,19 +116,25 @@ export default function(state, emitter) {
     await delay(200);
     try {
       metrics.startedUpload({ size, type });
+
       const ownedFile = await sender.upload();
       ownedFile.type = type;
       state.storage.totalUploads += 1;
       metrics.completedUpload(ownedFile);
 
       state.storage.addFile(ownedFile);
+
+      if (password) {
+        emitter.emit('password', { password, file: ownedFile });
+      }
+      emitter.emit('changeLimit', { file: ownedFile, value: dlCount });
+
       const cancelBtn = document.getElementById('cancel-upload');
       if (cancelBtn) {
         cancelBtn.hidden = 'hidden';
       }
       if (document.querySelector('.page')) {
         await delay(1000);
-        await fadeOut('.page');
       }
       emitter.emit('pushState', `/share/${ownedFile.id}`);
     } catch (err) {
@@ -127,6 +151,8 @@ export default function(state, emitter) {
       }
     } finally {
       openLinksInNewTab(links, false);
+      state.files = [];
+      state.password = '';
       state.uploading = false;
       state.transfer = null;
     }
@@ -150,6 +176,17 @@ export default function(state, emitter) {
     render();
   });
 
+  emitter.on('getPasswordExist', async ({ id }) => {
+    try {
+      state.fileInfo = await hasPassword(id);
+      render();
+    } catch (e) {
+      if (e.message === '404') {
+        return emitter.emit('pushState', '/404');
+      }
+    }
+  });
+
   emitter.on('getMetadata', async () => {
     const file = state.fileInfo;
 
@@ -172,6 +209,7 @@ export default function(state, emitter) {
   emitter.on('download', async file => {
     state.transfer.on('progress', updateProgress);
     state.transfer.on('decrypting', render);
+    state.transfer.on('complete', render);
     const links = openLinksInNewTab();
     const size = file.size;
     try {
@@ -186,12 +224,11 @@ export default function(state, emitter) {
       const speed = size / (time / 1000);
       if (document.querySelector('.page')) {
         await delay(1000);
-        await fadeOut('.page');
       }
       state.storage.totalDownloads += 1;
       state.transfer.reset();
       metrics.completedDownload({ size, time, speed });
-      emitter.emit('pushState', '/completed');
+      //emitter.emit('pushState', '/completed');
     } catch (err) {
       if (err.message === '0') {
         // download cancelled

+ 1 - 0
app/fileReceiver.js

@@ -172,6 +172,7 @@ export default class FileReceiver extends Nanobus {
 
       this.downloadRequest = null;
       this.msg = 'downloadFinish';
+      this.emit('complete');
       this.state = 'complete';
     } catch (e) {
       this.downloadRequest = null;

+ 2 - 0
app/fileSender.js

@@ -93,6 +93,7 @@ export default class FileSender extends Nanobus {
         url: `${result.url}#${secretKey}`,
         name: this.file.name,
         size: this.file.size,
+        manifest: this.file.manifest,
         time: time,
         speed: this.file.size / (time / 1000),
         createdAt: Date.now(),
@@ -101,6 +102,7 @@ export default class FileSender extends Nanobus {
         nonce: this.keychain.nonce,
         ownerToken: result.ownerToken
       });
+
       return ownedFile;
     } catch (e) {
       this.msg = 'errorPageHeader';

+ 7 - 1
app/main.css

@@ -2,7 +2,6 @@
 @import './templates/activeBackground/activeBackground.css';
 @import './templates/header/header.css';
 @import './templates/downloadButton/downloadButton.css';
-@import './templates/progress/progress.css';
 @import './templates/passwordInput/passwordInput.css';
 @import './templates/downloadPassword/downloadPassword.css';
 @import './templates/setPasswordSection/setPasswordSection.css';
@@ -11,7 +10,14 @@
 @import './templates/selectbox/selectbox.css';
 @import './templates/fileList/fileList.css';
 @import './templates/file/file.css';
+@import './templates/uploadedFile/uploadedFile.css';
+@import './templates/uploadedFileList/uploadedFileList.css';
 @import './templates/popup/popup.css';
+@import './templates/title/title.css';
+@import './templates/fileIcon/fileIcon.css';
+@import './templates/signupPromo/signupPromo.css';
+@import './templates/userAccount/userAccount.css';
 @import './pages/welcome/welcome.css';
 @import './pages/share/share.css';
+@import './pages/signin/signin.css';
 @import './pages/unsupported/unsupported.css';

+ 2 - 0
app/ownedFile.js

@@ -9,6 +9,7 @@ export default class OwnedFile {
     this.name = obj.name;
     this.size = obj.size;
     this.type = obj.type;
+    this.manifest = obj.manifest;
     this.time = obj.time;
     this.speed = obj.speed;
     this.createdAt = obj.createdAt;
@@ -70,6 +71,7 @@ export default class OwnedFile {
       name: this.name,
       size: this.size,
       type: this.type,
+      manifest: this.manifest,
       time: this.time,
       speed: this.speed,
       createdAt: this.createdAt,

+ 0 - 26
app/pages/completed/index.js

@@ -1,26 +0,0 @@
-const html = require('choo/html');
-const progress = require('../../templates/progress');
-const { fadeOut } = require('../../utils');
-
-module.exports = function(state, emit) {
-  return html`
-    <div class="page effect--fadeIn">
-      <div class="title">
-        ${state.translate('downloadFinish')}
-      </div>
-      <div class="description"></div>
-      ${progress(1)}
-      <div class="progressSection">
-        <div class="progressSection__text"></div>
-      </div>
-      <a class="link link--action"
-        href="/"
-        onclick=${sendNew}>${state.translate('sendYourFilesLink')}</a>
-    </div>`;
-
-  async function sendNew(e) {
-    e.preventDefault();
-    await fadeOut('.page');
-    emit('pushState', '/');
-  }
-};

Some files were not shown because too many files changed in this diff