Implement section 2.1 and parts of section 4.1 from the Send Android spec (#901)

* Fix #877 Implement Start page (Empty State) Design

* Update some kotlin and android sdk things, and update to the latest code in vnext

* Begin implementing the card ui which shows after uploading.

* Implement a progress bar.
This commit is contained in:
Donovan Preston 2018-08-15 15:29:29 -04:00 committed by GitHub
parent 29bafe1bae
commit 071e283f87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 157 additions and 97 deletions

View File

@ -1,6 +1,6 @@
/* global window, document, fetch */ /* global window, document, fetch */
const MAXFILESIZE = 1024 * 1024 * 1024 * 2; window.MAXFILESIZE = 1024 * 1024 * 1024 * 2;
const EventEmitter = require('events'); const EventEmitter = require('events');
const emitter = new EventEmitter(); const emitter = new EventEmitter();
@ -32,41 +32,58 @@ function dom(tagName, attributes, children = []) {
function uploadComplete(file) { function uploadComplete(file) {
document.body.innerHTML = ''; document.body.innerHTML = '';
const input = dom('input', { id: 'url', value: file.url }); const input = dom('input', { id: 'url', value: file.url, readonly: 'true' });
const copy = dom( const copyText = dom('span', {}, 'Copy link');
'button', const copyImage = dom('img', { id: 'copy-image', src: 'copy-link.png' });
const node = dom(
'div',
{ id: 'white' },
dom('div', { className: 'card' }, [
dom('div', {}, 'The card contents will be here.'),
dom('div', {}, [
'Expires after: ',
dom('span', { className: 'expiresAfter' }, 'exp')
]),
input,
dom(
'div',
{ {
id: 'copy-button', id: 'copy-link',
className: 'button', onclick: e => {
onclick: () => { e.preventDefault();
input.select(); input.select();
document.execCommand('copy'); document.execCommand('copy');
input.blur(); input.selectionEnd = input.selectionStart;
copy.textContent = 'Copied!'; copyText.textContent = 'Copied!';
setTimeout(function() { setTimeout(function() {
copy.textContent = 'Copy to clipboard'; copyText.textContent = 'Copy link';
}, 2000); }, 2000);
} }
}, },
'Copy to clipboard' [copyImage, copyText]
); ),
const node = dom( dom('img', {
'div', id: 'send-another',
{ id: 'striped' }, src: 'cloud-upload.png',
dom('div', { id: 'white' }, [ onclick: () => {
input, render();
copy, document.getElementById('label').click();
dom( }
'button', })
{ id: 'send-another', className: 'button', onclick: render },
'Send another file'
)
]) ])
); );
document.body.appendChild(node); document.body.appendChild(node);
} }
const state = { const state = {
translate: (...toTranslate) => {
return toTranslate.map(o => JSON.stringify(o)).toString();
},
raven: {
captureException: e => {
console.error('ERROR ' + e + ' ' + e.stack);
}
},
storage: { storage: {
files: [], files: [],
remove: function(fileId) { remove: function(fileId) {
@ -92,21 +109,30 @@ function upload(event) {
if (file.size === 0) { if (file.size === 0) {
return; return;
} }
if (file.size > MAXFILESIZE) {
console.log('file too big (no bigger than ' + MAXFILESIZE + ')');
return;
}
emitter.emit('upload', { file: file, type: 'click' }); emitter.emit('addFiles', { files: [file] });
emitter.emit('upload', {});
} }
function render() { function render() {
document.body.innerHTML = ''; document.body.innerHTML = '';
const striped = dom( const node = dom(
'div', 'div',
{ id: 'striped' }, { id: 'white' },
dom('div', { id: 'white' }, [ dom('div', { id: 'centering' }, [
dom('label', { id: 'label', htmlFor: 'input' }, 'Choose file'), dom('img', { src: 'encrypted-envelope.png' }),
dom('h4', {}, 'Private, Encrypted File Sharing'),
dom(
'div',
{},
'Send files through a safe, private, and encrypted link that automatically expires to ensure your stuff does not remain online forever.'
),
dom('div', { id: 'spacer' }),
dom(
'label',
{ id: 'label', htmlFor: 'input' },
dom('img', { src: 'cloud-upload.png' }, [])
),
dom('input', { dom('input', {
id: 'input', id: 'input',
type: 'file', type: 'file',
@ -115,19 +141,42 @@ function render() {
}) })
]) ])
); );
document.body.appendChild(striped); document.body.appendChild(node);
} }
emitter.on('render', function() { emitter.on('render', function() {
if (!state.transfer || !state.transfer.progress) {
return;
}
document.body.innerHTML = ''; document.body.innerHTML = '';
const percent = const percent = Math.floor(state.transfer.progressRatio * 100);
(state.transfer.progress[0] / state.transfer.progress[1]) * 100;
const node = dom( const node = dom(
'div', 'div',
{ style: 'background-color: white; width: 100%' }, { id: 'white', style: 'width: 90%' },
dom('span', { dom('div', { className: 'card' }, [
style: `display: inline-block; width: ${percent}%; background-color: blue` dom('div', {}, `${percent}%`),
}) dom(
'span',
{
style: `display: inline-block; height: 4px; border-radius: 2px; width: ${percent}%; background-color: #1b96ef; color: white`
},
'.'
),
dom(
'div',
{
style: 'text-align: right',
onclick: e => {
e.preventDefault();
if (state.uploading) {
emitter.emit('cancel');
render();
}
}
},
'CANCEL'
)
])
); );
document.body.appendChild(node); document.body.appendChild(node);
}); });
@ -150,8 +199,10 @@ window.addEventListener(
fetch(event.data) fetch(event.data)
.then(res => res.blob()) .then(res => res.blob())
.then(blob => { .then(blob => {
emitter.emit('upload', { file: blob, type: 'share' }); emitter.emit('addFiles', { files: [blob] });
}); emitter.emit('upload', {});
})
.catch(e => console.error('ERROR ' + e + ' ' + e.stack));
}, },
false false
); );

View File

@ -43,7 +43,7 @@
</option> </option>
<option name="pluginClasspaths"> <option name="pluginClasspaths">
<array> <array>
<option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-android-extensions/1.2.50/a5309d96fd097320a75947d2e9673a86c948f605/kotlin-android-extensions-1.2.50.jar" /> <option value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-android-extensions/1.2.60/69596054ff0e04bb3f38cd906dce6854a9d3438d/kotlin-android-extensions-1.2.60.jar" />
</array> </array>
</option> </option>
</compilerArguments> </compilerArguments>
@ -150,15 +150,12 @@
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:27.1.1@jar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:support-annotations:27.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-27.1.1" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-compat-27.1.1" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:support-compat-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.50@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel-1.1.0" level="project" /> <orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel-1.1.0" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.squareup:javawriter:2.1.1@jar" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: com.squareup:javawriter:2.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable-27.1.1" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-ui-27.1.1" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:support-core-ui-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-1.1.2" level="project" /> <orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-1.1.2" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.50@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-utils-27.1.1" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:support-core-utils-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.50@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0@jar" level="project" /> <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.google.code.findbugs:jsr305:2.0.1@jar" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: com.google.code.findbugs:jsr305:2.0.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.github.delight-im:Android-AdvancedWebView-v3.0.0" level="project" /> <orderEntry type="library" name="Gradle: com.github.delight-im:Android-AdvancedWebView-v3.0.0" level="project" />
@ -172,10 +169,13 @@
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-27.1.1" level="project" /> <orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-27.1.1" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-idling-resource-3.0.2" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-idling-resource-3.0.2" level="project" />
<orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-solver:1.1.2@jar" level="project" /> <orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-solver:1.1.2@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.60@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core-1.1.0" level="project" /> <orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core-1.1.0" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-library:1.3@jar" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-library:1.3@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-integration:1.3@jar" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-integration:1.3@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.60@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.0@jar" level="project" /> <orderEntry type="library" name="Gradle: android.arch.core:common:1.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.60@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: net.sf.kxml:kxml2:2.3.0@jar" level="project" /> <orderEntry type="library" scope="TEST" name="Gradle: net.sf.kxml:kxml2:2.3.0@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.1.0" level="project" /> <orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.1.0" level="project" />
</component> </component>

View File

@ -8,7 +8,7 @@ android {
compileSdkVersion 27 compileSdkVersion 27
defaultConfig { defaultConfig {
applicationId "com.mozilla.send.sendandroid" applicationId "com.mozilla.send.sendandroid"
minSdkVersion 24 minSdkVersion 26
targetSdkVersion 27 targetSdkVersion 27
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"

View File

@ -12,6 +12,7 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="false" />
<activity android:name=".MainActivity" android:screenOrientation="portrait"> <activity android:name=".MainActivity" android:screenOrientation="portrait">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,20 +1,11 @@
body { html {
background: url('background_1.jpg'); height: 100vh;
display: flex;
flex-direction: row;
flex: auto;
justify-content: center;
align-items: center;
padding: 0 20px;
box-sizing: border-box;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
} }
#striped { body {
box-sizing: border-box;
margin: 0;
min-height: 100vh;
background-image: repeating-linear-gradient( background-image: repeating-linear-gradient(
45deg, 45deg,
white, white,
@ -26,32 +17,37 @@ body {
#0083ff 30px, #0083ff 30px,
#0083ff 50px #0083ff 50px
); );
height: 350px;
width: 480px;
} }
#white { #white {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: white;
margin: 0 10px;
padding: 10px;
text-align: center;
}
#centering {
flex: 1;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background-color: white; width: 100%;
margin: 0 10px;
padding: 1px 10px 0 10px;
} }
#label { #label {
background: #0297f8; position: fixed;
border: 1px solid #0297f8; right: 2em;
color: white; bottom: 1em;
font-size: 24px; }
font-weight: 500;
#label img {
height: 60px; height: 60px;
width: 200px; width: 60px;
display: flex;
justify-content: center;
align-items: center;
} }
#input { #input {
@ -59,26 +55,38 @@ body {
} }
#url { #url {
flex: 1; display: none;
width: 100%;
height: 32px;
font-size: 24px;
margin-top: 1em;
} }
.button { #copy-link {
flex: 1; text-align: right;
display: block; }
background: #0297f8;
border: 1px solid #0297f8; #copy-image {
color: white; position: relative;
font-size: 24px; top: 6px;
font-weight: 500; height: 30px;
width: 95%; width: 30px;
height: 32px; }
margin-top: 1em;
.spacer {
height: 12em;
} }
#send-another { #send-another {
margin-bottom: 1em; margin-bottom: 1em;
height: 60px;
width: 60px;
position: fixed;
right: 2em;
bottom: 1em;
}
.card {
margin: 6px;
padding: 6px;
border: 1px solid white;
border-radius: 5px;
box-shadow: 5px 5px 5px 5px #d5d5d5;
text-align: left;
} }

View File

@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.2.50' ext.kotlin_version = '1.2.60'
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.1.3' classpath 'com.android.tools.build:gradle:3.1.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.60"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files