Merge pull request #904 from mozilla/android-port-to-choo-2
Fix #896 Port Send Android to choo
This commit is contained in:
commit
70bc2b7656
10 changed files with 195 additions and 206 deletions
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
6
android/pages/.eslintrc.yaml
Normal file
6
android/pages/.eslintrc.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
env:
|
||||
browser: true
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
|
33
android/pages/home.js
Normal file
33
android/pages/home.js
Normal 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
47
android/pages/share.js
Normal 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
24
android/pages/upload.js
Normal 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>`;
|
||||
}
|
6
android/stores/.eslintrc.yaml
Normal file
6
android/stores/.eslintrc.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
env:
|
||||
browser: true
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
|
17
android/stores/intents.js
Normal file
17
android/stores/intents.js
Normal 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
39
android/stores/state.js
Normal 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: '/'
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue