added survey dialog. closes #1307
This commit is contained in:
parent
ce4157ac08
commit
20b9279eec
|
@ -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 ||
|
||||||
|
|
|
@ -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 === '/') {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,6 @@ module.exports = function(state, emit) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
state.modal = null;
|
emit('closeModal');
|
||||||
emit('render');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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) {
|
||||||
|
|
14
app/utils.js
14
app/utils.js
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue