added survey dialog. closes #1307

This commit is contained in:
Danny Coates 2019-04-26 13:30:33 -07:00
parent ce4157ac08
commit 20b9279eec
14 changed files with 113 additions and 32 deletions

View File

@ -1,5 +1,5 @@
/* global AUTH_CONFIG LOCALE */ /* global AUTH_CONFIG */
import { browserName } from './utils'; import { browserName, locale } from './utils';
async function checkCrypto() { async function checkCrypto() {
try { try {
@ -91,7 +91,7 @@ export default async function getCapabilities() {
account = false; account = false;
} }
const share = const share =
typeof navigator.share === 'function' && LOCALE.startsWith('en'); // en until strings merge typeof navigator.share === 'function' && locale().startsWith('en'); // en until strings merge
const standalone = const standalone =
window.matchMedia('(display-mode: standalone)').matches || window.matchMedia('(display-mode: standalone)').matches ||

View File

@ -2,11 +2,12 @@ import FileSender from './fileSender';
import FileReceiver from './fileReceiver'; import FileReceiver from './fileReceiver';
import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils'; import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils';
import * as metrics from './metrics'; import * as metrics from './metrics';
import { bytes } from './utils'; import { bytes, locale } from './utils';
import okDialog from './ui/okDialog'; import okDialog from './ui/okDialog';
import copyDialog from './ui/copyDialog'; import copyDialog from './ui/copyDialog';
import shareDialog from './ui/shareDialog'; import shareDialog from './ui/shareDialog';
import signupDialog from './ui/signupDialog'; import signupDialog from './ui/signupDialog';
import surveyDialog from './ui/surveyDialog';
export default function(state, emitter) { export default function(state, emitter) {
let lastRender = 0; let lastRender = 0;
@ -281,6 +282,22 @@ export default function(state, emitter) {
// metrics.copiedLink({ location }); // metrics.copiedLink({ location });
}); });
emitter.on('closeModal', () => {
if (
state.PREFS.surveyUrl &&
['copy', 'share'].includes(state.modal.type) &&
locale().startsWith('en') &&
(state.storage.totalUploads > 1 || state.storage.totalDownloads > 0) &&
!state.user.surveyed
) {
state.user.surveyed = true;
state.modal = surveyDialog();
} else {
state.modal = null;
}
render();
});
setInterval(() => { setInterval(() => {
// poll for updates of the upload list // poll for updates of the upload list
if (!state.modal && state.route === '/') { if (!state.modal && state.route === '/') {

View File

@ -1,4 +1,4 @@
/* global DEFAULTS LIMITS LOCALE */ /* global DEFAULTS LIMITS PREFS */
import 'core-js'; import 'core-js';
import 'fast-text-encoding'; // MS Edge support import 'fast-text-encoding'; // MS Edge support
import 'fluent-intl-polyfill'; import 'fluent-intl-polyfill';
@ -17,7 +17,7 @@ import './main.css';
import User from './user'; import User from './user';
import { getTranslator } from './locale'; import { getTranslator } from './locale';
import Archive from './archive'; import Archive from './archive';
import { setTranslate } from './utils'; import { setTranslate, locale } from './utils';
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();
@ -45,11 +45,12 @@ if (process.env.NODE_ENV === 'production') {
} }
} }
const translate = await getTranslator(LOCALE); const translate = await getTranslator(locale());
setTranslate(translate); setTranslate(translate);
window.initialState = { window.initialState = {
LIMITS, LIMITS,
DEFAULTS, DEFAULTS,
PREFS,
archive: new Archive([], DEFAULTS.EXPIRE_SECONDS), archive: new Archive([], DEFAULTS.EXPIRE_SECONDS),
capabilities, capabilities,
translate, translate,

View File

@ -1,5 +1,5 @@
import storage from './storage'; import storage from './storage';
import { platform } from './utils'; import { platform, locale } from './utils';
import { sendMetrics } from './api'; import { sendMetrics } from './api';
let appState = null; let appState = null;
@ -7,7 +7,7 @@ let appState = null;
const HOUR = 1000 * 60 * 60; const HOUR = 1000 * 60 * 60;
const events = []; const events = [];
let session_id = Date.now(); let session_id = Date.now();
const lang = document.querySelector('html').lang; const lang = locale();
export default function initialize(state, emitter) { export default function initialize(state, emitter) {
appState = state; appState = state;

View File

@ -2,7 +2,7 @@ const html = require('choo/html');
const { copyToClipboard } = require('../utils'); const { copyToClipboard } = require('../utils');
module.exports = function(name, url) { module.exports = function(name, url) {
return function(state, emit, close) { const dialog = function(state, emit, close) {
return html` return html`
<send-copy-dialog <send-copy-dialog
class="flex flex-col items-center text-center p-4 max-w-sm m-auto" class="flex flex-col items-center text-center p-4 max-w-sm m-auto"
@ -45,4 +45,6 @@ module.exports = function(name, url) {
setTimeout(close, 1000); setTimeout(close, 1000);
} }
}; };
dialog.type = 'copy';
return dialog;
}; };

View File

@ -21,7 +21,6 @@ module.exports = function(state, emit) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
} }
state.modal = null; emit('closeModal');
emit('render');
} }
}; };

View File

@ -1,13 +1,7 @@
const html = require('choo/html'); const html = require('choo/html');
/* Possible strings for l10n
shareLinkDescription = Share the link to your file:
shareLinkButton = Share link
shareMessage = Download "{ $name }" with { -send-brand }: simple, safe file sharing
*/
module.exports = function(name, url) { module.exports = function(name, url) {
return function(state, emit, close) { const dialog = function(state, emit, close) {
return html` return html`
<send-share-dialog <send-share-dialog
class="flex flex-col items-center text-center p-4 max-w-sm m-auto" class="flex flex-col items-center text-center p-4 max-w-sm m-auto"
@ -16,7 +10,7 @@ module.exports = function(name, url) {
${state.translate('notifyUploadEncryptDone')} ${state.translate('notifyUploadEncryptDone')}
</h1> </h1>
<p class="font-normal leading-normal text-grey-darkest word-break-all"> <p class="font-normal leading-normal text-grey-darkest word-break-all">
Share the link to your file:<br /> ${state.translate('shareLinkDescription')}<br />
${name} ${name}
</p> </p>
<input <input
@ -29,9 +23,9 @@ module.exports = function(name, url) {
<button <button
class="btn rounded-lg w-full flex-no-shrink focus:outline" class="btn rounded-lg w-full flex-no-shrink focus:outline"
onclick="${share}" onclick="${share}"
title="Share link" title="${state.translate('shareLinkButton')}"
> >
Share link ${state.translate('shareLinkButton')}
</button> </button>
<button <button
class="text-blue-dark hover:text-blue-darker focus:text-blue-darker my-4 font-medium cursor-pointer focus:outline" class="text-blue-dark hover:text-blue-darker focus:text-blue-darker my-4 font-medium cursor-pointer focus:outline"
@ -48,8 +42,7 @@ module.exports = function(name, url) {
try { try {
await navigator.share({ await navigator.share({
title: state.translate('-send-brand'), title: state.translate('-send-brand'),
text: `Download "${name}" with Firefox Send: simple, safe file sharing`, text: state.translate('shareMessage', { name }),
//state.translate('shareMessage', { name }),
url url
}); });
} catch (e) { } catch (e) {
@ -61,4 +54,6 @@ module.exports = function(name, url) {
close(); close();
} }
}; };
dialog.type = 'share';
return dialog;
}; };

42
app/ui/surveyDialog.js Normal file
View File

@ -0,0 +1,42 @@
const html = require('choo/html');
const version = require('../../package.json').version;
const { browserName } = require('../utils');
module.exports = function() {
return function(state, emit, close) {
const surveyUrl = `${
state.PREFS.surveyUrl
}?ver=${version}&browser=${browserName()}&anon=${
state.user.loggedIn
}&active_count=${state.storage.files.length}`;
return html`
<send-survey-dialog
class="flex flex-col items-center text-center p-4 max-w-sm m-auto"
>
<h1 class="font-bold my-4">
Tell us what you think.
</h1>
<p class="font-normal leading-normal text-grey-darkest px-4">
Love Firefox Send? Take a quick survey to let us know how we can make
it better.
</p>
<a
class="btn rounded-lg w-full flex-no-shrink focus:outline my-5"
onclick="${() => emit('closeModal')}"
title="Give feedback"
href="${surveyUrl}"
target="_blank"
>
Give feedback
</a>
<button
class="text-blue-dark hover:text-blue-darker focus:text-blue-darker font-medium cursor-pointer focus:outline"
onclick="${close}"
title="Skip"
>
Skip
</button>
</send-survey-dialog>
`;
};
};

View File

@ -44,6 +44,14 @@ export default class User {
this.storage.set('firstAction', action); this.storage.set('firstAction', action);
} }
get surveyed() {
return this.storage.get('surveyed');
}
set surveyed(yes) {
this.storage.set('surveyed', yes);
}
get avatar() { get avatar() {
const defaultAvatar = assets.get('user.svg'); const defaultAvatar = assets.get('user.svg');
if (this.info.avatarDefault) { if (this.info.avatarDefault) {

View File

@ -14,6 +14,10 @@ function b64ToArray(str) {
return b64.toByteArray(str + '==='.slice((str.length + 3) % 4)); return b64.toByteArray(str + '==='.slice((str.length + 3) % 4));
} }
function locale() {
return document.querySelector('html').lang;
}
function loadShim(polyfill) { function loadShim(polyfill) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const shim = document.createElement('script'); const shim = document.createElement('script');
@ -67,8 +71,7 @@ function bytes(num) {
let nStr = n.toFixed(decimalDigits); let nStr = n.toFixed(decimalDigits);
if (LOCALIZE_NUMBERS) { if (LOCALIZE_NUMBERS) {
try { try {
const locale = document.querySelector('html').lang; nStr = n.toLocaleString(locale(), {
nStr = n.toLocaleString(locale, {
minimumFractionDigits: decimalDigits, minimumFractionDigits: decimalDigits,
maximumFractionDigits: decimalDigits maximumFractionDigits: decimalDigits
}); });
@ -85,8 +88,7 @@ function bytes(num) {
function percent(ratio) { function percent(ratio) {
if (LOCALIZE_NUMBERS) { if (LOCALIZE_NUMBERS) {
try { try {
const locale = document.querySelector('html').lang; return ratio.toLocaleString(locale(), { style: 'percent' });
return ratio.toLocaleString(locale, { style: 'percent' });
} catch (e) { } catch (e) {
// fall through // fall through
} }
@ -96,8 +98,7 @@ function percent(ratio) {
function number(n) { function number(n) {
if (LOCALIZE_NUMBERS) { if (LOCALIZE_NUMBERS) {
const locale = document.querySelector('html').lang; return n.toLocaleString(locale());
return n.toLocaleString(locale);
} }
return n.toString(); return n.toString();
} }
@ -267,6 +268,7 @@ function setTranslate(t) {
} }
module.exports = { module.exports = {
locale,
fadeOut, fadeOut,
delay, delay,
allowedCopy, allowedCopy,

View File

@ -138,3 +138,8 @@ noStreamsOptionCopy = Copy the link to open in another browser
noStreamsOptionFirefox = Try our favorite browser noStreamsOptionFirefox = Try our favorite browser
noStreamsOptionDownload = Continue with this browser noStreamsOptionDownload = Continue with this browser
downloadFirefoxPromo = { -send-short-brand } is brought to you by the all-new { -firefox }. downloadFirefoxPromo = { -send-short-brand } is brought to you by the all-new { -firefox }.
# the next line after the colon contains a file name
shareLinkDescription = Share the link to your file:
shareLinkButton = Share link
# $name is the name of the file
shareMessage = Download “{ $name }” with { -send-brand }: simple, safe file sharing

View File

@ -144,6 +144,11 @@ const conf = convict({
format: String, format: String,
default: 'https://identity.mozilla.com/apps/send', default: 'https://identity.mozilla.com/apps/send',
env: 'FXA_KEY_SCOPE' env: 'FXA_KEY_SCOPE'
},
survey_url: {
format: String,
default: '',
env: 'SURVEY_URL'
} }
}); });

View File

@ -47,8 +47,8 @@ module.exports = function(state) {
var LIMITS = ${JSON.stringify(clientConstants.LIMITS)}; var LIMITS = ${JSON.stringify(clientConstants.LIMITS)};
var DEFAULTS = ${JSON.stringify(clientConstants.DEFAULTS)}; var DEFAULTS = ${JSON.stringify(clientConstants.DEFAULTS)};
const LOCALE = '${state.locale}'; var PREFS = ${JSON.stringify(state.prefs)};
const downloadMetadata = ${ var downloadMetadata = ${
state.downloadMetadata ? raw(JSON.stringify(state.downloadMetadata)) : '{}' state.downloadMetadata ? raw(JSON.stringify(state.downloadMetadata)) : '{}'
}; };
${authConfig}; ${authConfig};

View File

@ -19,6 +19,10 @@ module.exports = async function(req) {
// continue without accounts // continue without accounts
} }
} }
const prefs = {};
if (config.survey_url) {
prefs.surveyUrl = config.survey_url;
}
return { return {
archive: { archive: {
numFiles: 0 numFiles: 0
@ -39,6 +43,7 @@ module.exports = async function(req) {
user: { avatar: assets.get('user.svg'), loggedIn: false }, user: { avatar: assets.get('user.svg'), loggedIn: false },
robots, robots,
authConfig, authConfig,
prefs,
layout layout
}; };
}; };