Fix #986 published claims out of order #1018

Merged
Jeremy1026 merged 7 commits from fix/986-published-claims-out-of-order into master 2018-02-27 05:55:31 +01:00
17 changed files with 251 additions and 248 deletions
Showing only changes of commit 96d77f9bc8 - Show all commits

View file

@ -17,6 +17,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
### Fixed ### Fixed
* Fixed sort by date of published content ([#986](https://github.com/lbryio/lbry-app/issues/986)) * Fixed sort by date of published content ([#986](https://github.com/lbryio/lbry-app/issues/986))
* Fix night mode start time, set to 9PM (#1050)
* *
### Deprecated ### Deprecated

View file

@ -0,0 +1,37 @@
// flow-typed signature: 89b1643389fe61f97995bdeb2f68d847
// flow-typed version: dcf8c1c580/mixpanel-browser_v2.11.x/flow_>=v0.25.x
declare module 'mixpanel-browser' {
declare type People = {
set(prop: Object|String, to?: any, callback?: Function): void;
set_once(prop: Object|String, to?: any, callback?: Function): void;
increment(prop: Object|string, by?: number, callback?: Function): void;
append(prop: Object|string, value?: any, callback?: Function): void;
union(prop: Object|string, value?: any, callback?: Function): void;
track_charge(amount: number, properties?: Object, callback?: Function): void;
clear_charges(callback?: Function): void;
delete_user(): void;
}
declare type MixpanelBrowser = {
init(token: string, config?: Object, name?: string): void;
push(item: [string, Object]): void;
disable(events?: string[]): void;
track(event_name: string, properties?: Object, callback?: Function): void;
track_links(query: Object|string, event_name: string, properties?: Object|Function): void;
track_forms(query: Object|string, event_name: string, properties?: Object|Function): void;
time_event(event_name: string): void;
register(properties: Object, days?: number): void;
register_once(properties: Object, default_value?: any, days?: number): void;
unregister(property: string): void;
identify(unique_id: string): void;
reset(): void;
get_distinct_id(): string;
alias(alias: string, original?: string): void;
set_config(config: Object): void;
get_config(): Object;
people: People;
}
declare module.exports: MixpanelBrowser;
}

View file

@ -29,7 +29,6 @@
"lbry" "lbry"
], ],
"dependencies": { "dependencies": {
"amplitude-js": "^4.0.0",
"bluebird": "^3.5.1", "bluebird": "^3.5.1",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"country-data": "^0.0.31", "country-data": "^0.0.31",
@ -45,6 +44,7 @@
"jshashes": "^1.0.7", "jshashes": "^1.0.7",
"keytar-prebuild": "^4.0.4", "keytar-prebuild": "^4.0.4",
"localforage": "^1.5.0", "localforage": "^1.5.0",
"mixpanel-browser": "^2.17.1",
"moment": "^2.20.1", "moment": "^2.20.1",
"npm": "^5.5.1", "npm": "^5.5.1",
"qrcode.react": "^0.7.2", "qrcode.react": "^0.7.2",

View file

@ -1,63 +0,0 @@
import { app, Menu, Tray as ElectronTray } from 'electron';
import path from 'path';
import createWindow from './createWindow';
export default class Tray {
window;
updateAttachedWindow;
tray;
constructor(window, updateAttachedWindow) {
this.window = window;
this.updateAttachedWindow = updateAttachedWindow;
}
create() {
let iconPath;
switch (process.platform) {
case 'darwin': {
iconPath = path.join(__static, '/img/tray/mac/trayTemplate.png');
break;
}
case 'win32': {
iconPath = path.join(__static, '/img/tray/windows/tray.ico');
break;
}
default: {
iconPath = path.join(__static, '/img/tray/default/tray.png');
}
}
this.tray = new ElectronTray(iconPath);
this.tray.on('double-click', () => {
if (!this.window || this.window.isDestroyed()) {
this.window = createWindow();
this.updateAttachedWindow(this.window);
} else {
this.window.show();
this.window.focus();
}
});
this.tray.setToolTip('LBRY App');
const template = [
{
label: `Open ${app.getName()}`,
click: () => {
if (!this.window || this.window.isDestroyed()) {
this.window = createWindow();
this.updateAttachedWindow(this.window);
} else {
this.window.show();
this.window.focus();
}
},
},
{ role: 'quit' },
];
const contextMenu = Menu.buildFromTemplate(template);
this.tray.setContextMenu(contextMenu);
}
}

41
src/main/createTray.js Normal file
View file

@ -0,0 +1,41 @@
import { app, Menu, Tray } from 'electron';
import path from 'path';
export default window => {
let iconPath;
switch (process.platform) {
case 'darwin': {
iconPath = path.join(__static, '/img/tray/mac/trayTemplate.png');
break;
}
case 'win32': {
iconPath = path.join(__static, '/img/tray/windows/tray.ico');
break;
}
default: {
iconPath = path.join(__static, '/img/tray/default/tray.png');
}
}
const tray = new Tray(iconPath);
tray.on('double-click', () => {
window.show();
});
tray.setToolTip('LBRY App');
const template = [
{
label: `Open ${app.getName()}`,
click: () => {
window.show();
},
},
{ role: 'quit' },
];
const contextMenu = Menu.buildFromTemplate(template);
tray.setContextMenu(contextMenu);
return tray;
};

View file

@ -2,7 +2,7 @@ import { app, BrowserWindow, dialog } from 'electron';
import setupBarMenu from './menu/setupBarMenu'; import setupBarMenu from './menu/setupBarMenu';
import setupContextMenu from './menu/setupContextMenu'; import setupContextMenu from './menu/setupContextMenu';
export default deepLinkingURIArg => { export default appState => {
let windowConfiguration = { let windowConfiguration = {
backgroundColor: '#155B4A', backgroundColor: '#155B4A',
minWidth: 800, minWidth: 800,
@ -35,11 +35,7 @@ export default deepLinkingURIArg => {
let deepLinkingURI; let deepLinkingURI;
// Protocol handler for win32 // Protocol handler for win32
if ( if (process.platform === 'win32' && String(process.argv[1]).startsWith('lbry')) {
!deepLinkingURIArg &&
process.platform === 'win32' &&
String(process.argv[1]).startsWith('lbry')
) {
// Keep only command line / deep linked arguments // Keep only command line / deep linked arguments
// Windows normalizes URIs when they're passed in from other apps. On Windows, this tries to // Windows normalizes URIs when they're passed in from other apps. On Windows, this tries to
// restore the original URI that was typed. // restore the original URI that was typed.
@ -48,15 +44,16 @@ export default deepLinkingURIArg => {
// - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as // - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as
// an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well. // an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well.
deepLinkingURI = process.argv[1].replace(/\/$/, '').replace('/#', '#'); deepLinkingURI = process.argv[1].replace(/\/$/, '').replace('/#', '#');
} else {
deepLinkingURI = deepLinkingURIArg;
} }
setupBarMenu(); setupBarMenu();
setupContextMenu(window); setupContextMenu(window);
window.on('closed', () => { window.on('close', event => {
window = null; if (!appState.isQuitting) {
event.preventDefault();
window.hide();
}
}); });
window.on('focus', () => { window.on('focus', () => {

View file

@ -8,7 +8,7 @@ import https from 'https';
import { shell, app, ipcMain, dialog } from 'electron'; import { shell, app, ipcMain, dialog } from 'electron';
import { autoUpdater } from 'electron-updater'; import { autoUpdater } from 'electron-updater';
import Daemon from './Daemon'; import Daemon from './Daemon';
import Tray from './Tray'; import createTray from './createTray';
import createWindow from './createWindow'; import createWindow from './createWindow';
autoUpdater.autoDownload = true; autoUpdater.autoDownload = true;
@ -32,11 +32,7 @@ let rendererWindow;
let tray; let tray;
let daemon; let daemon;
let isQuitting; const appState = {};
const updateRendererWindow = window => {
rendererWindow = window;
};
const installExtensions = async () => { const installExtensions = async () => {
// eslint-disable-next-line import/no-extraneous-dependencies,global-require // eslint-disable-next-line import/no-extraneous-dependencies,global-require
@ -64,7 +60,7 @@ app.on('ready', async () => {
daemon = new Daemon(); daemon = new Daemon();
daemon.on('exit', () => { daemon.on('exit', () => {
daemon = null; daemon = null;
if (!isQuitting) { if (!appState.isQuitting) {
dialog.showErrorBox( dialog.showErrorBox(
'Daemon has Exited', 'Daemon has Exited',
'The daemon may have encountered an unexpected error, or another daemon instance is already running.' 'The daemon may have encountered an unexpected error, or another daemon instance is already running.'
@ -77,15 +73,12 @@ app.on('ready', async () => {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
await installExtensions(); await installExtensions();
} }
rendererWindow = createWindow(); rendererWindow = createWindow(appState);
tray = new Tray(rendererWindow, updateRendererWindow); tray = createTray(rendererWindow);
tray.create();
}); });
app.on('activate', () => { app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the rendererWindow.show();
// dock icon is clicked and there are no other windows open.
if (!rendererWindow) rendererWindow = createWindow();
}); });
app.on('will-quit', event => { app.on('will-quit', event => {
@ -117,7 +110,7 @@ app.on('will-quit', event => {
return; return;
} }
isQuitting = true; appState.isQuitting = true;
if (daemon) daemon.quit(); if (daemon) daemon.quit();
}); });
@ -126,18 +119,13 @@ app.on('will-finish-launching', () => {
// Protocol handler for macOS // Protocol handler for macOS
app.on('open-url', (event, URL) => { app.on('open-url', (event, URL) => {
event.preventDefault(); event.preventDefault();
if (rendererWindow && !rendererWindow.isDestroyed()) { rendererWindow.webContents.send('open-uri-requested', URL);
rendererWindow.webContents.send('open-uri-requested', URL); rendererWindow.show();
rendererWindow.show();
rendererWindow.focus();
} else {
rendererWindow = createWindow(URL);
}
}); });
}); });
app.on('window-all-closed', () => { app.on('before-quit', () => {
// Subscribe to event so the app doesn't quit when closing the window. appState.isQuitting = true;
}); });
ipcMain.on('upgrade', (event, installerPath) => { ipcMain.on('upgrade', (event, installerPath) => {
@ -224,7 +212,7 @@ ipcMain.on('set-auth-token', (event, token) => {
process.on('uncaughtException', error => { process.on('uncaughtException', error => {
dialog.showErrorBox('Error Encountered', `Caught error: ${error}`); dialog.showErrorBox('Error Encountered', `Caught error: ${error}`);
isQuitting = true; appState.isQuitting = true;
if (daemon) daemon.quit(); if (daemon) daemon.quit();
app.exit(1); app.exit(1);
}); });
@ -240,14 +228,8 @@ const isSecondInstance = app.makeSingleInstance(argv => {
URI = argv[1].replace(/\/$/, '').replace('/#', '#'); URI = argv[1].replace(/\/$/, '').replace('/#', '#');
} }
if (rendererWindow && !rendererWindow.isDestroyed()) { rendererWindow.webContents.send('open-uri-requested', URI);
rendererWindow.webContents.send('open-uri-requested', URI); rendererWindow.show();
rendererWindow.show();
rendererWindow.focus();
} else {
rendererWindow = createWindow(URI);
}
}); });
if (isSecondInstance) { if (isSecondInstance) {

42
src/renderer/analytics.js Normal file
View file

@ -0,0 +1,42 @@
// @flow
import mixpanel from 'mixpanel-browser';
mixpanel.init('691723e855cabb9d27a7a79002216967');
type Analytics = {
track: (string, ?Object) => void,
setUser: (Object) => void,
toggle: (boolean, ?boolean) => void
}
let analyticsEnabled: boolean = false;
const analytics: Analytics = {
track: (name: string, payload: ?Object): void => {
if(analyticsEnabled) {
if(payload) {
mixpanel.track(name, payload);
} else {
mixpanel.track(name);
}
}
},
setUser: (user: Object): void => {
if(user.id) {
mixpanel.identify(user.id);
}
if(user.primary_email) {
mixpanel.people.set({
"$email": user.primary_email
});
}
},
toggle: (enabled: boolean, logDisabled: ?boolean): void => {
if(!enabled && logDisabled) {
mixpanel.track('DISABLED');
}
analyticsEnabled = enabled;
}
}
export default analytics;

View file

@ -1,5 +1,4 @@
const { remote } = require('electron'); import { remote } from 'electron';
import React from 'react'; import React from 'react';
import { Thumbnail } from 'component/common'; import { Thumbnail } from 'component/common';
import player from 'render-media'; import player from 'render-media';
@ -21,33 +20,21 @@ class VideoPlayer extends React.PureComponent {
this.togglePlayListener = this.togglePlay.bind(this); this.togglePlayListener = this.togglePlay.bind(this);
} }
componentWillReceiveProps(next) {
const el = this.refs.media.children[0];
if (!this.props.paused && next.paused && !el.paused) el.pause();
}
componentDidMount() { componentDidMount() {
const container = this.refs.media; const container = this.media;
const { const { contentType, changeVolume, volume, position, claim } = this.props;
contentType,
downloadPath,
mediaType,
changeVolume,
volume,
position,
claim,
uri,
} = this.props;
const loadedMetadata = e => { const loadedMetadata = () => {
this.setState({ hasMetadata: true, startedPlaying: true }); this.setState({ hasMetadata: true, startedPlaying: true });
this.refs.media.children[0].play(); this.media.children[0].play();
}; };
const renderMediaCallback = err => {
if (err) this.setState({ unplayable: true }); const renderMediaCallback = error => {
if (error) this.setState({ unplayable: true });
}; };
// Handle fullscreen change for the Windows platform // Handle fullscreen change for the Windows platform
const win32FullScreenChange = e => { const win32FullScreenChange = () => {
const win = remote.BrowserWindow.getFocusedWindow(); const win = remote.BrowserWindow.getFocusedWindow();
if (process.platform === 'win32') { if (process.platform === 'win32') {
win.setMenu(document.webkitIsFullScreen ? null : remote.Menu.getApplicationMenu()); win.setMenu(document.webkitIsFullScreen ? null : remote.Menu.getApplicationMenu());
@ -61,13 +48,13 @@ class VideoPlayer extends React.PureComponent {
player.append( player.append(
this.file(), this.file(),
container, container,
{ autoplay: false, controls: true }, { autoplay: true, controls: true },
renderMediaCallback.bind(this) renderMediaCallback.bind(this)
); );
} }
document.addEventListener('keydown', this.togglePlayListener); document.addEventListener('keydown', this.togglePlayListener);
const mediaElement = this.refs.media.children[0]; const mediaElement = this.media.children[0];
if (mediaElement) { if (mediaElement) {
mediaElement.currentTime = position || 0; mediaElement.currentTime = position || 0;
mediaElement.addEventListener('play', () => this.props.doPlay()); mediaElement.addEventListener('play', () => this.props.doPlay());
@ -87,29 +74,38 @@ class VideoPlayer extends React.PureComponent {
} }
} }
componentWillReceiveProps(next) {
const el = this.media.children[0];
if (!this.props.paused && next.paused && !el.paused) el.pause();
}
componentDidUpdate() {
const { contentType, downloadCompleted } = this.props;
const { startedPlaying } = this.state;
if (this.playableType() && !startedPlaying && downloadCompleted) {
const container = this.media.children[0];
if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
this.renderAudio(this.media, true);
} else {
player.render(this.file(), container, {
autoplay: true,
controls: true,
});
}
}
}
componentWillUnmount() { componentWillUnmount() {
document.removeEventListener('keydown', this.togglePlayListener); document.removeEventListener('keydown', this.togglePlayListener);
const mediaElement = this.refs.media.children[0]; const mediaElement = this.media.children[0];
if (mediaElement) { if (mediaElement) {
mediaElement.removeEventListener('click', this.togglePlayListener); mediaElement.removeEventListener('click', this.togglePlayListener);
} }
this.props.doPause(); this.props.doPause();
} }
renderAudio(container, autoplay) {
if (container.firstChild) {
container.firstChild.remove();
}
// clear the container
const { downloadPath } = this.props;
const audio = document.createElement('audio');
audio.autoplay = autoplay;
audio.controls = true;
audio.src = downloadPath;
container.appendChild(audio);
}
togglePlay(event) { togglePlay(event) {
// ignore all events except click and spacebar keydown, or input events in a form control // ignore all events except click and spacebar keydown, or input events in a form control
if ( if (
@ -119,7 +115,7 @@ class VideoPlayer extends React.PureComponent {
return; return;
} }
event.preventDefault(); event.preventDefault();
const mediaElement = this.refs.media.children[0]; const mediaElement = this.media.children[0];
if (mediaElement) { if (mediaElement) {
if (!mediaElement.paused) { if (!mediaElement.paused) {
mediaElement.pause(); mediaElement.pause();
@ -129,24 +125,6 @@ class VideoPlayer extends React.PureComponent {
} }
} }
componentDidUpdate() {
const { contentType, downloadCompleted } = this.props;
const { startedPlaying } = this.state;
if (this.playableType() && !startedPlaying && downloadCompleted) {
const container = this.refs.media.children[0];
if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
this.renderAudio(this.refs.media, true);
} else {
player.render(this.file(), container, {
autoplay: true,
controls: true,
});
}
}
}
file() { file() {
const { downloadPath, filename } = this.props; const { downloadPath, filename } = this.props;
@ -162,14 +140,26 @@ class VideoPlayer extends React.PureComponent {
return ['audio', 'video'].indexOf(mediaType) !== -1; return ['audio', 'video'].indexOf(mediaType) !== -1;
} }
renderAudio(container, autoplay) {
if (container.firstChild) {
container.firstChild.remove();
}
// clear the container
const { downloadPath } = this.props;
const audio = document.createElement('audio');
audio.autoplay = autoplay;
audio.controls = true;
audio.src = downloadPath;
container.appendChild(audio);
}
render() { render() {
const { mediaType, poster } = this.props; const { mediaType, poster } = this.props;
const { hasMetadata, unplayable } = this.state; const { hasMetadata, unplayable } = this.state;
const noMetadataMessage = 'Waiting for metadata.'; const noMetadataMessage = 'Waiting for metadata.';
const unplayableMessage = "Sorry, looks like we can't play this file."; const unplayableMessage = "Sorry, looks like we can't play this file.";
const needsMetadata = this.playableType();
return ( return (
<div> <div>
{['audio', 'application'].indexOf(mediaType) !== -1 && {['audio', 'application'].indexOf(mediaType) !== -1 &&
@ -179,7 +169,12 @@ class VideoPlayer extends React.PureComponent {
!hasMetadata && !hasMetadata &&
!unplayable && <LoadingScreen status={noMetadataMessage} />} !unplayable && <LoadingScreen status={noMetadataMessage} />}
{unplayable && <LoadingScreen status={unplayableMessage} spinner={false} />} {unplayable && <LoadingScreen status={unplayableMessage} spinner={false} />}
<div ref="media" className="media" /> <div
ref={container => {
this.media = container;
}}
className="media"
/>
</div> </div>
); );
} }

View file

@ -1,5 +1,4 @@
/* eslint-disable react/jsx-filename-extension */ /* eslint-disable react/jsx-filename-extension */
import amplitude from 'amplitude-js';
import App from 'component/app'; import App from 'component/app';
import SnackBar from 'component/snackBar'; import SnackBar from 'component/snackBar';
import SplashScreen from 'component/splash'; import SplashScreen from 'component/splash';
@ -17,6 +16,7 @@ import { doUserEmailVerify } from 'redux/actions/user';
import 'scss/all.scss'; import 'scss/all.scss';
import store from 'store'; import store from 'store';
import app from './app'; import app from './app';
import analytics from './analytics';
const { autoUpdater } = remote.require('electron-updater'); const { autoUpdater } = remote.require('electron-updater');
@ -62,15 +62,6 @@ ipcRenderer.on('window-is-focused', () => {
dock.setBadge(''); dock.setBadge('');
}); });
((history, ...args) => {
const { replaceState } = history;
const newHistory = history;
newHistory.replaceState = (_, __, path) => {
amplitude.getInstance().logEvent('NAVIGATION', { destination: path ? path.slice(1) : path });
return replaceState.apply(history, args);
};
})(window.history);
document.addEventListener('click', event => { document.addEventListener('click', event => {
let { target } = event; let { target } = event;
while (target && target !== document) { while (target && target !== document) {
@ -79,12 +70,12 @@ document.addEventListener('click', event => {
const hrefParts = window.location.href.split('#'); const hrefParts = window.location.href.split('#');
const element = target.title || (target.textContent && target.textContent.trim()); const element = target.title || (target.textContent && target.textContent.trim());
if (element) { if (element) {
amplitude.getInstance().logEvent('CLICK', { analytics.track('CLICK', {
target: element, target: element,
location: hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : '/', location: hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : '/',
}); });
} else { } else {
amplitude.getInstance().logEvent('UNMARKED_CLICK', { analytics.track('UNMARKED_CLICK', {
location: hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : '/', location: hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : '/',
source: target.outerHTML, source: target.outerHTML,
}); });
@ -120,28 +111,18 @@ const init = () => {
app.store.dispatch(doDownloadLanguages()); app.store.dispatch(doDownloadLanguages());
function onDaemonReady() { function onDaemonReady() {
lbry.status().then(info => { window.sessionStorage.setItem('loaded', 'y'); // once we've made it here once per session, we don't need to show splash again
amplitude.getInstance().init( app.store.dispatch(doDaemonReady());
// Amplitude API Key
'0b130efdcbdbf86ec2f7f9eff354033e',
info.lbry_id,
null,
() => {
window.sessionStorage.setItem('loaded', 'y'); // once we've made it here once per session, we don't need to show splash again
app.store.dispatch(doDaemonReady());
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>
<div> <div>
<App /> <App />
<SnackBar /> <SnackBar />
</div> </div>
</Provider>, </Provider>,
document.getElementById('app') document.getElementById('app')
); );
}
);
});
} }
if (window.sessionStorage.getItem('loaded') === 'y') { if (window.sessionStorage.getItem('loaded') === 'y') {

View file

@ -15,7 +15,7 @@ jsonrpc.call = (
return response.json().then(json => { return response.json().then(json => {
let error; let error;
if (json.error) { if (json.error) {
error = new Error(json.error); error = new Error(json.error.message);
} else { } else {
error = new Error('Protocol error with unknown response signature'); error = new Error('Protocol error with unknown response signature');
} }

View file

@ -14,6 +14,8 @@ class SettingsPage extends React.PureComponent {
this.state = { this.state = {
clearingCache: false, clearingCache: false,
}; };
this.onAutomaticDarkModeChange = this.onAutomaticDarkModeChange.bind(this);
} }
clearCache() { clearCache() {
@ -62,11 +64,16 @@ class SettingsPage extends React.PureComponent {
onThemeChange(event) { onThemeChange(event) {
const { value } = event.target; const { value } = event.target;
if (value === 'dark') {
this.onAutomaticDarkModeChange(false);
}
this.props.setClientSetting(settings.THEME, value); this.props.setClientSetting(settings.THEME, value);
} }
onAutomaticDarkModeChange(event) { onAutomaticDarkModeChange(value) {
this.props.setClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED, event.target.checked); this.props.setClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED, value);
} }
onInstantPurchaseEnabledChange(enabled) { onInstantPurchaseEnabledChange(enabled) {
@ -143,6 +150,7 @@ class SettingsPage extends React.PureComponent {
</main> </main>
); );
} }
return ( return (
<main className="main--single-column"> <main className="main--single-column">
<SubHeader /> <SubHeader />
@ -325,8 +333,9 @@ class SettingsPage extends React.PureComponent {
<FormRow <FormRow
type="checkbox" type="checkbox"
onChange={this.onAutomaticDarkModeChange.bind(this)} disabled={theme === 'dark'}
defaultChecked={automaticDarkModeEnabled} onChange={(e) => this.onAutomaticDarkModeChange(e.target.checked)}
checked={automaticDarkModeEnabled}
label={__('Automatic dark mode (9pm to 8am)')} label={__('Automatic dark mode (9pm to 8am)')}
/> />
</div> </div>

View file

@ -1,6 +1,7 @@
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import { selectHistoryIndex, selectHistoryStack } from 'redux/selectors/navigation'; import { selectHistoryIndex, selectHistoryStack } from 'redux/selectors/navigation';
import { toQueryString } from 'util/query_params'; import { toQueryString } from 'util/query_params';
import analytics from 'analytics';
export function doNavigate(path, params = {}, options = {}) { export function doNavigate(path, params = {}, options = {}) {
return dispatch => { return dispatch => {
@ -13,6 +14,8 @@ export function doNavigate(path, params = {}, options = {}) {
url += `?${toQueryString(params)}`; url += `?${toQueryString(params)}`;
} }
analytics.track('NAVIGATION', { destination: url });
const { scrollY } = options; const { scrollY } = options;
dispatch({ dispatch({

View file

@ -2,15 +2,16 @@ import * as ACTIONS from 'constants/action_types';
import * as SETTINGS from 'constants/settings'; import * as SETTINGS from 'constants/settings';
import Fs from 'fs'; import Fs from 'fs';
import Http from 'http'; import Http from 'http';
import Lbry from 'lbry'; import Lbry from 'lbry';
import moment from 'moment'; import moment from 'moment';
import analytics from 'analytics';
const UPDATE_IS_NIGHT_INTERVAL = 10 * 60 * 1000; const UPDATE_IS_NIGHT_INTERVAL = 10 * 60 * 1000;
export function doFetchDaemonSettings() { export function doFetchDaemonSettings() {
return dispatch => { return dispatch => {
Lbry.settings_get().then(settings => { Lbry.settings_get().then(settings => {
analytics.toggle(settings.share_usage_data);
dispatch({ dispatch({
type: ACTIONS.DAEMON_SETTINGS_RECEIVED, type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
data: { data: {
@ -27,6 +28,7 @@ export function doSetDaemonSetting(key, value) {
newSettings[key] = value; newSettings[key] = value;
Lbry.settings_set(newSettings).then(newSettings); Lbry.settings_set(newSettings).then(newSettings);
Lbry.settings_get().then(settings => { Lbry.settings_get().then(settings => {
analytics.toggle(settings.share_usage_data, true);
dispatch({ dispatch({
type: ACTIONS.DAEMON_SETTINGS_RECEIVED, type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
data: { data: {
@ -69,7 +71,7 @@ export function doUpdateIsNight() {
return { return {
type: ACTIONS.UPDATE_IS_NIGHT, type: ACTIONS.UPDATE_IS_NIGHT,
data: { isNight: (() => { data: { isNight: (() => {
const startNightMoment = moment('19:00', 'HH:mm'); const startNightMoment = moment('21:00', 'HH:mm');
const endNightMoment = moment('8:00', 'HH:mm'); const endNightMoment = moment('8:00', 'HH:mm');
return !(momentNow.isAfter(endNightMoment) && momentNow.isBefore(startNightMoment)); return !(momentNow.isAfter(endNightMoment) && momentNow.isBefore(startNightMoment));
})() })()

View file

@ -9,6 +9,7 @@ import {
selectUserCountryCode, selectUserCountryCode,
} from 'redux/selectors/user'; } from 'redux/selectors/user';
import rewards from 'rewards'; import rewards from 'rewards';
import analytics from 'analytics';
export function doFetchInviteStatus() { export function doFetchInviteStatus() {
return dispatch => { return dispatch => {
@ -42,6 +43,7 @@ export function doAuthenticate() {
}); });
Lbryio.authenticate() Lbryio.authenticate()
.then(user => { .then(user => {
analytics.setUser(user);
dispatch({ dispatch({
type: ACTIONS.AUTHENTICATION_SUCCESS, type: ACTIONS.AUTHENTICATION_SUCCESS,
data: { user }, data: { user },
@ -66,6 +68,7 @@ export function doUserFetch() {
}); });
Lbryio.getCurrentUser() Lbryio.getCurrentUser()
.then(user => { .then(user => {
analytics.setUser(user);
dispatch(doRewardList()); dispatch(doRewardList());
dispatch({ dispatch({

View file

@ -2,10 +2,10 @@ import { remote } from 'electron';
const application = remote.app; const application = remote.app;
const { dock } = application; const { dock } = application;
const win = remote.BrowserWindow.getFocusedWindow(); const browserWindow = remote.getCurrentWindow();
const setBadge = text => { const setBadge = text => {
if (!dock) return; if (!dock) return;
if (win.isFocused()) return; if (browserWindow.isFocused()) return;
dock.setBadge(text); dock.setBadge(text);
}; };

View file

@ -79,13 +79,6 @@
lodash "^4.2.0" lodash "^4.2.0"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@segment/top-domain@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@segment/top-domain/-/top-domain-3.0.0.tgz#02e5a5a4fd42a9f6cf886b05e82f104012a3c3a7"
dependencies:
component-cookie "^1.1.2"
component-url "^0.2.1"
"@types/node@^7.0.18": "@types/node@^7.0.18":
version "7.0.52" version "7.0.52"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.52.tgz#8990d3350375542b0c21a83cd0331e6a8fc86716" resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.52.tgz#8990d3350375542b0c21a83cd0331e6a8fc86716"
@ -217,16 +210,6 @@ amdefine@>=0.0.4:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
amplitude-js@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/amplitude-js/-/amplitude-js-4.0.0.tgz#70bbc0ec893b01d00453d3765f78bc0f32a395cc"
dependencies:
"@segment/top-domain" "^3.0.0"
blueimp-md5 "^2.10.0"
json3 "^3.3.2"
lodash "^4.17.4"
ua-parser-js "github:amplitude/ua-parser-js#ed538f1"
ansi-align@^2.0.0: ansi-align@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
@ -1400,10 +1383,6 @@ bluebird@^3.4.7, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@~3.5.0:
version "3.5.1" version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
blueimp-md5@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.10.0.tgz#02f0843921f90dca14f5b8920a38593201d6964d"
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.8" version "4.11.8"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
@ -2081,16 +2060,6 @@ compare-version@^0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080" resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080"
component-cookie@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/component-cookie/-/component-cookie-1.1.3.tgz#053e14a3bd7748154f55724fd39a60c01994ebed"
dependencies:
debug "*"
component-url@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/component-url/-/component-url-0.2.1.tgz#4e4f4799c43ead9fd3ce91b5a305d220208fee47"
compressible@~2.0.11: compressible@~2.0.11:
version "2.0.12" version "2.0.12"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66"
@ -2451,18 +2420,18 @@ date-now@^0.1.4:
version "0.1.4" version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
debug@*, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
debug@2, debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8: debug@2, debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@^3.0.0, debug@^3.0.1, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
debuglog@^1.0.1: debuglog@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
@ -5799,6 +5768,10 @@ mixin-object@^2.0.1:
for-in "^0.1.3" for-in "^0.1.3"
is-extendable "^0.1.1" is-extendable "^0.1.1"
mixpanel-browser@^2.17.1:
version "2.17.1"
resolved "https://registry.yarnpkg.com/mixpanel-browser/-/mixpanel-browser-2.17.1.tgz#1b90a0478ec912f35f761c52e08d4228fc37867f"
mkdirp@0.5, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: mkdirp@0.5, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.1" version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@ -8795,7 +8768,7 @@ typo-js@*:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.0.3.tgz#54d8ebc7949f1a7810908b6002c6841526c99d5a" resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.0.3.tgz#54d8ebc7949f1a7810908b6002c6841526c99d5a"
ua-parser-js@^0.7.9, "ua-parser-js@github:amplitude/ua-parser-js#ed538f1": ua-parser-js@^0.7.9:
version "0.7.10" version "0.7.10"
resolved "https://codeload.github.com/amplitude/ua-parser-js/tar.gz/ed538f16f5c6ecd8357da989b617d4f156dcf35d" resolved "https://codeload.github.com/amplitude/ua-parser-js/tar.gz/ed538f16f5c6ecd8357da989b617d4f156dcf35d"