Merge pull request #904 from mozilla/android-port-to-choo-2

Fix #896 Port Send Android to choo
This commit is contained in:
Danny Coates 2018-08-16 12:48:42 -07:00 committed by GitHub
commit 70bc2b7656
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 195 additions and 206 deletions

View file

@ -1,210 +1,14 @@
/* global window, document, fetch */
/* global window */
window.MAXFILESIZE = 1024 * 1024 * 1024 * 2;
const EventEmitter = require('events');
const emitter = new EventEmitter();
const choo = require('choo');
const app = choo();
function dom(tagName, attributes, children = []) {
const node = document.createElement(tagName);
for (const name in attributes) {
if (name.indexOf('on') === 0) {
node[name] = attributes[name];
} else if (name === 'htmlFor') {
node.htmlFor = attributes.htmlFor;
} else if (name === 'className') {
node.className = attributes.className;
} else {
node.setAttribute(name, attributes[name]);
}
}
if (!(children instanceof Array)) {
children = [children];
}
for (let child of children) {
if (typeof child === 'string') {
child = document.createTextNode(child);
}
node.appendChild(child);
}
return node;
}
function uploadComplete(file) {
document.body.innerHTML = '';
const input = dom('input', { id: 'url', value: file.url, readonly: 'true' });
const copyText = dom('span', {}, 'Copy link');
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-link',
onclick: e => {
e.preventDefault();
input.select();
document.execCommand('copy');
input.selectionEnd = input.selectionStart;
copyText.textContent = 'Copied!';
setTimeout(function() {
copyText.textContent = 'Copy link';
}, 2000);
}
},
[copyImage, copyText]
),
dom('img', {
id: 'send-another',
src: 'cloud-upload.png',
onclick: () => {
render();
document.getElementById('label').click();
}
})
])
);
document.body.appendChild(node);
}
const state = {
translate: (...toTranslate) => {
return toTranslate.map(o => JSON.stringify(o)).toString();
},
raven: {
captureException: e => {
console.error('ERROR ' + e + ' ' + e.stack);
}
},
storage: {
files: [],
remove: function(fileId) {
console.log('REMOVE FILEID', fileId);
},
writeFile: function(file) {
console.log('WRITEFILE', file);
},
addFile: uploadComplete,
totalUploads: 0
},
transfer: null,
uploading: false,
settingPassword: false,
passwordSetError: null,
route: '/'
};
function upload(event) {
event.preventDefault();
const target = event.target;
const file = target.files[0];
if (file.size === 0) {
return;
}
emitter.emit('addFiles', { files: [file] });
emitter.emit('upload', {});
}
function render() {
document.body.innerHTML = '';
const node = dom(
'div',
{ id: 'white' },
dom('div', { id: 'centering' }, [
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', {
id: 'input',
type: 'file',
name: 'input',
onchange: upload
})
])
);
document.body.appendChild(node);
}
emitter.on('render', function() {
if (!state.transfer || !state.transfer.progress) {
return;
}
document.body.innerHTML = '';
const percent = Math.floor(state.transfer.progressRatio * 100);
const node = dom(
'div',
{ id: 'white', style: 'width: 90%' },
dom('div', { className: 'card' }, [
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);
});
emitter.on('pushState', function(path) {
console.log('pushState ' + path + ' ' + JSON.stringify(state));
});
const fileManager = require('../app/fileManager').default;
try {
fileManager(state, emitter);
} catch (e) {
console.error('error' + e);
console.error(e);
}
window.addEventListener(
'message',
event => {
fetch(event.data)
.then(res => res.blob())
.then(blob => {
emitter.emit('addFiles', { files: [blob] });
emitter.emit('upload', {});
})
.catch(e => console.error('ERROR ' + e + ' ' + e.stack));
},
false
);
render();
app.use(require('./stores/state').default);
app.use(require('../app/fileManager').default);
app.use(require('./stores/intents').default);
app.route('/', require('./pages/home').default);
app.route('/upload', require('./pages/upload').default);
app.route('/share/:id', require('./pages/share').default);
app.mount('body');

View file

@ -90,3 +90,15 @@ body {
box-shadow: 5px 5px 5px 5px #d5d5d5;
text-align: left;
}
.progress {
display: inline-block;
height: 4px;
border-radius: 2px;
background-color: #1b96ef;
color: white;
}
.cancel {
text-align: right;
}

View file

@ -36,6 +36,7 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener {
mWebView!!.setWebChromeClient(LoggingWebChromeClient())
val webSettings = mWebView!!.getSettings()
webSettings.setAllowUniversalAccessFromFileURLs(true)
webSettings.setJavaScriptEnabled(true)
val intent = getIntent()

View file

@ -0,0 +1,6 @@
env:
browser: true
parserOptions:
sourceType: module

33
android/pages/home.js Normal file
View file

@ -0,0 +1,33 @@
const html = require('choo/html');
export default function mainPage(state, emit) {
function uploadFile(event) {
event.preventDefault();
const target = event.target;
const file = target.files[0];
if (file.size === 0) {
return;
}
emit('pushState', '/upload');
emit('addFiles', { files: [file] });
emit('upload', {});
}
return html`<body>
<div id="white">
<div id="centering">
<img src=${state.getAsset('encrypted-envelope.png')} />
<h4>Private, Encrypted File Sharing</h4>
<div>
Send files through a safe, private, and encrypted link that automatically expires to ensure your stuff does not remain online forever.
</div>
<div id="spacer">
</div>
<label id="label" for="input">
<img src=${state.getAsset('cloud-upload.png')} />
</label>
<input id="input" name="input" type="file" onchange=${uploadFile} />
</div>
</div>
</body>`;
}

47
android/pages/share.js Normal file
View file

@ -0,0 +1,47 @@
const html = require('choo/html');
export default function uploadComplete(state, emit) {
const file = state.storage.files[state.storage.files.length - 1];
function onclick(e) {
e.preventDefault();
input.select();
document.execCommand('copy');
input.selectionEnd = input.selectionStart;
copyText.textContent = 'Copied!';
setTimeout(function() {
copyText.textContent = 'Copy link';
}, 2000);
}
function uploadFile(event) {
event.preventDefault();
const target = event.target;
const file = target.files[0];
if (file.size === 0) {
return;
}
emit('pushState', '/upload');
emit('addFiles', { files: [file] });
emit('upload', {});
}
const input = html`<input id="url" value=${file.url} readonly="true" />`;
const copyText = html`<span>Copy link</span>`;
return html`<body>
<div id="white">
<div class="card">
<div>The card contents will be here.</div>
<div>Expires after: <span class="expires-after">exp</span></div>
${input}
<div id="copy-link" onclick=${onclick}>
<img id="copy-image" src=${state.getAsset('copy-link.png')} />
${copyText}
</div>
<label id="label" for="input">
<img src=${state.getAsset('cloud-upload.png')} />
</label>
<input id="input" name="input" type="file" onchange=${uploadFile} />
</div>
</body>`;
}

24
android/pages/upload.js Normal file
View file

@ -0,0 +1,24 @@
const html = require('choo/html');
export default function progressBar(state, emit) {
let percent = 0;
if (state.transfer && state.transfer.progress) {
percent = Math.floor(state.transfer.progressRatio * 100);
}
function onclick(e) {
e.preventDefault();
if (state.uploading) {
emit('cancel');
}
emit('pushState', '/');
}
return html`<body>
<div id="white">
<div class="card">
<div>${percent}%</div>
<span class="progress" style="width: ${percent}%">.</span>
<div class="cancel" onclick=${onclick}>CANCEL</div>
</div>
</div>
</body>`;
}

View file

@ -0,0 +1,6 @@
env:
browser: true
parserOptions:
sourceType: module

17
android/stores/intents.js Normal file
View file

@ -0,0 +1,17 @@
/* eslint-disable no-console */
export default function intentHandler(state, emitter) {
window.addEventListener(
'message',
event => {
fetch(event.data)
.then(res => res.blob())
.then(blob => {
emitter.emit('addFiles', { files: [blob] });
emitter.emit('upload', {});
})
.catch(e => console.error('ERROR ' + e + ' ' + e.stack));
},
false
);
}

39
android/stores/state.js Normal file
View file

@ -0,0 +1,39 @@
/* eslint-disable no-console */
export default function initialState(state, emitter) {
const files = [];
Object.assign(state, {
getAsset(name) {
return `/android_asset/${name}`;
},
translate: (...toTranslate) => {
return toTranslate.map(o => JSON.stringify(o)).toString();
},
raven: {
captureException: e => {
console.error('ERROR ' + e + ' ' + e.stack);
}
},
storage: {
files,
remove: function(fileId) {
console.log('REMOVE FILEID', fileId);
},
writeFile: function(file) {
console.log('WRITEFILE', file);
},
addFile: function(file) {
console.log('addfile' + JSON.stringify(file));
files.push(file);
emitter.emit('pushState', `/share/${file.id}`);
},
totalUploads: 0
},
transfer: null,
uploading: false,
settingPassword: false,
passwordSetError: null,
route: '/'
});
}