converting some things to choo/component

This commit is contained in:
Danny Coates 2018-11-12 11:13:31 -08:00
parent a576d54d64
commit 037c79730d
No known key found for this signature in database
GPG Key ID: 4C442633C62E00CB
16 changed files with 270 additions and 381 deletions

1
.gitignore vendored
View File

@ -6,6 +6,7 @@ dist
.nyc_output .nyc_output
.tox .tox
.pytest_cache .pytest_cache
*.iml
android/app/src/main/assets android/app/src/main/assets
ios/send-ios/assets/ios.js ios/send-ios/assets/ios.js
ios/send-ios/assets/vendor.js ios/send-ios/assets/vendor.js

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="android" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -24,7 +24,7 @@ import html from 'choo/html';
import Raven from 'raven-js'; import Raven from 'raven-js';
import assets from '../common/assets'; import assets from '../common/assets';
import header from '../app/ui/header'; import Header from '../app/ui/header';
import locale from '../common/locales'; import locale from '../common/locales';
import storage from '../app/storage'; import storage from '../app/storage';
import controller from '../app/controller'; import controller from '../app/controller';
@ -59,7 +59,7 @@ function body(main) {
> >
<img src="${assets.get('preferences.png')}" /> <img src="${assets.get('preferences.png')}" />
</a> </a>
${header(state, emit)} ${main(state, emit)} ${state.cache(Header, 'header').render()} ${main(state, emit)}
</body> </body>
`; `;

View File

@ -1,196 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<afterSyncTasks>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JVM 1.8" useProjectSettings="false">
<compilerSettings />
<compilerArguments>
<option name="destination" value="$MODULE_DIR$/build/tmp/kotlin-classes/debug" />
<option name="noStdlib" value="true" />
<option name="noReflect" value="true" />
<option name="moduleName" value="app_debug" />
<option name="jvmTarget" value="1.8" />
<option name="addCompilerBuiltIns" value="true" />
<option name="loadBuiltInsFromDependencies" value="true" />
<option name="languageVersion" value="1.2" />
<option name="apiVersion" value="1.2" />
<option name="pluginOptions">
<array>
<option value="plugin:org.jetbrains.kotlin.android:experimental=false" />
<option value="plugin:org.jetbrains.kotlin.android:enabled=true" />
<option value="plugin:org.jetbrains.kotlin.android:defaultCacheImplementation=hashMap" />
</array>
</option>
<option name="pluginClasspaths">
<array>
<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>
</option>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
<output url="file://$MODULE_DIR$/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
<output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources/debug/processDebugResources/r" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources/debugAndroidTest/processDebugAndroidTestResources/r" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotation_processor_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/apk_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/build-info" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-libraries" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/checkDebugClasspath" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_main_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_split_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/kotlin" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 27 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Gradle: net.java.dev.jna:jna-4.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:runner-1.0.2" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:27.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.61@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:support-compat-27.1.1" 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" 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.constraint:constraint-layout-1.1.2" 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: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" name="Gradle: com.github.delight-im:Android-AdvancedWebView-v3.0.0" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-core-3.0.2" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-android:0.23.4@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-fragment-27.1.1" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:runtime-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.61@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3@jar" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:monitor-1.0.2" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: org.mozilla.components:service-firefox-accounts-0.26.0" 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: org.mozilla.fxa_client:fxa_client-0.5.1" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlinx:kotlinx-coroutines-core-common:0.23.4@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" name="Gradle: org.jetbrains.kotlinx:atomicfu-common:0.10.3@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-common:1.2.61@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" />
</component>
</module>

View File

@ -35,8 +35,7 @@ dependencies {
} }
task generateAndLinkBundle(type: Exec, description: 'Generate the android.js bundle and link it into the assets directory') { task generateAndLinkBundle(type: Exec, description: 'Generate the android.js bundle and link it into the assets directory') {
commandLine 'node' commandLine './buildAssets.sh'
args '../generateAndLinkBundle.js'
} }
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {

7
android/app/buildAssets.sh Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
npm run build
rm -rf src/main/assets
mkdir -p src/main/assets
cp -R ../../dist/* src/main/assets
sed -i '' 's/url(/url(\/android_asset/g' src/main/assets/app.*.css

View File

@ -1,10 +0,0 @@
const child_process = require('child_process');
const path = require('path');
child_process.execSync('npm run build');
child_process.execSync(
`cp -R ${path.resolve(__dirname, '../dist/*')} ${path.resolve(
__dirname,
'app/src/main/assets'
)}`
);

View File

@ -1,5 +1,7 @@
import 'fast-text-encoding'; // MS Edge support import 'fast-text-encoding'; // MS Edge support
import 'fluent-intl-polyfill'; import 'fluent-intl-polyfill';
import choo from 'choo';
import nanotiming from 'nanotiming';
import routes from './routes'; import routes from './routes';
import capabilities from './capabilities'; import capabilities from './capabilities';
import locale from '../common/locales'; import locale from '../common/locales';
@ -14,7 +16,10 @@ import './main.css';
import User from './user'; import User from './user';
(async function start() { (async function start() {
const app = routes(); const app = routes(choo());
if (process.env.NODE_ENV === 'production') {
nanotiming.disabled = true;
}
if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) { if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) {
Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install(); Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install();
} }

View File

@ -1,37 +1,8 @@
const choo = require('choo'); const choo = require('choo');
const html = require('choo/html');
const nanotiming = require('nanotiming');
const download = require('./ui/download'); const download = require('./ui/download');
const footer = require('./ui/footer'); const body = require('./ui/body');
const fxPromo = require('./ui/fxPromo');
const header = require('./ui/header');
nanotiming.disabled = true; module.exports = function(app = choo()) {
function banner(state, emit) {
if (state.promo && !state.route.startsWith('/unsupported/')) {
return fxPromo(state, emit);
}
}
function body(main) {
return function(state, emit) {
const b = html`<body class="flex flex-col items-center font-sans bg-blue-lightest md:h-screen md:bg-grey-lightest">
${banner(state, emit)}
${header(state, emit)}
${main(state, emit)}
${footer(state)}
</body>`;
if (state.layout) {
// server side only
return state.layout(state, b);
}
return b;
};
}
module.exports = function() {
const app = choo();
app.route('/', body(require('./ui/home'))); app.route('/', body(require('./ui/home')));
app.route('/download/:id', body(download)); app.route('/download/:id', body(download));
app.route('/download/:id/:key', body(download)); app.route('/download/:id/:key', body(download));

View File

@ -1,58 +1,101 @@
const html = require('choo/html'); const html = require('choo/html');
const Component = require('choo/component');
module.exports = function(state, emit) { class Account extends Component {
if (!state.capabilities.account) { constructor(name, state, emit) {
return null; super(name);
this.state = state;
this.emit = emit;
this.enabled = state.capabilities.account;
this.local = state.components[name] = {};
this.setState();
} }
const user = state.user;
if (!user.loggedIn) {
return html`<button
class="p-2 border rounded border-white text-white hover:bg-white hover:text-blue md:text-blue md:border-blue md:hover:text-white md:hover:bg-blue"
onclick=${login}>
${state.translate('signInMenuOption')}
</button>`;
}
return html`<div class="relative h-8">
<input
type="image"
alt="${user.email}"
class="w-8 h-8 rounded-full text-white"
src="${user.avatar}"
onclick=${avatarClick}/>
<ul
id="accountMenu"
class="invisible list-reset absolute pin-t pin-r mt-10 pt-2 pb-2 bg-white shadow-md whitespace-no-wrap outline-none z-50"
onblur="${hideMenu}"
tabindex="-1">
<li class="p-2 text-grey-dark">${user.email}</li>
<li>
<a class="block px-4 py-2 text-grey-darkest hover:bg-blue hover:text-white cursor-pointer" onclick=${logout}>
${state.translate('logOut')}
</a>
</li>
</ul>
</div>`;
function avatarClick(event) { avatarClick(event) {
event.preventDefault(); event.preventDefault();
const menu = document.getElementById('accountMenu'); const menu = document.getElementById('accountMenu');
menu.classList.toggle('invisible'); menu.classList.toggle('invisible');
menu.focus(); menu.focus();
} }
function hideMenu(event) { hideMenu(event) {
event.stopPropagation(); event.stopPropagation();
const menu = document.getElementById('accountMenu'); const menu = document.getElementById('accountMenu');
menu.classList.add('invisible'); menu.classList.add('invisible');
} }
function login(event) { login(event) {
event.preventDefault(); event.preventDefault();
emit('login'); this.emit('login');
} }
function logout(event) { logout(event) {
event.preventDefault(); event.preventDefault();
emit('logout'); this.emit('logout');
} }
};
changed() {
return this.local.loggedIn !== this.state.user.loggedIn;
}
setState() {
const changed = this.changed();
if (changed) {
this.local.loggedIn = this.state.user.loggedIn;
}
return changed;
}
update() {
return this.setState();
}
createElement() {
if (!this.enabled) {
return null;
}
const user = this.state.user;
const translate = this.state.translate;
if (!this.local.loggedIn) {
return html`
<div>
<button
class="p-2 border rounded border-white text-white hover:bg-white hover:text-blue md:text-blue md:border-blue md:hover:text-white md:hover:bg-blue"
onclick="${e => this.login(e)}"
>
${translate('signInMenuOption')}
</button>
</div>
`;
}
return html`
<div class="relative h-8">
<input
type="image"
alt="${user.email}"
class="w-8 h-8 rounded-full text-white"
src="${user.avatar}"
onclick="${e => this.avatarClick(e)}"
/>
<ul
id="accountMenu"
class="invisible list-reset absolute pin-t pin-r mt-10 pt-2 pb-2 bg-white shadow-md whitespace-no-wrap outline-none z-50"
onblur="${e => this.hideMenu(e)}"
tabindex="-1"
>
<li class="p-2 text-grey-dark">${user.email}</li>
<li>
<a
class="block px-4 py-2 text-grey-darkest hover:bg-blue hover:text-white cursor-pointer"
onclick="${e => this.logout(e)}"
>
${translate('logOut')}
</a>
</li>
</ul>
</div>
`;
}
}
module.exports = Account;

28
app/ui/body.js Normal file
View File

@ -0,0 +1,28 @@
const html = require('choo/html');
const Promo = require('./promo');
const Header = require('./header');
const Footer = require('./footer');
function banner(state) {
if (state.promo && !state.route.startsWith('/unsupported/')) {
return state.cache(Promo, 'promo').render();
}
}
module.exports = function body(main) {
return function(state, emit) {
const b = html`
<body
class="flex flex-col items-center font-sans bg-blue-lightest md:h-screen md:bg-grey-lightest"
>
${banner(state, emit)} ${state.cache(Header, 'header').render()}
${main(state, emit)} ${state.cache(Footer, 'footer').render()}
</body>
`;
if (state.layout) {
// server side only
return state.layout(state, b);
}
return b;
};
};

View File

@ -1,52 +1,74 @@
const html = require('choo/html'); const html = require('choo/html');
const Component = require('choo/component');
const version = require('../../package.json').version; const version = require('../../package.json').version;
const { browserName } = require('../utils'); const { browserName } = require('../utils');
module.exports = function(state) { class Footer extends Component {
const browser = browserName(); constructor(name, state) {
const feedbackUrl = `https://qsurvey.mozilla.com/s3/txp-firefox-send?ver=${version}&browser=${browser}`; super(name);
const footer = html`<footer class="flex flex-col md:flex-row items-start w-full flex-none self-start p-6 font-medium text-xs text-grey-dark md:items-center justify-between bg-grey-lightest"> this.state = state;
<a class="mozilla-logo pb-10 md:pb-0 m-2" }
href="https://www.mozilla.org/">
Mozilla update() {
</a> return false;
<ul class="list-reset flex flex-col md:flex-row items-start md:items-center md:justify-end"> }
<li class="m-2"><a
href="https://www.mozilla.org/about/legal"> createElement() {
${state.translate('footerLinkLegal')} const translate = this.state.translate;
</a></li> const browser = browserName();
<li class="m-2"><a const feedbackUrl = `https://qsurvey.mozilla.com/s3/txp-firefox-send?ver=${version}&browser=${browser}`;
href="/legal"> return html`
${state.translate('footerLinkTerms')} <footer
</a></li> class="flex flex-col md:flex-row items-start w-full flex-none self-start p-6 font-medium text-xs text-grey-dark md:items-center justify-between bg-grey-lightest"
<li class="m-2"><a >
href="https://www.mozilla.org/privacy/websites/#cookies"> <a
${state.translate('footerLinkCookies')} class="mozilla-logo pb-10 md:pb-0 m-2"
</a></li> href="https://www.mozilla.org/"
<li class="m-2"><a >
href="https://www.mozilla.org/about/legal/report-infringement/"> Mozilla
${state.translate('reportIPInfringement')} </a>
</a></li> <ul
<li class="m-2"><a class="list-reset flex flex-col md:flex-row items-start md:items-center md:justify-end"
href="https://github.com/mozilla/send">GitHub >
</a></li> <li class="m-2">
<li class="m-2"><a <a href="https://www.mozilla.org/about/legal">
href="https://twitter.com/FxTestPilot">Twitter ${translate('footerLinkLegal')}
</a></li> </a>
<li class="m-2"><a href="${feedbackUrl}" </li>
rel="noreferrer noopener" <li class="m-2">
class="feedback-link" <a href="/legal"> ${translate('footerLinkTerms')} </a>
alt="Feedback" </li>
target="_blank"> <li class="m-2">
${state.translate('siteFeedback')} <a href="https://www.mozilla.org/privacy/websites/#cookies">
</a></li> ${translate('footerLinkCookies')}
</ul> </a>
</footer>`; </li>
// HACK <li class="m-2">
// We only want to render this once because we <a href="https://www.mozilla.org/about/legal/report-infringement/">
// toggle the targets of the links with utils/openLinksInNewTab ${translate('reportIPInfringement')}
footer.isSameNode = function(target) { </a>
return target && target.nodeName && target.nodeName === 'FOOTER'; </li>
}; <li class="m-2">
return footer; <a href="https://github.com/mozilla/send">GitHub </a>
}; </li>
<li class="m-2">
<a href="https://twitter.com/FxTestPilot">Twitter </a>
</li>
<li class="m-2">
<a
href="${feedbackUrl}"
rel="noreferrer noopener"
class="feedback-link"
alt="Feedback"
target="_blank"
>
${translate('siteFeedback')}
</a>
</li>
</ul>
</footer>
`;
}
}
module.exports = Footer;

View File

@ -1,19 +0,0 @@
const html = require('choo/html');
const assets = require('../../common/assets');
module.exports = function() {
return html`
<div class="w-full flex-none flex flex-row items-center content-center justify-center text-sm bg-grey-light leading-tight text-grey-darkest px-4 py-3">
<div class="flex items-center mx-auto">
<img
src="${assets.get('firefox_logo-only.svg')}"
class="w-6"
alt="Firefox"/>
<span class="ml-3">Send is brought to you by the all-new Firefox.
<a
class="text-blue"
href="https://www.mozilla.org/firefox/new/?utm_campaign=send-acquisition&utm_medium=referral&utm_source=send.firefox.com">Download Firefox now </a>
</span>
</div>
</div>`;
};

View File

@ -1,25 +1,34 @@
const html = require('choo/html'); const html = require('choo/html');
const account = require('./account'); const Component = require('choo/component');
const Account = require('./account');
module.exports = function(state, emit) { class Header extends Component {
const header = html` constructor(name, state, emit) {
<header class="relative flex-none flex flex-row items-center justify-between bg-blue md:bg-white w-full px-6 h-16 md:shadow z-20"> super(name);
<a this.state = state;
class="header-logo" this.emit = emit;
href="/"> this.account = state.cache(Account, 'account');
<h1 class="text-white md:text-black font-normal">Firefox <b>Send</b></h1> }
</a>
${account(state, emit)} update() {
<div class="invisible absolute pin-t pin-l mt-12 w-full flex flex-col items-center pointer-events-none"> this.account.render();
<div class="border rounded bg-grey-darkest text-white mt-2 p-2">Your upload has finished.<button class="border border-blue rounded-sm bg-blue text-white inline-block p-1 ml-2">Copy Link</button><button class="text-white inline-block p-1 ml-2"></button></div> return false;
${state.toast ? state.toast() : ''} }
</div>
</header>`; createElement() {
// HACK return html`
// We only want to render this once because we <header
// toggle the targets of the links with utils/openLinksInNewTab class="relative flex-none flex flex-row items-center justify-between bg-blue md:bg-white w-full px-6 h-16 md:shadow z-20"
// header.isSameNode = function(target) { >
// return target && target.nodeName && target.nodeName === 'HEADER'; <a class="header-logo" href="/">
// }; <h1 class="text-white md:text-black font-normal">
return header; Firefox <b>Send</b>
}; </h1>
</a>
${this.account.render()}
</header>
`;
}
}
module.exports = Header;

39
app/ui/promo.js Normal file
View File

@ -0,0 +1,39 @@
const html = require('choo/html');
const Component = require('choo/component');
const assets = require('../../common/assets');
class Promo extends Component {
constructor(name) {
super(name);
}
update() {
return false;
}
createElement() {
return html`
<div
class="w-full flex-none flex flex-row items-center content-center justify-center text-sm bg-grey-light leading-tight text-grey-darkest px-4 py-3"
>
<div class="flex items-center mx-auto">
<img
src="${assets.get('firefox_logo-only.svg')}"
class="w-6"
alt="Firefox"
/>
<span class="ml-3"
>Send is brought to you by the all-new Firefox.
<a
class="text-blue"
href="https://www.mozilla.org/firefox/new/?utm_campaign=send-acquisition&utm_medium=referral&utm_source=send.firefox.com"
>Download Firefox now </a
>
</span>
</div>
</div>
`;
}
}
module.exports = Promo;

View File

@ -185,6 +185,7 @@ const web = {
from: '*.*' from: '*.*'
} }
]), ]),
new webpack.EnvironmentPlugin(['NODE_ENV']),
new webpack.IgnorePlugin(/\.\.\/dist/), // used in common/*.js new webpack.IgnorePlugin(/\.\.\/dist/), // used in common/*.js
new webpack.IgnorePlugin(/require-from-string/), // used in common/locales.js new webpack.IgnorePlugin(/require-from-string/), // used in common/locales.js
new webpack.HashedModuleIdsPlugin(), new webpack.HashedModuleIdsPlugin(),