Pretty print and lint non-component sources

This commit is contained in:
Igor Gassmann 2017-12-21 14:32:51 -03:00
parent 4a48d0a521
commit 7cd25e777a
103 changed files with 3577 additions and 4210 deletions

View file

@ -22,8 +22,8 @@
"build": "yarn compile && electron-builder build", "build": "yarn compile && electron-builder build",
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"precommit": "lint-staged", "precommit": "lint-staged",
"lint": "eslint src/**/*.{js,jsx} --fix", "lint": "eslint 'src/**/*.{js,jsx}' --fix",
"pretty": "prettier src/**/*.{js,jsx,scss,json} --write" "pretty-print": "prettier 'src/**/*.{js,jsx,scss,json}' --write"
}, },
"keywords": [ "keywords": [
"lbry" "lbry"

View file

@ -1,68 +1,39 @@
/* eslint-disable no-console */
// Module imports // Module imports
const { import Path from 'path';
app, import Url from 'url';
BrowserWindow, import Jayson from 'jayson';
ipcMain, import Semver from 'semver';
Menu, import Https from 'https';
Tray, import Keytar from 'keytar';
globalShortcut, import ChildProcess from 'child_process';
} = require("electron"); import Assert from 'assert';
const path = require("path"); import { app, BrowserWindow, globalShortcut, ipcMain, Menu, Tray } from 'electron';
const url = require("url"); import mainMenu from './menu/mainMenu';
const jayson = require("jayson");
const semver = require("semver");
const https = require("https");
const keytar = require("keytar");
// tree-kill has better cross-platform handling of
// killing a process. child-process.kill was unreliable
const kill = require("tree-kill");
const child_process = require("child_process");
const assert = require("assert");
const localVersion = app.getVersion(); const localVersion = app.getVersion();
const setMenu = require("./menu/main-menu.js"); export { contextMenu as Default } from './menu/contextMenu';
export const contextMenu = require("./menu/context-menu");
// Debug configs // Debug configs
const isDevelopment = process.env.NODE_ENV === "development"; const isDevelopment = process.env.NODE_ENV === 'development';
if (isDevelopment) {
try {
const {
default: installExtension,
REACT_DEVELOPER_TOOLS,
REDUX_DEVTOOLS,
} = require("electron-devtools-installer");
app.on("ready", () => {
[REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS].forEach(extension => {
installExtension(extension)
.then(name => console.log(`Added Extension: ${name}`))
.catch(err => console.log("An error occurred: ", err));
});
});
} catch (err) {
console.error(err);
}
}
// Misc constants // Misc constants
const LATEST_RELEASE_API_URL = const LATEST_RELEASE_API_URL = 'https://api.github.com/repos/lbryio/lbry-app/releases/latest';
"https://api.github.com/repos/lbryio/lbry-app/releases/latest"; const DAEMON_PATH = process.env.LBRY_DAEMON || Path.join(__static, 'daemon/lbrynet-daemon');
const DAEMON_PATH =
process.env.LBRY_DAEMON || path.join(__static, "daemon/lbrynet-daemon");
const rendererUrl = isDevelopment const rendererUrl = isDevelopment
? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}` ? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
: `file://${__dirname}/index.html`; : `file://${__dirname}/index.html`;
const client = jayson.client.http({ const client = Jayson.client.http({
host: "localhost", host: 'localhost',
port: 5279, port: 5279,
path: "/", path: '/',
timeout: 1000, timeout: 1000,
}); });
// Keep a global reference of the window object, if you don't, the window will // Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected. // be closed automatically when the JavaScript object is garbage collected.
let win; let rendererWindow;
// Also keep the daemon subprocess alive // Also keep the daemon subprocess alive
let daemonSubprocess; let daemonSubprocess;
@ -95,8 +66,8 @@ function processRequestedUri(uri) {
// lbry://channel/#claimid. We remove the slash here as well. // lbry://channel/#claimid. We remove the slash here as well.
// On Linux and Mac, we just return the URI as given. // On Linux and Mac, we just return the URI as given.
if (process.platform === "win32") { if (process.platform === 'win32') {
return uri.replace(/\/$/, "").replace("/#", "#"); return uri.replace(/\/$/, '').replace('/#', '#');
} }
return uri; return uri;
} }
@ -109,223 +80,22 @@ function processRequestedUri(uri) {
function openItem(fullPath) { function openItem(fullPath) {
const subprocOptions = { const subprocOptions = {
detached: true, detached: true,
stdio: "ignore", stdio: 'ignore',
}; };
let child; let child;
if (process.platform === "darwin") { if (process.platform === 'darwin') {
child = child_process.spawn("open", [fullPath], subprocOptions); child = ChildProcess.spawn('open', [fullPath], subprocOptions);
} else if (process.platform === "linux") { } else if (process.platform === 'linux') {
child = child_process.spawn("xdg-open", [fullPath], subprocOptions); child = ChildProcess.spawn('xdg-open', [fullPath], subprocOptions);
} else if (process.platform === "win32") { } else if (process.platform === 'win32') {
child = child_process.spawn( child = ChildProcess.spawn(fullPath, Object.assign({}, subprocOptions, { shell: true }));
fullPath,
Object.assign({}, subprocOptions, { shell: true })
);
} }
// Causes child process reference to be garbage collected, allowing main process to exit // Causes child process reference to be garbage collected, allowing main process to exit
child.unref(); child.unref();
} }
function getPidsForProcessName(name) {
if (process.platform === "win32") {
const tasklistOut = child_process.execSync(
`tasklist /fi "Imagename eq ${name}.exe" /nh`,
{ encoding: "utf8" }
);
if (tasklistOut.startsWith("INFO")) {
return [];
}
return tasklistOut.match(/[^\r\n]+/g).map(line => line.split(/\s+/)[1]); // Second column of every non-empty line
}
const pgrepOut = child_process.spawnSync("pgrep", ["-x", name], {
encoding: "utf8",
}).stdout;
return pgrepOut.match(/\d+/g);
}
function createWindow() {
// Disable renderer process's webSecurity on development to enable CORS.
win = isDevelopment
? new BrowserWindow({
backgroundColor: "#155B4A",
minWidth: 800,
minHeight: 600,
webPreferences: { webSecurity: false },
})
: new BrowserWindow({
backgroundColor: "#155B4A",
minWidth: 800,
minHeight: 600,
});
win.webContents.session.setUserAgent(`LBRY/${localVersion}`);
win.maximize();
if (isDevelopment) {
win.webContents.openDevTools();
}
win.loadURL(rendererUrl);
if (openUri) {
// We stored and received a URI that an external app requested before we had a window object
win.webContents.on("did-finish-load", () => {
win.webContents.send("open-uri-requested", openUri);
});
}
win.removeAllListeners();
win.on("close", event => {
if (minimize) {
event.preventDefault();
win.hide();
}
});
win.on("closed", () => {
win = null;
});
win.on("hide", () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
});
win.on("show", () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
});
win.on("blur", () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
// Unregisters Alt+F4 shortcut
globalShortcut.unregister("Alt+F4");
});
win.on("focus", () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
// Registers shortcut for closing(quitting) the app
globalShortcut.register("Alt+F4", () => safeQuit());
win.webContents.send("window-is-focused", null);
});
// Menu bar
win.setAutoHideMenuBar(true);
win.setMenuBarVisibility(isDevelopment);
setMenu();
}
function createTray() {
// Minimize to tray logic follows:
// Set the tray icon
let iconPath;
if (process.platform === "darwin") {
// Using @2x for mac retina screens so the icon isn't blurry
// file name needs to include "Template" at the end for dark menu bar
iconPath = path.join(__static, "/img/fav/macTemplate@2x.png");
} else {
iconPath = path.join(__static, "/img/fav/32x32.png");
}
tray = new Tray(iconPath);
tray.setToolTip("LBRY App");
tray.setTitle("LBRY");
tray.on("double-click", () => {
win.show();
});
}
// This needs to be done as for linux the context menu doesn't update automatically(docs)
function updateTray() {
const contextMenu = Menu.buildFromTemplate(getMenuTemplate());
if (tray) {
tray.setContextMenu(contextMenu);
} else {
console.log("How did update tray get called without a tray?");
}
}
function getMenuTemplate() {
return [
getToggleItem(),
{
label: "Quit",
click: () => safeQuit(),
},
];
function getToggleItem() {
if (win.isVisible() && win.isFocused()) {
return {
label: "Hide LBRY App",
click: () => win.hide(),
};
}
return {
label: "Show LBRY App",
click: () => win.show(),
};
}
}
function handleOpenUriRequested(uri) {
if (!win) {
// Window not created yet, so store up requested URI for when it is
openUri = processRequestedUri(uri);
} else {
if (win.isMinimized()) {
win.restore();
} else if (!win.isVisible()) {
win.show();
}
win.focus();
win.webContents.send("open-uri-requested", processRequestedUri(uri));
}
}
function handleDaemonSubprocessExited() {
console.log("The daemon has exited.");
daemonSubprocess = null;
if (!daemonStopRequested) {
// We didn't request to stop the daemon, so display a
// warning and schedule a quit.
//
// TODO: maybe it would be better to restart the daemon?
if (win) {
console.log("Did not request daemon stop, so quitting in 5 seconds.");
win.loadURL(`file://${__static}/warning.html`);
setTimeout(quitNow, 5000);
} else {
console.log("Did not request daemon stop, so quitting.");
quitNow();
}
}
}
function launchDaemon() {
assert(!daemonSubprocess, "Tried to launch daemon twice");
console.log("Launching daemon:", DAEMON_PATH);
daemonSubprocess = child_process.spawn(DAEMON_PATH);
// Need to handle the data event instead of attaching to
// process.stdout because the latter doesn't work. I believe on
// windows it buffers stdout and we don't get any meaningful output
daemonSubprocess.stdout.on("data", buf => {
console.log(String(buf).trim());
});
daemonSubprocess.stderr.on("data", buf => {
console.log(String(buf).trim());
});
daemonSubprocess.on("exit", handleDaemonSubprocessExited);
}
/* /*
* Quits by first killing the daemon, the calling quitting for real. * Quits by first killing the daemon, the calling quitting for real.
*/ */
@ -334,6 +104,155 @@ export function safeQuit() {
app.quit(); app.quit();
} }
function getMenuTemplate() {
function getToggleItem() {
if (rendererWindow.isVisible() && rendererWindow.isFocused()) {
return {
label: 'Hide LBRY App',
click: () => rendererWindow.hide(),
};
}
return {
label: 'Show LBRY App',
click: () => rendererWindow.show(),
};
}
return [
getToggleItem(),
{
label: 'Quit',
click: () => safeQuit(),
},
];
}
// This needs to be done as for linux the context menu doesn't update automatically(docs)
function updateTray() {
const trayContextMenu = Menu.buildFromTemplate(getMenuTemplate());
if (tray) {
tray.setContextMenu(trayContextMenu);
} else {
console.log('How did update tray get called without a tray?');
}
}
function createWindow() {
// Disable renderer process's webSecurity on development to enable CORS.
let windowConfiguration = {
backgroundColor: '#155B4A',
minWidth: 800,
minHeight: 600,
autoHideMenuBar: true,
};
windowConfiguration = isDevelopment
? {
...windowConfiguration,
webPreferences: {
webSecurity: false,
},
}
: windowConfiguration;
let window = new BrowserWindow(windowConfiguration);
window.webContents.session.setUserAgent(`LBRY/${localVersion}`);
window.maximize();
if (isDevelopment) {
window.webContents.openDevTools();
}
window.loadURL(rendererUrl);
if (openUri) {
// We stored and received a URI that an external app requested before we had a window object
window.webContents.on('did-finish-load', () => {
window.webContents.send('open-uri-requested', openUri);
});
}
window.removeAllListeners();
window.on('close', event => {
if (minimize) {
event.preventDefault();
window.hide();
}
});
window.on('closed', () => {
window = null;
});
window.on('hide', () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
});
window.on('show', () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
});
window.on('blur', () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
// Unregisters Alt+F4 shortcut
globalShortcut.unregister('Alt+F4');
});
window.on('focus', () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
// Registers shortcut for closing(quitting) the app
globalShortcut.register('Alt+F4', () => safeQuit());
window.webContents.send('window-is-focused', null);
});
mainMenu();
return window;
}
function createTray() {
// Minimize to tray logic follows:
// Set the tray icon
let iconPath;
if (process.platform === 'darwin') {
// Using @2x for mac retina screens so the icon isn't blurry
// file name needs to include "Template" at the end for dark menu bar
iconPath = Path.join(__static, '/img/fav/macTemplate@2x.png');
} else {
iconPath = Path.join(__static, '/img/fav/32x32.png');
}
tray = new Tray(iconPath);
tray.setToolTip('LBRY App');
tray.setTitle('LBRY');
tray.on('double-click', () => {
rendererWindow.show();
});
}
function handleOpenUriRequested(uri) {
if (!rendererWindow) {
// Window not created yet, so store up requested URI for when it is
openUri = processRequestedUri(uri);
} else {
if (rendererWindow.isMinimized()) {
rendererWindow.restore();
} else if (!rendererWindow.isVisible()) {
rendererWindow.show();
}
rendererWindow.focus();
rendererWindow.webContents.send('open-uri-requested', processRequestedUri(uri));
}
}
/* /*
* Quits without any preparation. When a quit is requested (either through the * Quits without any preparation. When a quit is requested (either through the
* interface or through app.quit()), we abort the quit, try to shut down the daemon, * interface or through app.quit()), we abort the quit, try to shut down the daemon,
@ -344,16 +263,52 @@ function quitNow() {
safeQuit(); safeQuit();
} }
function handleDaemonSubprocessExited() {
console.log('The daemon has exited.');
daemonSubprocess = null;
if (!daemonStopRequested) {
// We didn't request to stop the daemon, so display a
// warning and schedule a quit.
//
// TODO: maybe it would be better to restart the daemon?
if (rendererWindow) {
console.log('Did not request daemon stop, so quitting in 5 seconds.');
rendererWindow.loadURL(`file://${__static}/warning.html`);
setTimeout(quitNow, 5000);
} else {
console.log('Did not request daemon stop, so quitting.');
quitNow();
}
}
}
function launchDaemon() {
Assert(!daemonSubprocess, 'Tried to launch daemon twice');
console.log('Launching daemon:', DAEMON_PATH);
daemonSubprocess = ChildProcess.spawn(DAEMON_PATH);
// Need to handle the data event instead of attaching to
// process.stdout because the latter doesn't work. I believe on
// windows it buffers stdout and we don't get any meaningful output
daemonSubprocess.stdout.on('data', buf => {
console.log(String(buf).trim());
});
daemonSubprocess.stderr.on('data', buf => {
console.log(String(buf).trim());
});
daemonSubprocess.on('exit', handleDaemonSubprocessExited);
}
const isSecondaryInstance = app.makeSingleInstance(argv => { const isSecondaryInstance = app.makeSingleInstance(argv => {
if (argv.length >= 2) { if (argv.length >= 2) {
handleOpenUriRequested(argv[1]); // This will handle restoring and focusing the window handleOpenUriRequested(argv[1]); // This will handle restoring and focusing the window
} else if (win) { } else if (rendererWindow) {
if (win.isMinimized()) { if (rendererWindow.isMinimized()) {
win.restore(); rendererWindow.restore();
} else if (!win.isVisible()) { } else if (!rendererWindow.isVisible()) {
win.show(); rendererWindow.show();
} }
win.focus(); rendererWindow.focus();
} }
}); });
@ -365,126 +320,44 @@ if (isSecondaryInstance) {
function launchDaemonIfNotRunning() { function launchDaemonIfNotRunning() {
// Check if the daemon is already running. If we get // Check if the daemon is already running. If we get
// an error its because its not running // an error its because its not running
console.log("Checking for lbrynet daemon"); console.log('Checking for lbrynet daemon');
client.request("status", [], (err, res) => { client.request('status', [], err => {
if (err) { if (err) {
console.log("lbrynet daemon needs to be launched"); console.log('lbrynet daemon needs to be launched');
launchDaemon(); launchDaemon();
} else { } else {
console.log("lbrynet daemon is already running"); console.log('lbrynet daemon is already running');
} }
}); });
} }
/* // Taken from webtorrent-desktop
* Last resort for killing unresponsive daemon instances. function checkLinuxTraySupport(cb) {
* Looks for any processes called "lbrynet-daemon" and // Check that we're on Ubuntu (or another debian system) and that we have
* tries to force kill them. // libappindicator1.
*/ ChildProcess.exec('dpkg --get-selections libappindicator1', (err, stdout) => {
function forceKillAllDaemonsAndQuit() { if (err) return cb(err);
console.log( // Unfortunately there's no cleaner way, as far as I can tell, to check
"Attempting to force kill any running lbrynet-daemon instances..." // whether a debian package is installed:
); if (stdout.endsWith('\tinstall\n')) {
return cb(null);
const daemonPids = getPidsForProcessName("lbrynet-daemon");
if (!daemonPids) {
console.log("No lbrynet-daemon found running.");
quitNow();
} else {
console.log(
`Found ${
daemonPids.length
} running daemon instances. Attempting to force kill...`
);
for (const pid of daemonPids) {
let daemonKillAttemptsComplete = 0;
kill(pid, "SIGKILL", err => {
daemonKillAttemptsComplete++;
if (err) {
console.log(
`Failed to force kill daemon task with pid ${pid}. Error message: ${
err.message
}`
);
} else {
console.log(`Force killed daemon task with pid ${pid}.`);
}
if (daemonKillAttemptsComplete >= daemonPids.length - 1) {
quitNow();
}
});
} }
} return cb(new Error('debian package not installed'));
}
app.setAsDefaultProtocolClient("lbry");
app.on("ready", () => {
launchDaemonIfNotRunning();
if (process.platform === "linux") {
checkLinuxTraySupport(err => {
if (!err) createTray();
else minimize = false;
});
} else {
createTray();
}
createWindow();
});
// Quit when all windows are closed.
app.on("window-all-closed", () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== "darwin") {
app.quit();
}
});
app.on("before-quit", event => {
if (!readyToQuit) {
// We need to shutdown the daemon before we're ready to actually quit. This
// event will be triggered re-entrantly once preparation is done.
event.preventDefault();
shutdownDaemonAndQuit();
} else {
console.log("Quitting.");
}
});
app.on("activate", () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow();
}
});
if (process.platform === "darwin") {
app.on("open-url", (event, uri) => {
handleOpenUriRequested(uri);
}); });
} else if (process.argv.length >= 2) {
handleOpenUriRequested(process.argv[1]);
} }
// When a quit is attempted, this is called. It attempts to shutdown the daemon, // When a quit is attempted, this is called. It attempts to shutdown the daemon,
// then calls quitNow() to quit for real. // then calls quitNow() to quit for real.
function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) { function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) {
function doShutdown() { function doShutdown() {
console.log("Shutting down daemon"); console.log('Shutting down daemon');
daemonStopRequested = true; daemonStopRequested = true;
client.request("daemon_stop", [], (err, res) => { client.request('daemon_stop', [], err => {
if (err) { if (err) {
console.log( console.log(`received error when stopping lbrynet-daemon. Error message: ${err.message}\n`);
`received error when stopping lbrynet-daemon. Error message: ${ console.log('You will need to manually kill the daemon.');
err.message
}\n`
);
console.log("You will need to manually kill the daemon.");
} else { } else {
console.log("Successfully stopped daemon via RPC call."); console.log('Successfully stopped daemon via RPC call.');
quitNow(); quitNow();
} }
}); });
@ -493,7 +366,7 @@ function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) {
if (daemonSubprocess) { if (daemonSubprocess) {
doShutdown(); doShutdown();
} else if (!evenIfNotStartedByApp) { } else if (!evenIfNotStartedByApp) {
console.log("Not killing lbrynet-daemon because app did not start it"); console.log('Not killing lbrynet-daemon because app did not start it');
quitNow(); quitNow();
} else { } else {
doShutdown(); doShutdown();
@ -503,111 +376,154 @@ function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) {
// If not, we should wait until the daemon is closed before we start the install. // If not, we should wait until the daemon is closed before we start the install.
} }
// Taken from webtorrent-desktop if (isDevelopment) {
function checkLinuxTraySupport(cb) { import('devtron')
// Check that we're on Ubuntu (or another debian system) and that we have .then(({ install }) => {
// libappindicator1. install();
child_process.exec( console.log('Added Extension: Devtron');
"dpkg --get-selections libappindicator1", })
(err, stdout) => { .catch(error => {
if (err) return cb(err); console.error(error);
// Unfortunately there's no cleaner way, as far as I can tell, to check });
// whether a debian package is installed: import('electron-devtools-installer')
if (stdout.endsWith("\tinstall\n")) { .then(({ default: installExtension, REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS }) => {
cb(null); app.on('ready', () => {
} else { [REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS].forEach(extension => {
cb(new Error("debian package not installed")); installExtension(extension)
} .then(name => console.log(`Added Extension: ${name}`))
} .catch(err => console.log('An error occurred: ', err));
); });
});
})
.catch(error => {
console.error(error);
});
} }
ipcMain.on("upgrade", (event, installerPath) => { app.setAsDefaultProtocolClient('lbry');
app.on("quit", () => {
console.log("Launching upgrade installer at", installerPath); app.on('ready', () => {
launchDaemonIfNotRunning();
if (process.platform === 'linux') {
checkLinuxTraySupport(err => {
if (!err) createTray();
else minimize = false;
});
} else {
createTray();
}
rendererWindow = createWindow();
});
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('before-quit', event => {
if (!readyToQuit) {
// We need to shutdown the daemon before we're ready to actually quit. This
// event will be triggered re-entrantly once preparation is done.
event.preventDefault();
shutdownDaemonAndQuit();
} else {
console.log('Quitting.');
}
});
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (rendererWindow === null) {
createWindow();
}
});
if (process.platform === 'darwin') {
app.on('open-url', (event, uri) => {
handleOpenUriRequested(uri);
});
} else if (process.argv.length >= 2) {
handleOpenUriRequested(process.argv[1]);
}
ipcMain.on('upgrade', (event, installerPath) => {
app.on('quit', () => {
console.log('Launching upgrade installer at', installerPath);
// This gets triggered called after *all* other quit-related events, so // This gets triggered called after *all* other quit-related events, so
// we'll only get here if we're fully prepared and quitting for real. // we'll only get here if we're fully prepared and quitting for real.
openItem(installerPath); openItem(installerPath);
}); });
if (win) { if (rendererWindow) {
win.loadURL(`file://${__static}/upgrade.html`); rendererWindow.loadURL(`file://${__static}/upgrade.html`);
} }
shutdownDaemonAndQuit(true); shutdownDaemonAndQuit(true);
// wait for daemon to shut down before upgrading // wait for daemon to shut down before upgrading
// what to do if no shutdown in a long time? // what to do if no shutdown in a long time?
console.log("Update downloaded to", installerPath); console.log('Update downloaded to', installerPath);
console.log( console.log(
"The app will close, and you will be prompted to install the latest version of LBRY." 'The app will close, and you will be prompted to install the latest version of LBRY.'
); );
console.log("After the install is complete, please reopen the app."); console.log('After the install is complete, please reopen the app.');
}); });
ipcMain.on("version-info-requested", () => { ipcMain.on('version-info-requested', () => {
function formatRc(ver) { function formatRc(ver) {
// Adds dash if needed to make RC suffix semver friendly // Adds dash if needed to make RC suffix semver friendly
return ver.replace(/([^-])rc/, "$1-rc"); return ver.replace(/([^-])rc/, '$1-rc');
} }
let result = ""; let result = '';
const opts = { const opts = {
headers: { headers: {
"User-Agent": `LBRY/${localVersion}`, 'User-Agent': `LBRY/${localVersion}`,
}, },
}; };
const req = https.get( const req = Https.get(Object.assign(opts, Url.parse(LATEST_RELEASE_API_URL)), res => {
Object.assign(opts, url.parse(LATEST_RELEASE_API_URL)), res.on('data', data => {
res => { result += data;
res.on("data", data => { });
result += data; res.on('end', () => {
}); const tagName = JSON.parse(result).tag_name;
res.on("end", () => { const [, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
const tagName = JSON.parse(result).tag_name; if (!remoteVersion) {
const [_, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/); if (rendererWindow) {
if (!remoteVersion) { rendererWindow.webContents.send('version-info-received', null);
if (win) {
win.webContents.send("version-info-received", null);
}
} else {
const upgradeAvailable = semver.gt(
formatRc(remoteVersion),
formatRc(localVersion)
);
if (win) {
win.webContents.send("version-info-received", {
remoteVersion,
localVersion,
upgradeAvailable,
});
}
} }
}); } else {
} const upgradeAvailable = Semver.gt(formatRc(remoteVersion), formatRc(localVersion));
); if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', {
remoteVersion,
localVersion,
upgradeAvailable,
});
}
}
});
});
req.on("error", err => { req.on('error', err => {
console.log("Failed to get current version from GitHub. Error:", err); console.log('Failed to get current version from GitHub. Error:', err);
if (win) { if (rendererWindow) {
win.webContents.send("version-info-received", null); rendererWindow.webContents.send('version-info-received', null);
} }
}); });
}); });
ipcMain.on("get-auth-token", event => { ipcMain.on('get-auth-token', event => {
keytar.getPassword("LBRY", "auth_token").then(token => { Keytar.getPassword('LBRY', 'auth_token').then(token => {
event.sender.send( event.sender.send('auth-token-response', token ? token.toString().trim() : null);
"auth-token-response",
token ? token.toString().trim() : null
);
}); });
}); });
ipcMain.on("set-auth-token", (event, token) => { ipcMain.on('set-auth-token', (event, token) => {
keytar.setPassword( Keytar.setPassword('LBRY', 'auth_token', token ? token.toString().trim() : null);
"LBRY",
"auth_token",
token ? token.toString().trim() : null
);
}); });

View file

@ -1,35 +0,0 @@
const { Menu } = require("electron");
const electron = require("electron");
const app = electron.app;
const contextMenuTemplate = [
{
role: "cut",
},
{
role: "copy",
},
{
role: "paste",
},
];
module.exports = {
showContextMenu(win, posX, posY, showDevItems) {
const template = contextMenuTemplate.slice();
if (showDevItems) {
template.push({
type: "separator",
});
template.push({
label: "Inspect Element",
click() {
win.inspectElement(posX, posY);
},
});
}
Menu.buildFromTemplate(template).popup(win);
},
};

View file

@ -0,0 +1,30 @@
import { Menu } from 'electron';
const contextMenuTemplate = [
{
role: 'cut',
},
{
role: 'copy',
},
{
role: 'paste',
},
];
export default function contextMenu(win, posX, posY, showDevItems) {
const template = contextMenuTemplate.slice();
if (showDevItems) {
template.push({
type: 'separator',
});
template.push({
label: 'Inspect Element',
click() {
win.inspectElement(posX, posY);
},
});
}
Menu.buildFromTemplate(template).popup(win);
}

View file

@ -1,136 +0,0 @@
const { app, shell, Menu } = require("electron");
const { safeQuit } = require("../index.js");
const baseTemplate = [
{
label: "File",
submenu: [
{
label: "Quit",
accelerator: "CommandOrControl+Q",
click: () => safeQuit(),
},
],
},
{
label: "Edit",
submenu: [
{
role: "undo",
},
{
role: "redo",
},
{
type: "separator",
},
{
role: "cut",
},
{
role: "copy",
},
{
role: "paste",
},
{
role: "selectall",
},
],
},
{
label: "View",
submenu: [
{
role: "reload",
},
{
label: "Developer",
submenu: [
{
role: "forcereload",
},
{
role: "toggledevtools",
},
],
},
{
type: "separator",
},
{
role: "togglefullscreen",
},
],
},
{
role: "help",
submenu: [
{
label: "Learn More",
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.send("open-menu", "/help");
}
},
},
{
label: "Frequently Asked Questions",
click(item, focusedWindow) {
shell.openExternal("https://lbry.io/faq");
},
},
{
type: "separator",
},
{
label: "Report Issue",
click(item, focusedWindow) {
shell.openExternal("https://lbry.io/faq/contributing#report-a-bug");
},
},
{
type: "separator",
},
{
label: "Developer API Guide",
click(item, focusedWindow) {
shell.openExternal("https://lbry.io/quickstart");
},
},
],
},
];
const macOSAppMenuTemplate = {
label: app.getName(),
submenu: [
{
role: "about",
},
{
type: "separator",
},
{
role: "hide",
},
{
role: "hideothers",
},
{
role: "unhide",
},
{
type: "separator",
},
{
role: "quit",
},
],
};
module.exports = () => {
const template = baseTemplate.slice();
process.platform === "darwin" && template.unshift(macOSAppMenuTemplate);
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
};

138
src/main/menu/mainMenu.js Normal file
View file

@ -0,0 +1,138 @@
import { app, Menu, shell } from 'electron';
import { safeQuit } from '../index';
const baseTemplate = [
{
label: 'File',
submenu: [
{
label: 'Quit',
accelerator: 'CommandOrControl+Q',
click: () => safeQuit(),
},
],
},
{
label: 'Edit',
submenu: [
{
role: 'undo',
},
{
role: 'redo',
},
{
type: 'separator',
},
{
role: 'cut',
},
{
role: 'copy',
},
{
role: 'paste',
},
{
role: 'selectall',
},
],
},
{
label: 'View',
submenu: [
{
role: 'reload',
},
{
label: 'Developer',
submenu: [
{
role: 'forcereload',
},
{
role: 'toggledevtools',
},
],
},
{
type: 'separator',
},
{
role: 'togglefullscreen',
},
],
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.send('open-menu', '/help');
}
},
},
{
label: 'Frequently Asked Questions',
click() {
shell.openExternal('https://lbry.io/faq');
},
},
{
type: 'separator',
},
{
label: 'Report Issue',
click() {
shell.openExternal('https://lbry.io/faq/contributing#report-a-bug');
},
},
{
type: 'separator',
},
{
label: 'Developer API Guide',
click() {
shell.openExternal('https://lbry.io/quickstart');
},
},
],
},
];
const macOSAppMenuTemplate = {
label: app.getName(),
submenu: [
{
role: 'about',
},
{
type: 'separator',
},
{
role: 'hide',
},
{
role: 'hideothers',
},
{
role: 'unhide',
},
{
type: 'separator',
},
{
role: 'quit',
},
],
};
export default () => {
const template = baseTemplate.slice();
if (process.platform === 'darwin') {
template.unshift(macOSAppMenuTemplate);
}
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
};

View file

@ -1,17 +1,16 @@
import store from "store"; import store from 'store';
import { remote } from "electron"; import { remote } from 'electron';
import path from "path"; import path from 'path';
import y18n from 'y18n';
const env = process.env.NODE_ENV || "production"; const env = process.env.NODE_ENV || 'production';
const config = { const config = {
...require(`./config/${env}`), ...import(`./config/${env}`),
}; };
const i18n = require("y18n")({ const i18n = y18n({
directory: path directory: path.join(remote.app.getAppPath(), '/../static/locales').replace(/\\/g, '\\\\'),
.join(remote.app.getAppPath(), "/../static/locales")
.replace(/\\/g, "\\\\"),
updateFiles: false, updateFiles: false,
locale: "en", locale: 'en',
}); });
const logs = []; const logs = [];
@ -26,8 +25,17 @@ const app = {
}, },
}; };
window.__ = i18n.__; // Workaround for https://github.com/electron-userland/electron-webpack/issues/52
window.__n = i18n.__n; if (env !== 'development') {
window.staticResourcesPath = path
.join(remote.app.getAppPath(), '../static')
.replace(/\\/g, '\\\\');
} else {
window.staticResourcesPath = '';
}
global.app = app; // eslint-disable-next-line no-underscore-dangle
module.exports = app; window.__ = i18n.__;
// eslint-disable-next-line no-underscore-dangle
window.__n = i18n.__n;
window.app = app;

View file

@ -1,166 +1,156 @@
export const OPEN_MODAL = "OPEN_MODAL"; export const OPEN_MODAL = 'OPEN_MODAL';
export const CLOSE_MODAL = "CLOSE_MODAL"; export const CLOSE_MODAL = 'CLOSE_MODAL';
export const SHOW_SNACKBAR = "SHOW_SNACKBAR"; export const SHOW_SNACKBAR = 'SHOW_SNACKBAR';
export const REMOVE_SNACKBAR_SNACK = "REMOVE_SNACKBAR_SNACK"; export const REMOVE_SNACKBAR_SNACK = 'REMOVE_SNACKBAR_SNACK';
export const WINDOW_FOCUSED = "WINDOW_FOCUSED"; export const WINDOW_FOCUSED = 'WINDOW_FOCUSED';
export const DAEMON_READY = "DAEMON_READY"; export const DAEMON_READY = 'DAEMON_READY';
export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH"; export const DAEMON_VERSION_MATCH = 'DAEMON_VERSION_MATCH';
export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH"; export const DAEMON_VERSION_MISMATCH = 'DAEMON_VERSION_MISMATCH';
export const VOLUME_CHANGED = "VOLUME_CHANGED"; export const VOLUME_CHANGED = 'VOLUME_CHANGED';
// Navigation // Navigation
export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH"; export const CHANGE_AFTER_AUTH_PATH = 'CHANGE_AFTER_AUTH_PATH';
export const WINDOW_SCROLLED = "WINDOW_SCROLLED"; export const WINDOW_SCROLLED = 'WINDOW_SCROLLED';
export const HISTORY_NAVIGATE = "HISTORY_NAVIGATE"; export const HISTORY_NAVIGATE = 'HISTORY_NAVIGATE';
// Upgrades // Upgrades
export const UPGRADE_CANCELLED = "UPGRADE_CANCELLED"; export const UPGRADE_CANCELLED = 'UPGRADE_CANCELLED';
export const DOWNLOAD_UPGRADE = "DOWNLOAD_UPGRADE"; export const DOWNLOAD_UPGRADE = 'DOWNLOAD_UPGRADE';
export const UPGRADE_DOWNLOAD_STARTED = "UPGRADE_DOWNLOAD_STARTED"; export const UPGRADE_DOWNLOAD_STARTED = 'UPGRADE_DOWNLOAD_STARTED';
export const UPGRADE_DOWNLOAD_COMPLETED = "UPGRADE_DOWNLOAD_COMPLETED"; export const UPGRADE_DOWNLOAD_COMPLETED = 'UPGRADE_DOWNLOAD_COMPLETED';
export const UPGRADE_DOWNLOAD_PROGRESSED = "UPGRADE_DOWNLOAD_PROGRESSED"; export const UPGRADE_DOWNLOAD_PROGRESSED = 'UPGRADE_DOWNLOAD_PROGRESSED';
export const CHECK_UPGRADE_AVAILABLE = "CHECK_UPGRADE_AVAILABLE"; export const CHECK_UPGRADE_AVAILABLE = 'CHECK_UPGRADE_AVAILABLE';
export const CHECK_UPGRADE_START = "CHECK_UPGRADE_START"; export const CHECK_UPGRADE_START = 'CHECK_UPGRADE_START';
export const CHECK_UPGRADE_SUCCESS = "CHECK_UPGRADE_SUCCESS"; export const CHECK_UPGRADE_SUCCESS = 'CHECK_UPGRADE_SUCCESS';
export const CHECK_UPGRADE_FAIL = "CHECK_UPGRADE_FAIL"; export const CHECK_UPGRADE_FAIL = 'CHECK_UPGRADE_FAIL';
export const CHECK_UPGRADE_SUBSCRIBE = "CHECK_UPGRADE_SUBSCRIBE"; export const CHECK_UPGRADE_SUBSCRIBE = 'CHECK_UPGRADE_SUBSCRIBE';
export const UPDATE_VERSION = "UPDATE_VERSION"; export const UPDATE_VERSION = 'UPDATE_VERSION';
export const UPDATE_REMOTE_VERSION = "UPDATE_REMOTE_VERSION"; export const UPDATE_REMOTE_VERSION = 'UPDATE_REMOTE_VERSION';
export const SKIP_UPGRADE = "SKIP_UPGRADE"; export const SKIP_UPGRADE = 'SKIP_UPGRADE';
export const START_UPGRADE = "START_UPGRADE"; export const START_UPGRADE = 'START_UPGRADE';
// Wallet // Wallet
export const GET_NEW_ADDRESS_STARTED = "GET_NEW_ADDRESS_STARTED"; export const GET_NEW_ADDRESS_STARTED = 'GET_NEW_ADDRESS_STARTED';
export const GET_NEW_ADDRESS_COMPLETED = "GET_NEW_ADDRESS_COMPLETED"; export const GET_NEW_ADDRESS_COMPLETED = 'GET_NEW_ADDRESS_COMPLETED';
export const FETCH_TRANSACTIONS_STARTED = "FETCH_TRANSACTIONS_STARTED"; export const FETCH_TRANSACTIONS_STARTED = 'FETCH_TRANSACTIONS_STARTED';
export const FETCH_TRANSACTIONS_COMPLETED = "FETCH_TRANSACTIONS_COMPLETED"; export const FETCH_TRANSACTIONS_COMPLETED = 'FETCH_TRANSACTIONS_COMPLETED';
export const UPDATE_BALANCE = "UPDATE_BALANCE"; export const UPDATE_BALANCE = 'UPDATE_BALANCE';
export const CHECK_ADDRESS_IS_MINE_STARTED = "CHECK_ADDRESS_IS_MINE_STARTED"; export const CHECK_ADDRESS_IS_MINE_STARTED = 'CHECK_ADDRESS_IS_MINE_STARTED';
export const CHECK_ADDRESS_IS_MINE_COMPLETED = export const CHECK_ADDRESS_IS_MINE_COMPLETED = 'CHECK_ADDRESS_IS_MINE_COMPLETED';
"CHECK_ADDRESS_IS_MINE_COMPLETED"; export const SET_DRAFT_TRANSACTION_AMOUNT = 'SET_DRAFT_TRANSACTION_AMOUNT';
export const SET_DRAFT_TRANSACTION_AMOUNT = "SET_DRAFT_TRANSACTION_AMOUNT"; export const SET_DRAFT_TRANSACTION_ADDRESS = 'SET_DRAFT_TRANSACTION_ADDRESS';
export const SET_DRAFT_TRANSACTION_ADDRESS = "SET_DRAFT_TRANSACTION_ADDRESS"; export const SEND_TRANSACTION_STARTED = 'SEND_TRANSACTION_STARTED';
export const SEND_TRANSACTION_STARTED = "SEND_TRANSACTION_STARTED"; export const SEND_TRANSACTION_COMPLETED = 'SEND_TRANSACTION_COMPLETED';
export const SEND_TRANSACTION_COMPLETED = "SEND_TRANSACTION_COMPLETED"; export const SEND_TRANSACTION_FAILED = 'SEND_TRANSACTION_FAILED';
export const SEND_TRANSACTION_FAILED = "SEND_TRANSACTION_FAILED"; export const FETCH_BLOCK_SUCCESS = 'FETCH_BLOCK_SUCCESS';
export const FETCH_BLOCK_SUCCESS = "FETCH_BLOCK_SUCCESS"; export const SUPPORT_TRANSACTION_STARTED = 'SUPPORT_TRANSACTION_STARTED';
export const SUPPORT_TRANSACTION_STARTED = "SUPPORT_TRANSACTION_STARTED"; export const SUPPORT_TRANSACTION_COMPLETED = 'SUPPORT_TRANSACTION_COMPLETED';
export const SUPPORT_TRANSACTION_COMPLETED = "SUPPORT_TRANSACTION_COMPLETED"; export const SUPPORT_TRANSACTION_FAILED = 'SUPPORT_TRANSACTION_FAILED';
export const SUPPORT_TRANSACTION_FAILED = "SUPPORT_TRANSACTION_FAILED";
// Claims // Claims
export const FETCH_FEATURED_CONTENT_STARTED = "FETCH_FEATURED_CONTENT_STARTED"; export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';
export const FETCH_FEATURED_CONTENT_COMPLETED = export const FETCH_FEATURED_CONTENT_COMPLETED = 'FETCH_FEATURED_CONTENT_COMPLETED';
"FETCH_FEATURED_CONTENT_COMPLETED"; export const RESOLVE_URIS_STARTED = 'RESOLVE_URIS_STARTED';
export const RESOLVE_URIS_STARTED = "RESOLVE_URIS_STARTED"; export const RESOLVE_URIS_COMPLETED = 'RESOLVE_URIS_COMPLETED';
export const RESOLVE_URIS_COMPLETED = "RESOLVE_URIS_COMPLETED"; export const FETCH_CHANNEL_CLAIMS_STARTED = 'FETCH_CHANNEL_CLAIMS_STARTED';
export const FETCH_CHANNEL_CLAIMS_STARTED = "FETCH_CHANNEL_CLAIMS_STARTED"; export const FETCH_CHANNEL_CLAIMS_COMPLETED = 'FETCH_CHANNEL_CLAIMS_COMPLETED';
export const FETCH_CHANNEL_CLAIMS_COMPLETED = "FETCH_CHANNEL_CLAIMS_COMPLETED"; export const FETCH_CHANNEL_CLAIM_COUNT_STARTED = 'FETCH_CHANNEL_CLAIM_COUNT_STARTED';
export const FETCH_CHANNEL_CLAIM_COUNT_STARTED = export const FETCH_CHANNEL_CLAIM_COUNT_COMPLETED = 'FETCH_CHANNEL_CLAIM_COUNT_COMPLETED';
"FETCH_CHANNEL_CLAIM_COUNT_STARTED"; export const FETCH_CLAIM_LIST_MINE_STARTED = 'FETCH_CLAIM_LIST_MINE_STARTED';
export const FETCH_CHANNEL_CLAIM_COUNT_COMPLETED = export const FETCH_CLAIM_LIST_MINE_COMPLETED = 'FETCH_CLAIM_LIST_MINE_COMPLETED';
"FETCH_CHANNEL_CLAIM_COUNT_COMPLETED"; export const ABANDON_CLAIM_STARTED = 'ABANDON_CLAIM_STARTED';
export const FETCH_CLAIM_LIST_MINE_STARTED = "FETCH_CLAIM_LIST_MINE_STARTED"; export const ABANDON_CLAIM_SUCCEEDED = 'ABANDON_CLAIM_SUCCEEDED';
export const FETCH_CLAIM_LIST_MINE_COMPLETED = export const FETCH_CHANNEL_LIST_MINE_STARTED = 'FETCH_CHANNEL_LIST_MINE_STARTED';
"FETCH_CLAIM_LIST_MINE_COMPLETED"; export const FETCH_CHANNEL_LIST_MINE_COMPLETED = 'FETCH_CHANNEL_LIST_MINE_COMPLETED';
export const ABANDON_CLAIM_STARTED = "ABANDON_CLAIM_STARTED"; export const CREATE_CHANNEL_STARTED = 'CREATE_CHANNEL_STARTED';
export const ABANDON_CLAIM_SUCCEEDED = "ABANDON_CLAIM_SUCCEEDED"; export const CREATE_CHANNEL_COMPLETED = 'CREATE_CHANNEL_COMPLETED';
export const FETCH_CHANNEL_LIST_MINE_STARTED = export const PUBLISH_STARTED = 'PUBLISH_STARTED';
"FETCH_CHANNEL_LIST_MINE_STARTED"; export const PUBLISH_COMPLETED = 'PUBLISH_COMPLETED';
export const FETCH_CHANNEL_LIST_MINE_COMPLETED = export const PUBLISH_FAILED = 'PUBLISH_FAILED';
"FETCH_CHANNEL_LIST_MINE_COMPLETED"; export const SET_PLAYING_URI = 'PLAY_URI';
export const CREATE_CHANNEL_STARTED = "CREATE_CHANNEL_STARTED";
export const CREATE_CHANNEL_COMPLETED = "CREATE_CHANNEL_COMPLETED";
export const PUBLISH_STARTED = "PUBLISH_STARTED";
export const PUBLISH_COMPLETED = "PUBLISH_COMPLETED";
export const PUBLISH_FAILED = "PUBLISH_FAILED";
export const SET_PLAYING_URI = "PLAY_URI";
// Files // Files
export const FILE_LIST_STARTED = "FILE_LIST_STARTED"; export const FILE_LIST_STARTED = 'FILE_LIST_STARTED';
export const FILE_LIST_SUCCEEDED = "FILE_LIST_SUCCEEDED"; export const FILE_LIST_SUCCEEDED = 'FILE_LIST_SUCCEEDED';
export const FETCH_FILE_INFO_STARTED = "FETCH_FILE_INFO_STARTED"; export const FETCH_FILE_INFO_STARTED = 'FETCH_FILE_INFO_STARTED';
export const FETCH_FILE_INFO_COMPLETED = "FETCH_FILE_INFO_COMPLETED"; export const FETCH_FILE_INFO_COMPLETED = 'FETCH_FILE_INFO_COMPLETED';
export const FETCH_COST_INFO_STARTED = "FETCH_COST_INFO_STARTED"; export const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED';
export const FETCH_COST_INFO_COMPLETED = "FETCH_COST_INFO_COMPLETED"; export const FETCH_COST_INFO_COMPLETED = 'FETCH_COST_INFO_COMPLETED';
export const LOADING_VIDEO_STARTED = "LOADING_VIDEO_STARTED"; export const LOADING_VIDEO_STARTED = 'LOADING_VIDEO_STARTED';
export const LOADING_VIDEO_COMPLETED = "LOADING_VIDEO_COMPLETED"; export const LOADING_VIDEO_COMPLETED = 'LOADING_VIDEO_COMPLETED';
export const LOADING_VIDEO_FAILED = "LOADING_VIDEO_FAILED"; export const LOADING_VIDEO_FAILED = 'LOADING_VIDEO_FAILED';
export const DOWNLOADING_STARTED = "DOWNLOADING_STARTED"; export const DOWNLOADING_STARTED = 'DOWNLOADING_STARTED';
export const DOWNLOADING_PROGRESSED = "DOWNLOADING_PROGRESSED"; export const DOWNLOADING_PROGRESSED = 'DOWNLOADING_PROGRESSED';
export const DOWNLOADING_COMPLETED = "DOWNLOADING_COMPLETED"; export const DOWNLOADING_COMPLETED = 'DOWNLOADING_COMPLETED';
export const PLAY_VIDEO_STARTED = "PLAY_VIDEO_STARTED"; export const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED';
export const FETCH_AVAILABILITY_STARTED = "FETCH_AVAILABILITY_STARTED"; export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED';
export const FETCH_AVAILABILITY_COMPLETED = "FETCH_AVAILABILITY_COMPLETED"; export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED';
export const FILE_DELETE = "FILE_DELETE"; export const FILE_DELETE = 'FILE_DELETE';
// Search // Search
export const SEARCH_STARTED = "SEARCH_STARTED"; export const SEARCH_STARTED = 'SEARCH_STARTED';
export const SEARCH_COMPLETED = "SEARCH_COMPLETED"; export const SEARCH_COMPLETED = 'SEARCH_COMPLETED';
export const SEARCH_CANCELLED = "SEARCH_CANCELLED"; export const SEARCH_CANCELLED = 'SEARCH_CANCELLED';
// Settings // Settings
export const DAEMON_SETTINGS_RECEIVED = "DAEMON_SETTINGS_RECEIVED"; export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
export const CLIENT_SETTING_CHANGED = "CLIENT_SETTING_CHANGED"; export const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED';
// User // User
export const AUTHENTICATION_STARTED = "AUTHENTICATION_STARTED"; export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
export const AUTHENTICATION_SUCCESS = "AUTHENTICATION_SUCCESS"; export const AUTHENTICATION_SUCCESS = 'AUTHENTICATION_SUCCESS';
export const AUTHENTICATION_FAILURE = "AUTHENTICATION_FAILURE"; export const AUTHENTICATION_FAILURE = 'AUTHENTICATION_FAILURE';
export const USER_EMAIL_DECLINE = "USER_EMAIL_DECLINE"; export const USER_EMAIL_DECLINE = 'USER_EMAIL_DECLINE';
export const USER_EMAIL_NEW_STARTED = "USER_EMAIL_NEW_STARTED"; export const USER_EMAIL_NEW_STARTED = 'USER_EMAIL_NEW_STARTED';
export const USER_EMAIL_NEW_SUCCESS = "USER_EMAIL_NEW_SUCCESS"; export const USER_EMAIL_NEW_SUCCESS = 'USER_EMAIL_NEW_SUCCESS';
export const USER_EMAIL_NEW_EXISTS = "USER_EMAIL_NEW_EXISTS"; export const USER_EMAIL_NEW_EXISTS = 'USER_EMAIL_NEW_EXISTS';
export const USER_EMAIL_NEW_FAILURE = "USER_EMAIL_NEW_FAILURE"; export const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE';
export const USER_EMAIL_VERIFY_STARTED = "USER_EMAIL_VERIFY_STARTED"; export const USER_EMAIL_VERIFY_STARTED = 'USER_EMAIL_VERIFY_STARTED';
export const USER_EMAIL_VERIFY_SUCCESS = "USER_EMAIL_VERIFY_SUCCESS"; export const USER_EMAIL_VERIFY_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS';
export const USER_EMAIL_VERIFY_FAILURE = "USER_EMAIL_VERIFY_FAILURE"; export const USER_EMAIL_VERIFY_FAILURE = 'USER_EMAIL_VERIFY_FAILURE';
export const USER_IDENTITY_VERIFY_STARTED = "USER_IDENTITY_VERIFY_STARTED"; export const USER_IDENTITY_VERIFY_STARTED = 'USER_IDENTITY_VERIFY_STARTED';
export const USER_IDENTITY_VERIFY_SUCCESS = "USER_IDENTITY_VERIFY_SUCCESS"; export const USER_IDENTITY_VERIFY_SUCCESS = 'USER_IDENTITY_VERIFY_SUCCESS';
export const USER_IDENTITY_VERIFY_FAILURE = "USER_IDENTITY_VERIFY_FAILURE"; export const USER_IDENTITY_VERIFY_FAILURE = 'USER_IDENTITY_VERIFY_FAILURE';
export const USER_FETCH_STARTED = "USER_FETCH_STARTED"; export const USER_FETCH_STARTED = 'USER_FETCH_STARTED';
export const USER_FETCH_SUCCESS = "USER_FETCH_SUCCESS"; export const USER_FETCH_SUCCESS = 'USER_FETCH_SUCCESS';
export const USER_FETCH_FAILURE = "USER_FETCH_FAILURE"; export const USER_FETCH_FAILURE = 'USER_FETCH_FAILURE';
export const USER_INVITE_STATUS_FETCH_STARTED = export const USER_INVITE_STATUS_FETCH_STARTED = 'USER_INVITE_STATUS_FETCH_STARTED';
"USER_INVITE_STATUS_FETCH_STARTED"; export const USER_INVITE_STATUS_FETCH_SUCCESS = 'USER_INVITE_STATUS_FETCH_SUCCESS';
export const USER_INVITE_STATUS_FETCH_SUCCESS = export const USER_INVITE_STATUS_FETCH_FAILURE = 'USER_INVITE_STATUS_FETCH_FAILURE';
"USER_INVITE_STATUS_FETCH_SUCCESS"; export const USER_INVITE_NEW_STARTED = 'USER_INVITE_NEW_STARTED';
export const USER_INVITE_STATUS_FETCH_FAILURE = export const USER_INVITE_NEW_SUCCESS = 'USER_INVITE_NEW_SUCCESS';
"USER_INVITE_STATUS_FETCH_FAILURE"; export const USER_INVITE_NEW_FAILURE = 'USER_INVITE_NEW_FAILURE';
export const USER_INVITE_NEW_STARTED = "USER_INVITE_NEW_STARTED"; export const FETCH_ACCESS_TOKEN_SUCCESS = 'FETCH_ACCESS_TOKEN_SUCCESS';
export const USER_INVITE_NEW_SUCCESS = "USER_INVITE_NEW_SUCCESS";
export const USER_INVITE_NEW_FAILURE = "USER_INVITE_NEW_FAILURE";
export const FETCH_ACCESS_TOKEN_SUCCESS = "FETCH_ACCESS_TOKEN_SUCCESS";
// Rewards // Rewards
export const FETCH_REWARDS_STARTED = "FETCH_REWARDS_STARTED"; export const FETCH_REWARDS_STARTED = 'FETCH_REWARDS_STARTED';
export const FETCH_REWARDS_COMPLETED = "FETCH_REWARDS_COMPLETED"; export const FETCH_REWARDS_COMPLETED = 'FETCH_REWARDS_COMPLETED';
export const CLAIM_REWARD_STARTED = "CLAIM_REWARD_STARTED"; export const CLAIM_REWARD_STARTED = 'CLAIM_REWARD_STARTED';
export const CLAIM_REWARD_SUCCESS = "CLAIM_REWARD_SUCCESS"; export const CLAIM_REWARD_SUCCESS = 'CLAIM_REWARD_SUCCESS';
export const CLAIM_REWARD_FAILURE = "CLAIM_REWARD_FAILURE"; export const CLAIM_REWARD_FAILURE = 'CLAIM_REWARD_FAILURE';
export const CLAIM_REWARD_CLEAR_ERROR = "CLAIM_REWARD_CLEAR_ERROR"; export const CLAIM_REWARD_CLEAR_ERROR = 'CLAIM_REWARD_CLEAR_ERROR';
export const FETCH_REWARD_CONTENT_COMPLETED = "FETCH_REWARD_CONTENT_COMPLETED"; export const FETCH_REWARD_CONTENT_COMPLETED = 'FETCH_REWARD_CONTENT_COMPLETED';
// Language // Language
export const DOWNLOAD_LANGUAGE_SUCCEEDED = "DOWNLOAD_LANGUAGE_SUCCEEDED"; export const DOWNLOAD_LANGUAGE_SUCCEEDED = 'DOWNLOAD_LANGUAGE_SUCCEEDED';
export const DOWNLOAD_LANGUAGE_FAILED = "DOWNLOAD_LANGUAGE_FAILED"; export const DOWNLOAD_LANGUAGE_FAILED = 'DOWNLOAD_LANGUAGE_FAILED';
// ShapeShift // ShapeShift
export const GET_SUPPORTED_COINS_START = "GET_SUPPORTED_COINS_START"; export const GET_SUPPORTED_COINS_START = 'GET_SUPPORTED_COINS_START';
export const GET_SUPPORTED_COINS_SUCCESS = "GET_SUPPORTED_COINS_SUCCESS"; export const GET_SUPPORTED_COINS_SUCCESS = 'GET_SUPPORTED_COINS_SUCCESS';
export const GET_SUPPORTED_COINS_FAIL = "GET_SUPPORTED_COINS_FAIL"; export const GET_SUPPORTED_COINS_FAIL = 'GET_SUPPORTED_COINS_FAIL';
export const GET_COIN_STATS_START = "GET_COIN_STATS_START"; export const GET_COIN_STATS_START = 'GET_COIN_STATS_START';
export const GET_COIN_STATS_SUCCESS = "GET_COIN_STATS_SUCCESS"; export const GET_COIN_STATS_SUCCESS = 'GET_COIN_STATS_SUCCESS';
export const GET_COIN_STATS_FAIL = "GET_COIN_STATS_FAIL"; export const GET_COIN_STATS_FAIL = 'GET_COIN_STATS_FAIL';
export const PREPARE_SHAPE_SHIFT_START = "PREPARE_SHAPE_SHIFT_START"; export const PREPARE_SHAPE_SHIFT_START = 'PREPARE_SHAPE_SHIFT_START';
export const PREPARE_SHAPE_SHIFT_SUCCESS = "PREPARE_SHAPE_SHIFT_SUCCESS"; export const PREPARE_SHAPE_SHIFT_SUCCESS = 'PREPARE_SHAPE_SHIFT_SUCCESS';
export const PREPARE_SHAPE_SHIFT_FAIL = "PREPARE_SHAPE_SHIFT_FAIL"; export const PREPARE_SHAPE_SHIFT_FAIL = 'PREPARE_SHAPE_SHIFT_FAIL';
export const GET_ACTIVE_SHIFT_START = "GET_ACTIVE_SHIFT_START"; export const GET_ACTIVE_SHIFT_START = 'GET_ACTIVE_SHIFT_START';
export const GET_ACTIVE_SHIFT_SUCCESS = "GET_ACTIVE_SHIFT_SUCCESS"; export const GET_ACTIVE_SHIFT_SUCCESS = 'GET_ACTIVE_SHIFT_SUCCESS';
export const GET_ACTIVE_SHIFT_FAIL = "GET_ACTIVE_SHIFT_FAIL"; export const GET_ACTIVE_SHIFT_FAIL = 'GET_ACTIVE_SHIFT_FAIL';
export const CLEAR_SHAPE_SHIFT = "CLEAR_SHAPE_SHIFT"; export const CLEAR_SHAPE_SHIFT = 'CLEAR_SHAPE_SHIFT';
// Subscriptions // Subscriptions
export const CHANNEL_SUBSCRIBE = "CHANNEL_SUBSCRIBE"; export const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
export const CHANNEL_UNSUBSCRIBE = "CHANNEL_UNSUBSCRIBE"; export const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
export const HAS_FETCHED_SUBSCRIPTIONS = "HAS_FETCHED_SUBSCRIPTIONS"; export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';

View file

@ -1,5 +1,5 @@
export const FEATURED = "rocket"; export const FEATURED = 'rocket';
export const LOCAL = "folder"; export const LOCAL = 'folder';
export const FILE = "file"; export const FILE = 'file';
export const HISTORY = "history"; export const HISTORY = 'history';
export const HELP_CIRCLE = "question-circle"; export const HELP_CIRCLE = 'question-circle';

View file

@ -1,187 +1,187 @@
const LANGUAGES = { const LANGUAGES = {
aa: ["Afar", "Afar"], aa: ['Afar', 'Afar'],
ab: ["Abkhazian", "Аҧсуа"], ab: ['Abkhazian', 'Аҧсуа'],
af: ["Afrikaans", "Afrikaans"], af: ['Afrikaans', 'Afrikaans'],
ak: ["Akan", "Akana"], ak: ['Akan', 'Akana'],
am: ["Amharic", "አማርኛ"], am: ['Amharic', 'አማርኛ'],
an: ["Aragonese", "Aragonés"], an: ['Aragonese', 'Aragonés'],
ar: ["Arabic", "العربية"], ar: ['Arabic', 'العربية'],
as: ["Assamese", "অসমীয়া"], as: ['Assamese', 'অসমীয়া'],
av: ["Avar", "Авар"], av: ['Avar', 'Авар'],
ay: ["Aymara", "Aymar"], ay: ['Aymara', 'Aymar'],
az: ["Azerbaijani", "Azərbaycanca / آذربايجان"], az: ['Azerbaijani', 'Azərbaycanca / آذربايجان'],
ba: ["Bashkir", "Башҡорт"], ba: ['Bashkir', 'Башҡорт'],
be: ["Belarusian", "Беларуская"], be: ['Belarusian', 'Беларуская'],
bg: ["Bulgarian", "Български"], bg: ['Bulgarian', 'Български'],
bh: ["Bihari", "भोजपुरी"], bh: ['Bihari', 'भोजपुरी'],
bi: ["Bislama", "Bislama"], bi: ['Bislama', 'Bislama'],
bm: ["Bambara", "Bamanankan"], bm: ['Bambara', 'Bamanankan'],
bn: ["Bengali", "বাংলা"], bn: ['Bengali', 'বাংলা'],
bo: ["Tibetan", "བོད་ཡིག / Bod skad"], bo: ['Tibetan', 'བོད་ཡིག / Bod skad'],
br: ["Breton", "Brezhoneg"], br: ['Breton', 'Brezhoneg'],
bs: ["Bosnian", "Bosanski"], bs: ['Bosnian', 'Bosanski'],
ca: ["Catalan", "Català"], ca: ['Catalan', 'Català'],
ce: ["Chechen", "Нохчийн"], ce: ['Chechen', 'Нохчийн'],
ch: ["Chamorro", "Chamoru"], ch: ['Chamorro', 'Chamoru'],
co: ["Corsican", "Corsu"], co: ['Corsican', 'Corsu'],
cr: ["Cree", "Nehiyaw"], cr: ['Cree', 'Nehiyaw'],
cs: ["Czech", "Česky"], cs: ['Czech', 'Česky'],
cu: ["Old Church Slavonic / Old Bulgarian", "словѣньскъ / slověnĭskŭ"], cu: ['Old Church Slavonic / Old Bulgarian', 'словѣньскъ / slověnĭskŭ'],
cv: ["Chuvash", "Чăваш"], cv: ['Chuvash', 'Чăваш'],
cy: ["Welsh", "Cymraeg"], cy: ['Welsh', 'Cymraeg'],
da: ["Danish", "Dansk"], da: ['Danish', 'Dansk'],
de: ["German", "Deutsch"], de: ['German', 'Deutsch'],
dv: ["Divehi", "ދިވެހިބަސް"], dv: ['Divehi', 'ދިވެހިބަސް'],
dz: ["Dzongkha", "ཇོང་ཁ"], dz: ['Dzongkha', 'ཇོང་ཁ'],
ee: ["Ewe", "Ɛʋɛ"], ee: ['Ewe', 'Ɛʋɛ'],
el: ["Greek", "Ελληνικά"], el: ['Greek', 'Ελληνικά'],
en: ["English", "English"], en: ['English', 'English'],
eo: ["Esperanto", "Esperanto"], eo: ['Esperanto', 'Esperanto'],
es: ["Spanish", "Español"], es: ['Spanish', 'Español'],
et: ["Estonian", "Eesti"], et: ['Estonian', 'Eesti'],
eu: ["Basque", "Euskara"], eu: ['Basque', 'Euskara'],
fa: ["Persian", "فارسی"], fa: ['Persian', 'فارسی'],
ff: ["Peul", "Fulfulde"], ff: ['Peul', 'Fulfulde'],
fi: ["Finnish", "Suomi"], fi: ['Finnish', 'Suomi'],
fj: ["Fijian", "Na Vosa Vakaviti"], fj: ['Fijian', 'Na Vosa Vakaviti'],
fo: ["Faroese", "Føroyskt"], fo: ['Faroese', 'Føroyskt'],
fr: ["French", "Français"], fr: ['French', 'Français'],
fy: ["West Frisian", "Frysk"], fy: ['West Frisian', 'Frysk'],
ga: ["Irish", "Gaeilge"], ga: ['Irish', 'Gaeilge'],
gd: ["Scottish Gaelic", "Gàidhlig"], gd: ['Scottish Gaelic', 'Gàidhlig'],
gl: ["Galician", "Galego"], gl: ['Galician', 'Galego'],
gn: ["Guarani", "Avañe'ẽ"], gn: ['Guarani', "Avañe'ẽ"],
gu: ["Gujarati", "ગુજરાતી"], gu: ['Gujarati', 'ગુજરાતી'],
gv: ["Manx", "Gaelg"], gv: ['Manx', 'Gaelg'],
ha: ["Hausa", "هَوُسَ"], ha: ['Hausa', 'هَوُسَ'],
he: ["Hebrew", "עברית"], he: ['Hebrew', 'עברית'],
hi: ["Hindi", "हिन्दी"], hi: ['Hindi', 'हिन्दी'],
ho: ["Hiri Motu", "Hiri Motu"], ho: ['Hiri Motu', 'Hiri Motu'],
hr: ["Croatian", "Hrvatski"], hr: ['Croatian', 'Hrvatski'],
ht: ["Haitian", "Krèyol ayisyen"], ht: ['Haitian', 'Krèyol ayisyen'],
hu: ["Hungarian", "Magyar"], hu: ['Hungarian', 'Magyar'],
hy: ["Armenian", "Հայերեն"], hy: ['Armenian', 'Հայերեն'],
hz: ["Herero", "Otsiherero"], hz: ['Herero', 'Otsiherero'],
ia: ["Interlingua", "Interlingua"], ia: ['Interlingua', 'Interlingua'],
id: ["Indonesian", "Bahasa Indonesia"], id: ['Indonesian', 'Bahasa Indonesia'],
ie: ["Interlingue", "Interlingue"], ie: ['Interlingue', 'Interlingue'],
ig: ["Igbo", "Igbo"], ig: ['Igbo', 'Igbo'],
ii: ["Sichuan Yi", "ꆇꉙ / 四川彝语"], ii: ['Sichuan Yi', 'ꆇꉙ / 四川彝语'],
ik: ["Inupiak", "Iñupiak"], ik: ['Inupiak', 'Iñupiak'],
io: ["Ido", "Ido"], io: ['Ido', 'Ido'],
is: ["Icelandic", "Íslenska"], is: ['Icelandic', 'Íslenska'],
it: ["Italian", "Italiano"], it: ['Italian', 'Italiano'],
iu: ["Inuktitut", "ᐃᓄᒃᑎᑐᑦ"], iu: ['Inuktitut', 'ᐃᓄᒃᑎᑐᑦ'],
ja: ["Japanese", "日本語"], ja: ['Japanese', '日本語'],
jv: ["Javanese", "Basa Jawa"], jv: ['Javanese', 'Basa Jawa'],
ka: ["Georgian", "ქართული"], ka: ['Georgian', 'ქართული'],
kg: ["Kongo", "KiKongo"], kg: ['Kongo', 'KiKongo'],
ki: ["Kikuyu", "Gĩkũyũ"], ki: ['Kikuyu', 'Gĩkũyũ'],
kj: ["Kuanyama", "Kuanyama"], kj: ['Kuanyama', 'Kuanyama'],
kk: ["Kazakh", "Қазақша"], kk: ['Kazakh', 'Қазақша'],
kl: ["Greenlandic", "Kalaallisut"], kl: ['Greenlandic', 'Kalaallisut'],
km: ["Cambodian", "ភាសាខ្មែរ"], km: ['Cambodian', 'ភាសាខ្មែរ'],
kn: ["Kannada", "ಕನ್ನಡ"], kn: ['Kannada', 'ಕನ್ನಡ'],
ko: ["Korean", "한국어"], ko: ['Korean', '한국어'],
kr: ["Kanuri", "Kanuri"], kr: ['Kanuri', 'Kanuri'],
ks: ["Kashmiri", "कश्मीरी / كشميري"], ks: ['Kashmiri', 'कश्मीरी / كشميري'],
ku: ["Kurdish", "Kurdî / كوردی"], ku: ['Kurdish', 'Kurdî / كوردی'],
kv: ["Komi", "Коми"], kv: ['Komi', 'Коми'],
kw: ["Cornish", "Kernewek"], kw: ['Cornish', 'Kernewek'],
ky: ["Kirghiz", "Kırgızca / Кыргызча"], ky: ['Kirghiz', 'Kırgızca / Кыргызча'],
la: ["Latin", "Latina"], la: ['Latin', 'Latina'],
lb: ["Luxembourgish", "Lëtzebuergesch"], lb: ['Luxembourgish', 'Lëtzebuergesch'],
lg: ["Ganda", "Luganda"], lg: ['Ganda', 'Luganda'],
li: ["Limburgian", "Limburgs"], li: ['Limburgian', 'Limburgs'],
ln: ["Lingala", "Lingála"], ln: ['Lingala', 'Lingála'],
lo: ["Laotian", "ລາວ / Pha xa lao"], lo: ['Laotian', 'ລາວ / Pha xa lao'],
lt: ["Lithuanian", "Lietuvių"], lt: ['Lithuanian', 'Lietuvių'],
lv: ["Latvian", "Latviešu"], lv: ['Latvian', 'Latviešu'],
mg: ["Malagasy", "Malagasy"], mg: ['Malagasy', 'Malagasy'],
mh: ["Marshallese", "Kajin Majel / Ebon"], mh: ['Marshallese', 'Kajin Majel / Ebon'],
mi: ["Maori", "Māori"], mi: ['Maori', 'Māori'],
mk: ["Macedonian", "Македонски"], mk: ['Macedonian', 'Македонски'],
ml: ["Malayalam", "മലയാളം"], ml: ['Malayalam', 'മലയാളം'],
mn: ["Mongolian", "Монгол"], mn: ['Mongolian', 'Монгол'],
mo: ["Moldovan", "Moldovenească"], mo: ['Moldovan', 'Moldovenească'],
mr: ["Marathi", "मराठी"], mr: ['Marathi', 'मराठी'],
ms: ["Malay", "Bahasa Melayu"], ms: ['Malay', 'Bahasa Melayu'],
mt: ["Maltese", "bil-Malti"], mt: ['Maltese', 'bil-Malti'],
my: ["Burmese", "Myanmasa"], my: ['Burmese', 'Myanmasa'],
na: ["Nauruan", "Dorerin Naoero"], na: ['Nauruan', 'Dorerin Naoero'],
nd: ["North Ndebele", "Sindebele"], nd: ['North Ndebele', 'Sindebele'],
ne: ["Nepali", "नेपाली"], ne: ['Nepali', 'नेपाली'],
ng: ["Ndonga", "Oshiwambo"], ng: ['Ndonga', 'Oshiwambo'],
nl: ["Dutch", "Nederlands"], nl: ['Dutch', 'Nederlands'],
nn: ["Norwegian Nynorsk", "Norsk (nynorsk)"], nn: ['Norwegian Nynorsk', 'Norsk (nynorsk)'],
no: ["Norwegian", "Norsk (bokmål / riksmål)"], no: ['Norwegian', 'Norsk (bokmål / riksmål)'],
nr: ["South Ndebele", "isiNdebele"], nr: ['South Ndebele', 'isiNdebele'],
nv: ["Navajo", "Diné bizaad"], nv: ['Navajo', 'Diné bizaad'],
ny: ["Chichewa", "Chi-Chewa"], ny: ['Chichewa', 'Chi-Chewa'],
oc: ["Occitan", "Occitan"], oc: ['Occitan', 'Occitan'],
oj: ["Ojibwa", "ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin"], oj: ['Ojibwa', 'ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin'],
om: ["Oromo", "Oromoo"], om: ['Oromo', 'Oromoo'],
or: ["Oriya", "ଓଡ଼ିଆ"], or: ['Oriya', 'ଓଡ଼ିଆ'],
os: ["Ossetian / Ossetic", "Иронау"], os: ['Ossetian / Ossetic', 'Иронау'],
pa: ["Panjabi / Punjabi", "ਪੰਜਾਬੀ / पंजाबी / پنجابي"], pa: ['Panjabi / Punjabi', 'ਪੰਜਾਬੀ / पंजाबी / پنجابي'],
pi: ["Pali", "Pāli / पाऴि"], pi: ['Pali', 'Pāli / पाऴि'],
pl: ["Polish", "Polski"], pl: ['Polish', 'Polski'],
ps: ["Pashto", "پښتو"], ps: ['Pashto', 'پښتو'],
pt: ["Portuguese", "Português"], pt: ['Portuguese', 'Português'],
qu: ["Quechua", "Runa Simi"], qu: ['Quechua', 'Runa Simi'],
rm: ["Raeto Romance", "Rumantsch"], rm: ['Raeto Romance', 'Rumantsch'],
rn: ["Kirundi", "Kirundi"], rn: ['Kirundi', 'Kirundi'],
ro: ["Romanian", "Română"], ro: ['Romanian', 'Română'],
ru: ["Russian", "Русский"], ru: ['Russian', 'Русский'],
rw: ["Rwandi", "Kinyarwandi"], rw: ['Rwandi', 'Kinyarwandi'],
sa: ["Sanskrit", "संस्कृतम्"], sa: ['Sanskrit', 'संस्कृतम्'],
sc: ["Sardinian", "Sardu"], sc: ['Sardinian', 'Sardu'],
sd: ["Sindhi", "सिनधि"], sd: ['Sindhi', 'सिनधि'],
se: ["Northern Sami", "Sámegiella"], se: ['Northern Sami', 'Sámegiella'],
sg: ["Sango", "Sängö"], sg: ['Sango', 'Sängö'],
sh: ["Serbo-Croatian", "Srpskohrvatski / Српскохрватски"], sh: ['Serbo-Croatian', 'Srpskohrvatski / Српскохрватски'],
si: ["Sinhalese", "සිංහල"], si: ['Sinhalese', 'සිංහල'],
sk: ["Slovak", "Slovenčina"], sk: ['Slovak', 'Slovenčina'],
sl: ["Slovenian", "Slovenščina"], sl: ['Slovenian', 'Slovenščina'],
sm: ["Samoan", "Gagana Samoa"], sm: ['Samoan', 'Gagana Samoa'],
sn: ["Shona", "chiShona"], sn: ['Shona', 'chiShona'],
so: ["Somalia", "Soomaaliga"], so: ['Somalia', 'Soomaaliga'],
sq: ["Albanian", "Shqip"], sq: ['Albanian', 'Shqip'],
sr: ["Serbian", "Српски"], sr: ['Serbian', 'Српски'],
ss: ["Swati", "SiSwati"], ss: ['Swati', 'SiSwati'],
st: ["Southern Sotho", "Sesotho"], st: ['Southern Sotho', 'Sesotho'],
su: ["Sundanese", "Basa Sunda"], su: ['Sundanese', 'Basa Sunda'],
sv: ["Swedish", "Svenska"], sv: ['Swedish', 'Svenska'],
sw: ["Swahili", "Kiswahili"], sw: ['Swahili', 'Kiswahili'],
ta: ["Tamil", "தமிழ்"], ta: ['Tamil', 'தமிழ்'],
te: ["Telugu", "తెలుగు"], te: ['Telugu', 'తెలుగు'],
tg: ["Tajik", "Тоҷикӣ"], tg: ['Tajik', 'Тоҷикӣ'],
th: ["Thai", "ไทย / Phasa Thai"], th: ['Thai', 'ไทย / Phasa Thai'],
ti: ["Tigrinya", "ትግርኛ"], ti: ['Tigrinya', 'ትግርኛ'],
tk: ["Turkmen", "Туркмен / تركمن"], tk: ['Turkmen', 'Туркмен / تركمن'],
tl: ["Tagalog / Filipino", "Tagalog"], tl: ['Tagalog / Filipino', 'Tagalog'],
tn: ["Tswana", "Setswana"], tn: ['Tswana', 'Setswana'],
to: ["Tonga", "Lea Faka-Tonga"], to: ['Tonga', 'Lea Faka-Tonga'],
tr: ["Turkish", "Türkçe"], tr: ['Turkish', 'Türkçe'],
ts: ["Tsonga", "Xitsonga"], ts: ['Tsonga', 'Xitsonga'],
tt: ["Tatar", "Tatarça"], tt: ['Tatar', 'Tatarça'],
tw: ["Twi", "Twi"], tw: ['Twi', 'Twi'],
ty: ["Tahitian", "Reo Mā`ohi"], ty: ['Tahitian', 'Reo Mā`ohi'],
ug: ["Uyghur", "Uyƣurqə / ئۇيغۇرچە"], ug: ['Uyghur', 'Uyƣurqə / ئۇيغۇرچە'],
uk: ["Ukrainian", "Українська"], uk: ['Ukrainian', 'Українська'],
ur: ["Urdu", "اردو"], ur: ['Urdu', 'اردو'],
uz: ["Uzbek", "Ўзбек"], uz: ['Uzbek', 'Ўзбек'],
ve: ["Venda", "Tshivenḓa"], ve: ['Venda', 'Tshivenḓa'],
vi: ["Vietnamese", "Tiếng Việt"], vi: ['Vietnamese', 'Tiếng Việt'],
vo: ["Volapük", "Volapük"], vo: ['Volapük', 'Volapük'],
wa: ["Walloon", "Walon"], wa: ['Walloon', 'Walon'],
wo: ["Wolof", "Wollof"], wo: ['Wolof', 'Wollof'],
xh: ["Xhosa", "isiXhosa"], xh: ['Xhosa', 'isiXhosa'],
yi: ["Yiddish", "ייִדיש"], yi: ['Yiddish', 'ייִדיש'],
yo: ["Yoruba", "Yorùbá"], yo: ['Yoruba', 'Yorùbá'],
za: ["Zhuang", "Cuengh / Tôô / 壮语"], za: ['Zhuang', 'Cuengh / Tôô / 壮语'],
zh: ["Chinese", "中文"], zh: ['Chinese', '中文'],
zu: ["Zulu", "isiZulu"], zu: ['Zulu', 'isiZulu'],
}; };
export default LANGUAGES; export default LANGUAGES;

View file

@ -1,15 +1,15 @@
export const CONFIRM_FILE_REMOVE = "confirmFileRemove"; export const CONFIRM_FILE_REMOVE = 'confirmFileRemove';
export const INCOMPATIBLE_DAEMON = "incompatibleDaemon"; export const INCOMPATIBLE_DAEMON = 'incompatibleDaemon';
export const FILE_TIMEOUT = "file_timeout"; export const FILE_TIMEOUT = 'file_timeout';
export const DOWNLOADING = "downloading"; export const DOWNLOADING = 'downloading';
export const ERROR = "error"; export const ERROR = 'error';
export const INSUFFICIENT_CREDITS = "insufficient_credits"; export const INSUFFICIENT_CREDITS = 'insufficient_credits';
export const UPGRADE = "upgrade"; export const UPGRADE = 'upgrade';
export const WELCOME = "welcome"; export const WELCOME = 'welcome';
export const EMAIL_COLLECTION = "email_collection"; export const EMAIL_COLLECTION = 'email_collection';
export const FIRST_REWARD = "first_reward"; export const FIRST_REWARD = 'first_reward';
export const AUTHENTICATION_FAILURE = "auth_failure"; export const AUTHENTICATION_FAILURE = 'auth_failure';
export const TRANSACTION_FAILED = "transaction_failed"; export const TRANSACTION_FAILED = 'transaction_failed';
export const REWARD_APPROVAL_REQUIRED = "reward_approval_required"; export const REWARD_APPROVAL_REQUIRED = 'reward_approval_required';
export const AFFIRM_PURCHASE = "affirm_purchase"; export const AFFIRM_PURCHASE = 'affirm_purchase';
export const CONFIRM_CLAIM_REVOKE = "confirmClaimRevoke"; export const CONFIRM_CLAIM_REVOKE = 'confirmClaimRevoke';

View file

@ -1,13 +1,13 @@
/* hardcoded names still exist for these in reducers/settings.js - only discovered when debugging */ /* hardcoded names still exist for these in reducers/settings.js - only discovered when debugging */
/* Many settings are stored in the localStorage by their name - /* Many SETTINGS are stored in the localStorage by their name -
be careful about changing the value of a settings constant, as doing so can invalidate existing settings */ be careful about changing the value of a SETTINGS constant, as doing so can invalidate existing SETTINGS */
export const CREDIT_REQUIRED_ACKNOWLEDGED = "credit_required_acknowledged"; export const CREDIT_REQUIRED_ACKNOWLEDGED = 'credit_required_acknowledged';
export const NEW_USER_ACKNOWLEDGED = "welcome_acknowledged"; export const NEW_USER_ACKNOWLEDGED = 'welcome_acknowledged';
export const EMAIL_COLLECTION_ACKNOWLEDGED = "email_collection_acknowledged"; export const EMAIL_COLLECTION_ACKNOWLEDGED = 'email_collection_acknowledged';
export const LANGUAGE = "language"; export const LANGUAGE = 'language';
export const SHOW_NSFW = "showNsfw"; export const SHOW_NSFW = 'showNsfw';
export const SHOW_UNAVAILABLE = "showUnavailable"; export const SHOW_UNAVAILABLE = 'showUnavailable';
export const INSTANT_PURCHASE_ENABLED = "instantPurchaseEnabled"; export const INSTANT_PURCHASE_ENABLED = 'instantPurchaseEnabled';
export const INSTANT_PURCHASE_MAX = "instantPurchaseMax"; export const INSTANT_PURCHASE_MAX = 'instantPurchaseMax';
export const THEME = "theme"; export const THEME = 'theme';
export const THEMES = "themes"; export const THEMES = 'themes';

View file

@ -1,3 +1,3 @@
export const NO_DEPOSITS = "no_deposits"; export const NO_DEPOSITS = 'no_deposits';
export const RECEIVED = "received"; export const RECEIVED = 'received';
export const COMPLETE = "complete"; export const COMPLETE = 'complete';

View file

@ -1 +1,2 @@
export const TIP = "tip"; // eslint-disable-next-line import/prefer-default-export
export const TIP = 'tip';

View file

@ -1,3 +1,5 @@
// @flow
declare module 'reselect' { declare module 'reselect' {
declare module.exports: any; declare module.exports: any;
} }

View file

@ -1,87 +1,71 @@
import React from "react"; /* eslint-disable react/jsx-filename-extension */
import ReactDOM from "react-dom"; import React from 'react';
import App from "component/app/index.js"; import ReactDOM from 'react-dom';
import SnackBar from "component/snackBar"; import App from 'component/app';
import { Provider } from "react-redux"; import SnackBar from 'component/snackBar';
import store from "store.js"; import { Provider } from 'react-redux';
import SplashScreen from "component/splash"; import store from 'store';
import { doDaemonReady } from "redux/actions/app"; import SplashScreen from 'component/splash';
import { doNavigate } from "redux/actions/navigation"; import { doDaemonReady } from 'redux/actions/app';
import { doDownloadLanguages } from "redux/actions/settings"; import { doNavigate } from 'redux/actions/navigation';
import * as types from "constants/action_types"; import { doDownloadLanguages } from 'redux/actions/settings';
import amplitude from "amplitude-js"; import * as ACTIONS from 'constants/action_types';
import lbry from "lbry"; import amplitude from 'amplitude-js';
import "scss/all.scss"; import lbry from 'lbry';
import 'scss/all.scss';
import { ipcRenderer, remote, shell } from 'electron';
const env = process.env.NODE_ENV || "production"; const { contextMenu } = remote.require('./main.js');
const { remote, ipcRenderer, shell } = require("electron");
const contextMenu = remote.require("./main.js").contextMenu; window.addEventListener('contextmenu', event => {
const app = require("./app");
// Workaround for https://github.com/electron-userland/electron-webpack/issues/52
if (process.env.NODE_ENV !== "development") {
window.staticResourcesPath = require("path")
.join(remote.app.getAppPath(), "../static")
.replace(/\\/g, "\\\\");
} else {
window.staticResourcesPath = "";
}
window.addEventListener("contextmenu", event => {
contextMenu.showContextMenu( contextMenu.showContextMenu(
remote.getCurrentWindow(), remote.getCurrentWindow(),
event.x, event.x,
event.y, event.y,
env === "development" app.env === 'development'
); );
event.preventDefault(); event.preventDefault();
}); });
ipcRenderer.on("open-uri-requested", (event, uri) => { ipcRenderer.on('open-uri-requested', (event, uri) => {
if (uri && uri.startsWith("lbry://")) { if (uri && uri.startsWith('lbry://')) {
app.store.dispatch(doNavigate("/show", { uri })); app.store.dispatch(doNavigate('/show', { uri }));
} }
}); });
ipcRenderer.on("open-menu", (event, uri) => { ipcRenderer.on('open-menu', (event, uri) => {
if (uri && uri.startsWith("/help")) { if (uri && uri.startsWith('/help')) {
app.store.dispatch(doNavigate("/help")); app.store.dispatch(doNavigate('/help'));
} }
}); });
const dock = remote.app.dock; const { dock } = remote.app;
ipcRenderer.on("window-is-focused", (event, data) => { ipcRenderer.on('window-is-focused', () => {
if (!dock) return; if (!dock) return;
app.store.dispatch({ type: types.WINDOW_FOCUSED }); app.store.dispatch({ type: ACTIONS.WINDOW_FOCUSED });
dock.setBadge(""); dock.setBadge('');
}); });
document.addEventListener("click", event => { document.addEventListener('click', event => {
let target = event.target; let { target } = event;
while (target && target !== document) { while (target && target !== document) {
if (target.matches("a") || target.matches("button")) { if (target.matches('a') || target.matches('button')) {
// TODO: Look into using accessiblity labels (this would also make the app more accessible) // TODO: Look into using accessiblity labels (this would also make the app more accessible)
const hrefParts = window.location.href.split("#"); const hrefParts = window.location.href.split('#');
const element = target.title || (target.text && target.text.trim()); const element = target.title || (target.text && target.text.trim());
if (element) { if (element) {
amplitude.getInstance().logEvent("CLICK", { amplitude.getInstance().logEvent('CLICK', {
target: element, target: element,
location: location: hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : '/',
hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : "/",
}); });
} else { } else {
amplitude.getInstance().logEvent("UNMARKED_CLICK", { amplitude.getInstance().logEvent('UNMARKED_CLICK', {
location: location: hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : '/',
hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : "/",
}); });
} }
} }
if ( if (target.matches('a[href^="http"]') || target.matches('a[href^="mailto"]')) {
target.matches('a[href^="http"]') ||
target.matches('a[href^="mailto"]')
) {
event.preventDefault(); event.preventDefault();
shell.openExternal(target.href); shell.openExternal(target.href);
return; return;
@ -90,20 +74,18 @@ document.addEventListener("click", event => {
} }
}); });
const initialState = app.store.getState(); const init = function initializeReactApp() {
const init = function() {
app.store.dispatch(doDownloadLanguages()); app.store.dispatch(doDownloadLanguages());
function onDaemonReady() { function onDaemonReady() {
lbry.status().then(info => { lbry.status().then(info => {
amplitude.getInstance().init( amplitude.getInstance().init(
// Amplitude API Key // Amplitude API Key
"0b130efdcbdbf86ec2f7f9eff354033e", '0b130efdcbdbf86ec2f7f9eff354033e',
info.lbry_id, info.lbry_id,
null, null,
() => { () => {
window.sessionStorage.setItem("loaded", "y"); // once we've made it here once per session, we don't need to show splash again 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()); app.store.dispatch(doDaemonReady());
ReactDOM.render( ReactDOM.render(
@ -113,21 +95,21 @@ const init = function() {
<SnackBar /> <SnackBar />
</div> </div>
</Provider>, </Provider>,
document.getElementById("app") document.getElementById('app')
); );
} }
); );
}); });
} }
if (window.sessionStorage.getItem("loaded") == "y") { if (window.sessionStorage.getItem('loaded') === 'y') {
onDaemonReady(); onDaemonReady();
} else { } else {
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>
<SplashScreen onReadyToLaunch={onDaemonReady} /> <SplashScreen onReadyToLaunch={onDaemonReady} />
</Provider>, </Provider>,
document.getElementById("app") document.getElementById('app')
); );
} }
}; };

View file

@ -1,76 +1,56 @@
const jsonrpc = {}; const jsonrpc = {};
jsonrpc.call = function( jsonrpc.call = function callJsonRpc(
connectionString, connectionString,
method, method,
params, params,
callback, callback,
errorCallback, errorCallback,
connectFailedCallback, connectFailedCallback
timeout
) { ) {
function checkAndParse(response) { function checkAndParse(response) {
if (response.status >= 200 && response.status < 300) { if (response.status >= 200 && response.status < 300) {
return response.json(); return response.json();
} }
return response return response.json().then(json => {
.json() let error;
.then(json => { if (json.error) {
let error; error = new Error(json.error);
if (json.error) { } else {
error = new Error(json.error); error = new Error('Protocol error with unknown response signature');
} else {
error = new Error("Protocol error with unknown response signature");
}
return Promise.reject(error);
})
.catch(e => {
console.error(e);
});
}
function makeRequest(url, options) {
return new Promise((resolve, reject) => {
fetch(url, options)
.then(resolve)
.catch(reject);
if (timeout) {
const e = new Error(__("Protocol request timed out"));
setTimeout(() => reject(e), timeout);
} }
return Promise.reject(error);
}); });
} }
const counter = parseInt(sessionStorage.getItem("JSONRPCCounter") || 0); const counter = parseInt(sessionStorage.getItem('JSONRPCCounter') || 0, 10);
const url = connectionString; const url = connectionString;
const options = { const options = {
method: "POST", method: 'POST',
body: JSON.stringify({ body: JSON.stringify({
jsonrpc: "2.0", jsonrpc: '2.0',
method, method,
params, params,
id: counter, id: counter,
}), }),
}; };
sessionStorage.setItem("JSONRPCCounter", counter + 1); sessionStorage.setItem('JSONRPCCounter', counter + 1);
return fetch(url, options) return fetch(url, options)
.then(checkAndParse) .then(checkAndParse)
.then(response => { .then(response => {
const error = const error = response.error || (response.result && response.result.error);
response.error || (response.result && response.result.error);
if (!error && typeof callback === "function") { if (!error && typeof callback === 'function') {
return callback(response.result); return callback(response.result);
} }
if (error && typeof errorCallback === "function") { if (error && typeof errorCallback === 'function') {
return errorCallback(error); return errorCallback(error);
} }
const errorEvent = new CustomEvent("unhandledError", { const errorEvent = new CustomEvent('unhandledError', {
detail: { detail: {
connectionString, connectionString,
method, method,
@ -81,22 +61,25 @@ jsonrpc.call = function(
}, },
}); });
document.dispatchEvent(errorEvent); document.dispatchEvent(errorEvent);
return Promise.resolve();
}) })
.catch(e => { .catch(error => {
if (connectFailedCallback) { if (connectFailedCallback) {
return connectFailedCallback(e); return connectFailedCallback(error);
} }
const errorEvent = new CustomEvent("unhandledError", { const errorEvent = new CustomEvent('unhandledError', {
detail: { detail: {
connectionString, connectionString,
method, method,
params, params,
code: e.response && e.response.status, code: error.response && error.response.status,
message: __("Connection to API server failed"), message: __('Connection to API server failed'),
}, },
}); });
document.dispatchEvent(errorEvent); document.dispatchEvent(errorEvent);
return Promise.resolve();
}); });
}; };

View file

@ -1,5 +1,31 @@
import jsonrpc from "./jsonrpc.js"; import jsonrpc from 'jsonrpc';
import lbryuri from "./lbryuri.js"; import { ipcRenderer } from 'electron';
const CHECK_DAEMON_STARTED_TRY_NUMBER = 200;
const Lbry = {
isConnected: false,
daemonConnectionString: 'http://localhost:5279',
pendingPublishTimeout: 20 * 60 * 1000,
};
function apiCall(method, params, resolve, reject) {
return jsonrpc.call(Lbry.daemonConnectionString, method, params, resolve, reject, reject);
}
const lbryProxy = new Proxy(Lbry, {
get(target, name) {
if (name in target) {
return target[name];
}
return function(params = {}) {
return new Promise((resolve, reject) => {
apiCall(name, params, resolve, reject);
});
};
},
});
function getLocal(key, fallback = undefined) { function getLocal(key, fallback = undefined) {
const itemRaw = localStorage.getItem(key); const itemRaw = localStorage.getItem(key);
@ -10,49 +36,24 @@ function setLocal(key, value) {
localStorage.setItem(key, JSON.stringify(value)); localStorage.setItem(key, JSON.stringify(value));
} }
const { remote, ipcRenderer } = require("electron");
let lbry = {
isConnected: false,
daemonConnectionString: "http://localhost:5279",
pendingPublishTimeout: 20 * 60 * 1000,
};
function apiCall(method, params, resolve, reject) {
return jsonrpc.call(
lbry.daemonConnectionString,
method,
params,
resolve,
reject,
reject
);
}
/** /**
* Records a publish attempt in local storage. Returns a dictionary with all the data needed to * Records a publish attempt in local storage. Returns a dictionary with all the data needed to
* needed to make a dummy claim or file info object. * needed to make a dummy claim or file info object.
*/ */
let pendingId = 0; let pendingId = 0;
function savePendingPublish({ name, channel_name }) { function savePendingPublish({ name, channelName }) {
let uri; pendingId += 1;
if (channel_name) { const pendingPublishes = getLocal('pendingPublishes') || [];
uri = lbryuri.build({ name: channel_name, path: name }, false);
} else {
uri = lbryuri.build({ name }, false);
}
++pendingId;
const pendingPublishes = getLocal("pendingPublishes") || [];
const newPendingPublish = { const newPendingPublish = {
name, name,
channel_name, channelName,
claim_id: `pending-${pendingId}`, claim_id: `pending-${pendingId}`,
txid: `pending-${pendingId}`, txid: `pending-${pendingId}`,
nout: 0, nout: 0,
outpoint: `pending-${pendingId}:0`, outpoint: `pending-${pendingId}:0`,
time: Date.now(), time: Date.now(),
}; };
setLocal("pendingPublishes", [...pendingPublishes, newPendingPublish]); setLocal('pendingPublishes', [...pendingPublishes, newPendingPublish]);
return newPendingPublish; return newPendingPublish;
} }
@ -60,31 +61,27 @@ function savePendingPublish({ name, channel_name }) {
* If there is a pending publish with the given name or outpoint, remove it. * If there is a pending publish with the given name or outpoint, remove it.
* A channel name may also be provided along with name. * A channel name may also be provided along with name.
*/ */
function removePendingPublishIfNeeded({ name, channel_name, outpoint }) { function removePendingPublishIfNeeded({ name, channelName, outpoint }) {
function pubMatches(pub) { function pubMatches(pub) {
return ( return (
pub.outpoint === outpoint || pub.outpoint === outpoint ||
(pub.name === name && (pub.name === name && (!channelName || pub.channel_name === channelName))
(!channel_name || pub.channel_name === channel_name))
); );
} }
setLocal( setLocal('pendingPublishes', Lbry.getPendingPublishes().filter(pub => !pubMatches(pub)));
"pendingPublishes",
lbry.getPendingPublishes().filter(pub => !pubMatches(pub))
);
} }
/** /**
* Gets the current list of pending publish attempts. Filters out any that have timed out and * Gets the current list of pending publish attempts. Filters out any that have timed out and
* removes them from the list. * removes them from the list.
*/ */
lbry.getPendingPublishes = function() { Lbry.getPendingPublishes = function() {
const pendingPublishes = getLocal("pendingPublishes") || []; const pendingPublishes = getLocal('pendingPublishes') || [];
const newPendingPublishes = pendingPublishes.filter( const newPendingPublishes = pendingPublishes.filter(
pub => Date.now() - pub.time <= lbry.pendingPublishTimeout pub => Date.now() - pub.time <= Lbry.pendingPublishTimeout
); );
setLocal("pendingPublishes", newPendingPublishes); setLocal('pendingPublishes', newPendingPublishes);
return newPendingPublishes; return newPendingPublishes;
}; };
@ -92,65 +89,52 @@ lbry.getPendingPublishes = function() {
* Gets a pending publish attempt by its name or (fake) outpoint. A channel name can also be * Gets a pending publish attempt by its name or (fake) outpoint. A channel name can also be
* provided along withe the name. If no pending publish is found, returns null. * provided along withe the name. If no pending publish is found, returns null.
*/ */
function getPendingPublish({ name, channel_name, outpoint }) { function getPendingPublish({ name, channelName, outpoint }) {
const pendingPublishes = lbry.getPendingPublishes(); const pendingPublishes = Lbry.getPendingPublishes();
return ( return (
pendingPublishes.find( pendingPublishes.find(
pub => pub =>
pub.outpoint === outpoint || pub.outpoint === outpoint ||
(pub.name === name && (pub.name === name && (!channelName || pub.channel_name === channelName))
(!channel_name || pub.channel_name === channel_name))
) || null ) || null
); );
} }
function pendingPublishToDummyClaim({ function pendingPublishToDummyClaim({ channelName, name, outpoint, claimId, txid, nout }) {
channel_name, return { name, outpoint, claimId, txid, nout, channelName };
name,
outpoint,
claim_id,
txid,
nout,
}) {
return { name, outpoint, claim_id, txid, nout, channel_name };
} }
function pendingPublishToDummyFileInfo({ name, outpoint, claim_id }) { function pendingPublishToDummyFileInfo({ name, outpoint, claimId }) {
return { name, outpoint, claim_id, metadata: null }; return { name, outpoint, claimId, metadata: null };
} }
// core // core
lbry._connectPromise = null; Lbry.connectPromise = null;
lbry.connect = function() { Lbry.connect = function() {
if (lbry._connectPromise === null) { if (Lbry.connectPromise === null) {
lbry._connectPromise = new Promise((resolve, reject) => { Lbry.connectPromise = new Promise((resolve, reject) => {
let tryNum = 0; let tryNum = 0;
function checkDaemonStartedFailed() {
if (tryNum <= 200) {
// Move # of tries into constant or config option
setTimeout(() => {
tryNum++;
checkDaemonStarted();
}, tryNum < 50 ? 400 : 1000);
} else {
reject(new Error("Unable to connect to LBRY"));
}
}
// Check every half second to see if the daemon is accepting connections // Check every half second to see if the daemon is accepting connections
function checkDaemonStarted() { function checkDaemonStarted() {
lbry tryNum += 1;
lbryProxy
.status() .status()
.then(resolve) .then(resolve)
.catch(checkDaemonStartedFailed); .catch(() => {
if (tryNum <= CHECK_DAEMON_STARTED_TRY_NUMBER) {
setTimeout(checkDaemonStarted, tryNum < 50 ? 400 : 1000);
} else {
reject(new Error('Unable to connect to LBRY'));
}
});
} }
checkDaemonStarted(); checkDaemonStarted();
}); });
} }
return lbry._connectPromise; return Lbry.connectPromise;
}; };
/** /**
@ -160,13 +144,24 @@ lbry.connect = function() {
* This currently includes a work-around to cache the file in local storage so that the pending * This currently includes a work-around to cache the file in local storage so that the pending
* publish can appear in the UI immediately. * publish can appear in the UI immediately.
*/ */
lbry.publishDeprecated = function( Lbry.publishDeprecated = function(params, fileListedCallback, publishedCallback, errorCallback) {
params, // Give a short grace period in case publish() returns right away or (more likely) gives an error
fileListedCallback, const returnPendingTimeout = setTimeout(
publishedCallback, () => {
errorCallback const { name, channel_name: channelName } = params;
) { if (publishedCallback || fileListedCallback) {
lbry.publish(params).then( savePendingPublish({
name,
channelName,
});
publishedCallback(true);
}
},
2000,
{ once: true }
);
lbryProxy.publish(params).then(
result => { result => {
if (returnPendingTimeout) clearTimeout(returnPendingTimeout); if (returnPendingTimeout) clearTimeout(returnPendingTimeout);
publishedCallback(result); publishedCallback(result);
@ -176,66 +171,40 @@ lbry.publishDeprecated = function(
errorCallback(err); errorCallback(err);
} }
); );
// Give a short grace period in case publish() returns right away or (more likely) gives an error
const returnPendingTimeout = setTimeout(
() => {
if (publishedCallback) {
savePendingPublish({
name: params.name,
channel_name: params.channel_name,
});
publishedCallback(true);
}
if (fileListedCallback) {
const { name, channel_name } = params;
savePendingPublish({
name: params.name,
channel_name: params.channel_name,
});
fileListedCallback(true);
}
},
2000,
{ once: true }
);
}; };
lbry.imagePath = function(file) { Lbry.imagePath = function(file) {
return `${staticResourcesPath}/img/${file}`; return `${staticResourcesPath}/img/${file}`;
}; };
lbry.getMediaType = function(contentType, fileName) { Lbry.getMediaType = function(contentType, fileName) {
if (contentType) { if (contentType) {
return /^[^/]+/.exec(contentType)[0]; return /^[^/]+/.exec(contentType)[0];
} else if (fileName) { } else if (fileName) {
const dotIndex = fileName.lastIndexOf("."); const dotIndex = fileName.lastIndexOf('.');
if (dotIndex == -1) { if (dotIndex === -1) {
return "unknown"; return 'unknown';
} }
const ext = fileName.substr(dotIndex + 1); const ext = fileName.substr(dotIndex + 1);
if (/^mp4|m4v|webm|flv|f4v|ogv$/i.test(ext)) { if (/^mp4|m4v|webm|flv|f4v|ogv$/i.test(ext)) {
return "video"; return 'video';
} else if (/^mp3|m4a|aac|wav|flac|ogg|opus$/i.test(ext)) { } else if (/^mp3|m4a|aac|wav|flac|ogg|opus$/i.test(ext)) {
return "audio"; return 'audio';
} else if ( } else if (/^html|htm|xml|pdf|odf|doc|docx|md|markdown|txt|epub|org$/i.test(ext)) {
/^html|htm|xml|pdf|odf|doc|docx|md|markdown|txt|epub|org$/i.test(ext) return 'document';
) {
return "document";
} }
return "unknown"; return 'unknown';
} }
return "unknown"; return 'unknown';
}; };
lbry.getAppVersionInfo = function() { Lbry.getAppVersionInfo = function() {
return new Promise((resolve, reject) => { return new Promise(resolve => {
ipcRenderer.once("version-info-received", (event, versionInfo) => { ipcRenderer.once('version-info-received', (event, versionInfo) => {
resolve(versionInfo); resolve(versionInfo);
}); });
ipcRenderer.send("version-info-requested"); ipcRenderer.send('version-info-requested');
}); });
}; };
@ -248,9 +217,9 @@ lbry.getAppVersionInfo = function() {
* Returns results from the file_list API method, plus dummy entries for pending publishes. * Returns results from the file_list API method, plus dummy entries for pending publishes.
* (If a real publish with the same name is found, the pending publish will be ignored and removed.) * (If a real publish with the same name is found, the pending publish will be ignored and removed.)
*/ */
lbry.file_list = function(params = {}) { Lbry.file_list = function(params = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const { name, channel_name, outpoint } = params; const { name, channel_name: channelName, outpoint } = params;
/** /**
* If we're searching by outpoint, check first to see if there's a matching pending publish. * If we're searching by outpoint, check first to see if there's a matching pending publish.
@ -266,16 +235,14 @@ lbry.file_list = function(params = {}) {
} }
apiCall( apiCall(
"file_list", 'file_list',
params, params,
fileInfos => { fileInfos => {
removePendingPublishIfNeeded({ name, channel_name, outpoint }); removePendingPublishIfNeeded({ name, channelName, outpoint });
// if a naked file_list call, append the pending file infos // if a naked file_list call, append the pending file infos
if (!name && !channel_name && !outpoint) { if (!name && !channelName && !outpoint) {
const dummyFileInfos = lbry const dummyFileInfos = Lbry.getPendingPublishes().map(pendingPublishToDummyFileInfo);
.getPendingPublishes()
.map(pendingPublishToDummyFileInfo);
resolve([...fileInfos, ...dummyFileInfos]); resolve([...fileInfos, ...dummyFileInfos]);
} else { } else {
@ -287,23 +254,21 @@ lbry.file_list = function(params = {}) {
}); });
}; };
lbry.claim_list_mine = function(params = {}) { Lbry.claim_list_mine = function(params = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
apiCall( apiCall(
"claim_list_mine", 'claim_list_mine',
params, params,
claims => { claims => {
for (const { name, channel_name, txid, nout } of claims) { claims.forEach(({ name, channel_name: channelName, txid, nout }) => {
removePendingPublishIfNeeded({ removePendingPublishIfNeeded({
name, name,
channel_name, channelName,
outpoint: `${txid}:${nout}`, outpoint: `${txid}:${nout}`,
}); });
} });
const dummyClaims = lbry const dummyClaims = Lbry.getPendingPublishes().map(pendingPublishToDummyClaim);
.getPendingPublishes()
.map(pendingPublishToDummyClaim);
resolve([...claims, ...dummyClaims]); resolve([...claims, ...dummyClaims]);
}, },
reject reject
@ -311,13 +276,13 @@ lbry.claim_list_mine = function(params = {}) {
}); });
}; };
lbry.resolve = function(params = {}) { Lbry.resolve = function(params = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
apiCall( apiCall(
"resolve", 'resolve',
params, params,
data => { data => {
if ("uri" in params) { if ('uri' in params) {
// If only a single URI was requested, don't nest the results in an object // If only a single URI was requested, don't nest the results in an object
resolve(data && data[params.uri] ? data[params.uri] : {}); resolve(data && data[params.uri] ? data[params.uri] : {});
} else { } else {
@ -329,18 +294,4 @@ lbry.resolve = function(params = {}) {
}); });
}; };
lbry = new Proxy(lbry, { export default lbryProxy;
get(target, name) {
if (name in target) {
return target[name];
}
return function(params = {}) {
return new Promise((resolve, reject) => {
apiCall(name, params, resolve, reject);
});
};
},
});
export default lbry;

View file

@ -1,48 +1,46 @@
import lbry from "./lbry.js"; import Lbry from 'lbry';
import querystring from 'querystring';
import { ipcRenderer } from 'electron';
const querystring = require("querystring"); const Lbryio = {
const { ipcRenderer } = require("electron");
const lbryio = {
enabled: true, enabled: true,
_authenticationPromise: null, authenticationPromise: null,
_exchangePromise: null, exchangePromise: null,
_exchangeLastFetched: null, exchangeLastFetched: null,
}; };
const CONNECTION_STRING = process.env.LBRY_APP_API_URL const CONNECTION_STRING = process.env.LBRY_APP_API_URL
? process.env.LBRY_APP_API_URL.replace(/\/*$/, "/") // exactly one slash at the end ? process.env.LBRY_APP_API_URL.replace(/\/*$/, '/') // exactly one slash at the end
: "https://api.lbry.io/"; : 'https://api.lbry.io/';
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000; const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
lbryio.getExchangeRates = function() { Lbryio.getExchangeRates = function() {
if ( if (
!lbryio._exchangeLastFetched || !Lbryio.exchangeLastFetched ||
Date.now() - lbryio._exchangeLastFetched > EXCHANGE_RATE_TIMEOUT Date.now() - Lbryio.exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
) { ) {
lbryio._exchangePromise = new Promise((resolve, reject) => { Lbryio.exchangePromise = new Promise((resolve, reject) => {
lbryio Lbryio.call('lbc', 'exchange_rate', {}, 'get', true)
.call("lbc", "exchange_rate", {}, "get", true) .then(({ lbc_usd: LBC_USD, lbc_btc: LBC_BTC, btc_usd: BTC_USD }) => {
.then(({ lbc_usd, lbc_btc, btc_usd }) => { const rates = { LBC_USD, LBC_BTC, BTC_USD };
const rates = { lbc_usd, lbc_btc, btc_usd };
resolve(rates); resolve(rates);
}) })
.catch(reject); .catch(reject);
}); });
lbryio._exchangeLastFetched = Date.now(); Lbryio.exchangeLastFetched = Date.now();
} }
return lbryio._exchangePromise; return Lbryio.exchangePromise;
}; };
lbryio.call = function(resource, action, params = {}, method = "get") { Lbryio.call = function(resource, action, params = {}, method = 'get') {
if (!lbryio.enabled) { if (!Lbryio.enabled) {
console.log(__("Internal API disabled")); console.log(__('Internal API disabled'));
return Promise.reject(new Error(__("LBRY internal API is disabled"))); return Promise.reject(new Error(__('LBRY internal API is disabled')));
} }
if (!(method == "get" || method == "post")) { if (!(method === 'get' || method === 'post')) {
return Promise.reject(new Error(__("Invalid method"))); return Promise.reject(new Error(__('Invalid method')));
} }
function checkAndParse(response) { function checkAndParse(response) {
@ -54,7 +52,7 @@ lbryio.call = function(resource, action, params = {}, method = "get") {
if (json.error) { if (json.error) {
error = new Error(json.error); error = new Error(json.error);
} else { } else {
error = new Error("Unknown API error signature"); error = new Error('Unknown API error signature');
} }
error.response = response; // this is primarily a hack used in actions/user.js error.response = response; // this is primarily a hack used in actions/user.js
return Promise.reject(error); return Promise.reject(error);
@ -65,20 +63,20 @@ lbryio.call = function(resource, action, params = {}, method = "get") {
return fetch(url, options).then(checkAndParse); return fetch(url, options).then(checkAndParse);
} }
return lbryio.getAuthToken().then(token => { return Lbryio.getAuthToken().then(token => {
const fullParams = { auth_token: token, ...params }; const fullParams = { auth_token: token, ...params };
const qs = querystring.stringify(fullParams); const qs = querystring.stringify(fullParams);
let url = `${CONNECTION_STRING}${resource}/${action}?${qs}`; let url = `${CONNECTION_STRING}${resource}/${action}?${qs}`;
let options = { let options = {
method: "GET", method: 'GET',
}; };
if (method == "post") { if (method === 'post') {
options = { options = {
method: "POST", method: 'POST',
headers: { headers: {
"Content-Type": "application/x-www-form-urlencoded", 'Content-Type': 'application/x-www-form-urlencoded',
}, },
body: qs, body: qs,
}; };
@ -89,35 +87,35 @@ lbryio.call = function(resource, action, params = {}, method = "get") {
}); });
}; };
lbryio._authToken = null; Lbryio.authToken = null;
lbryio.getAuthToken = () => Lbryio.getAuthToken = () =>
new Promise((resolve, reject) => { new Promise(resolve => {
if (lbryio._authToken) { if (Lbryio.authToken) {
resolve(lbryio._authToken); resolve(Lbryio.authToken);
} else { } else {
ipcRenderer.once("auth-token-response", (event, token) => { ipcRenderer.once('auth-token-response', (event, token) => {
lbryio._authToken = token; Lbryio.authToken = token;
return resolve(token); return resolve(token);
}); });
ipcRenderer.send("get-auth-token"); ipcRenderer.send('get-auth-token');
} }
}); });
lbryio.setAuthToken = token => { Lbryio.setAuthToken = token => {
lbryio._authToken = token ? token.toString().trim() : null; Lbryio.authToken = token ? token.toString().trim() : null;
ipcRenderer.send("set-auth-token", token); ipcRenderer.send('set-auth-token', token);
}; };
lbryio.getCurrentUser = () => lbryio.call("user", "me"); Lbryio.getCurrentUser = () => Lbryio.call('user', 'me');
lbryio.authenticate = function() { Lbryio.authenticate = function() {
if (!lbryio.enabled) { if (!Lbryio.enabled) {
return new Promise((resolve, reject) => { return new Promise(resolve => {
resolve({ resolve({
id: 1, id: 1,
language: "en", language: 'en',
primary_email: "disabled@lbry.io", primary_email: 'disabled@lbry.io',
has_verified_email: true, has_verified_email: true,
is_identity_verified: true, is_identity_verified: true,
is_reward_approved: false, is_reward_approved: false,
@ -125,58 +123,55 @@ lbryio.authenticate = function() {
}); });
} }
if (lbryio._authenticationPromise === null) { if (Lbryio.authenticationPromise === null) {
lbryio._authenticationPromise = new Promise((resolve, reject) => { Lbryio.authenticationPromise = new Promise((resolve, reject) => {
lbryio Lbryio.getAuthToken()
.getAuthToken()
.then(token => { .then(token => {
if (!token || token.length > 60) { if (!token || token.length > 60) {
return false; return false;
} }
// check that token works // check that token works
return lbryio return Lbryio.getCurrentUser()
.getCurrentUser()
.then(() => true) .then(() => true)
.catch(() => false); .catch(() => false);
}) })
.then(isTokenValid => { .then(isTokenValid => {
if (isTokenValid) { if (isTokenValid) {
return; return reject;
} }
return lbry return Lbry.status()
.status()
.then(status => .then(status =>
lbryio.call( Lbryio.call(
"user", 'user',
"new", 'new',
{ {
auth_token: "", auth_token: '',
language: "en", language: 'en',
app_id: status.installation_id, app_id: status.installation_id,
}, },
"post" 'post'
) )
) )
.then(response => { .then(response => {
if (!response.auth_token) { if (!response.auth_token) {
throw new Error(__("auth_token is missing from response")); throw new Error(__('auth_token is missing from response'));
} }
return lbryio.setAuthToken(response.auth_token); return Lbryio.setAuthToken(response.auth_token);
}); });
}) })
.then(lbryio.getCurrentUser) .then(Lbryio.getCurrentUser)
.then(resolve, reject); .then(resolve, reject);
}); });
} }
return lbryio._authenticationPromise; return Lbryio.authenticationPromise;
}; };
lbryio.getStripeToken = () => Lbryio.getStripeToken = () =>
CONNECTION_STRING.startsWith("http://localhost:") CONNECTION_STRING.startsWith('http://localhost:')
? "pk_test_NoL1JWL7i1ipfhVId5KfDZgo" ? 'pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
: "pk_live_e8M4dRNnCCbmpZzduEUZBgJO"; : 'pk_live_e8M4dRNnCCbmpZzduEUZBgJO';
export default lbryio; export default Lbryio;

View file

@ -1,10 +1,10 @@
const CHANNEL_NAME_MIN_LEN = 1; const CHANNEL_NAME_MIN_LEN = 1;
const CLAIM_ID_MAX_LEN = 40; const CLAIM_ID_MAX_LEN = 40;
const lbryuri = {}; const Lbryuri = {};
lbryuri.REGEXP_INVALID_URI = /[^A-Za-z0-9-]/g; Lbryuri.REGEXP_INVALID_URI = /[^A-Za-z0-9-]/g;
lbryuri.REGEXP_ADDRESS = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/; Lbryuri.REGEXP_ADDRESS = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
/** /**
* Parses a LBRY name into its component parts. Throws errors with user-friendly * Parses a LBRY name into its component parts. Throws errors with user-friendly
@ -28,13 +28,13 @@ lbryuri.REGEXP_ADDRESS = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
* - contentName (string): For anon claims, the name; for channel claims, the path * - contentName (string): For anon claims, the name; for channel claims, the path
* - channelName (string, if present): Channel name without @ * - channelName (string, if present): Channel name without @
*/ */
lbryuri.parse = function(uri, requireProto = false) { Lbryuri.parse = function(uri, requireProto = false) {
// Break into components. Empty sub-matches are converted to null // Break into components. Empty sub-matches are converted to null
const componentsRegex = new RegExp( const componentsRegex = new RegExp(
"^((?:lbry://)?)" + // protocol '^((?:lbry://)?)' + // protocol
"([^:$#/]*)" + // name (stops at the first separator or end) '([^:$#/]*)' + // name (stops at the first separator or end)
"([:$#]?)([^/]*)" + // modifier separator, modifier (stops at the first path separator or end) '([:$#]?)([^/]*)' + // modifier separator, modifier (stops at the first path separator or end)
"(/?)(.*)" // path separator, path '(/?)(.*)' // path separator, path
); );
const [proto, name, modSep, modVal, pathSep, path] = componentsRegex const [proto, name, modSep, modVal, pathSep, path] = componentsRegex
.exec(uri) .exec(uri)
@ -45,57 +45,54 @@ lbryuri.parse = function(uri, requireProto = false) {
// Validate protocol // Validate protocol
if (requireProto && !proto) { if (requireProto && !proto) {
throw new Error(__("LBRY URIs must include a protocol prefix (lbry://).")); throw new Error(__('LBRY URIs must include a protocol prefix (lbry://).'));
} }
// Validate and process name // Validate and process name
if (!name) { if (!name) {
throw new Error(__("URI does not include name.")); throw new Error(__('URI does not include name.'));
} }
const isChannel = name.startsWith("@"); const isChannel = name.startsWith('@');
const channelName = isChannel ? name.slice(1) : name; const channelName = isChannel ? name.slice(1) : name;
if (isChannel) { if (isChannel) {
if (!channelName) { if (!channelName) {
throw new Error(__("No channel name after @.")); throw new Error(__('No channel name after @.'));
} }
if (channelName.length < CHANNEL_NAME_MIN_LEN) { if (channelName.length < CHANNEL_NAME_MIN_LEN) {
throw new Error( throw new Error(__(`Channel names must be at least %s characters.`, CHANNEL_NAME_MIN_LEN));
__(
`Channel names must be at least %s characters.`,
CHANNEL_NAME_MIN_LEN
)
);
} }
contentName = path; contentName = path;
} }
const nameBadChars = (channelName || name).match(lbryuri.REGEXP_INVALID_URI); const nameBadChars = (channelName || name).match(Lbryuri.REGEXP_INVALID_URI);
if (nameBadChars) { if (nameBadChars) {
throw new Error( throw new Error(
__( __(
`Invalid character %s in name: %s.`, `Invalid character %s in name: %s.`,
nameBadChars.length == 1 ? "" : "s", nameBadChars.length === 1 ? '' : 's',
nameBadChars.join(", ") nameBadChars.join(', ')
) )
); );
} }
// Validate and process modifier (claim ID, bid position or claim sequence) // Validate and process modifier (claim ID, bid position or claim sequence)
let claimId, claimSequence, bidPosition; let claimId;
let claimSequence;
let bidPosition;
if (modSep) { if (modSep) {
if (!modVal) { if (!modVal) {
throw new Error(__(`No modifier provided after separator %s.`, modSep)); throw new Error(__(`No modifier provided after separator %s.`, modSep));
} }
if (modSep == "#") { if (modSep === '#') {
claimId = modVal; claimId = modVal;
} else if (modSep == ":") { } else if (modSep === ':') {
claimSequence = modVal; claimSequence = modVal;
} else if (modSep == "$") { } else if (modSep === '$') {
bidPosition = modVal; bidPosition = modVal;
} }
} }
@ -109,29 +106,27 @@ lbryuri.parse = function(uri, requireProto = false) {
} }
if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) { if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) {
throw new Error(__("Claim sequence must be a number.")); throw new Error(__('Claim sequence must be a number.'));
} }
if (bidPosition && !bidPosition.match(/^-?[1-9][0-9]*$/)) { if (bidPosition && !bidPosition.match(/^-?[1-9][0-9]*$/)) {
throw new Error(__("Bid position must be a number.")); throw new Error(__('Bid position must be a number.'));
} }
// Validate and process path // Validate and process path
if (path) { if (path) {
if (!isChannel) { if (!isChannel) {
throw new Error(__("Only channel URIs may have a path.")); throw new Error(__('Only channel URIs may have a path.'));
} }
const pathBadChars = path.match(lbryuri.REGEXP_INVALID_URI); const pathBadChars = path.match(Lbryuri.REGEXP_INVALID_URI);
if (pathBadChars) { if (pathBadChars) {
throw new Error( throw new Error(__(`Invalid character in path: %s`, pathBadChars.join(', ')));
__(`Invalid character in path: %s`, pathBadChars.join(", "))
);
} }
contentName = path; contentName = path;
} else if (pathSep) { } else if (pathSep) {
throw new Error(__("No path provided after /")); throw new Error(__('No path provided after /'));
} }
return { return {
@ -140,8 +135,8 @@ lbryuri.parse = function(uri, requireProto = false) {
isChannel, isChannel,
...(contentName ? { contentName } : {}), ...(contentName ? { contentName } : {}),
...(channelName ? { channelName } : {}), ...(channelName ? { channelName } : {}),
...(claimSequence ? { claimSequence: parseInt(claimSequence) } : {}), ...(claimSequence ? { claimSequence: parseInt(claimSequence, 10) } : {}),
...(bidPosition ? { bidPosition: parseInt(bidPosition) } : {}), ...(bidPosition ? { bidPosition: parseInt(bidPosition, 10) } : {}),
...(claimId ? { claimId } : {}), ...(claimId ? { claimId } : {}),
...(path ? { path } : {}), ...(path ? { path } : {}),
}; };
@ -152,21 +147,13 @@ lbryuri.parse = function(uri, requireProto = false) {
* *
* The channelName key will accept names with or without the @ prefix. * The channelName key will accept names with or without the @ prefix.
*/ */
lbryuri.build = function(uriObj, includeProto = true, allowExtraProps = false) { Lbryuri.build = function(uriObj, includeProto = true) {
let { const { claimId, claimSequence, bidPosition, contentName, channelName } = uriObj;
name,
claimId, let { name, path } = uriObj;
claimSequence,
bidPosition,
path,
contentName,
channelName,
} = uriObj;
if (channelName) { if (channelName) {
const channelNameFormatted = channelName.startsWith("@") const channelNameFormatted = channelName.startsWith('@') ? channelName : `@${channelName}`;
? channelName
: `@${channelName}`;
if (!name) { if (!name) {
name = channelNameFormatted; name = channelNameFormatted;
} else if (name !== channelNameFormatted) { } else if (name !== channelNameFormatted) {
@ -187,52 +174,50 @@ lbryuri.build = function(uriObj, includeProto = true, allowExtraProps = false) {
if (path && path !== contentName) { if (path && path !== contentName) {
throw new Error( throw new Error(
__( __(
"Path and contentName do not match. Only one is required; most likely you wanted contentName." 'Path and contentName do not match. Only one is required; most likely you wanted contentName.'
) )
); );
} }
} }
return ( return (
(includeProto ? "lbry://" : "") + (includeProto ? 'lbry://' : '') +
name + name +
(claimId ? `#${claimId}` : "") + (claimId ? `#${claimId}` : '') +
(claimSequence ? `:${claimSequence}` : "") + (claimSequence ? `:${claimSequence}` : '') +
(bidPosition ? `\$${bidPosition}` : "") + (bidPosition ? `${bidPosition}` : '') +
(path ? `/${path}` : "") (path ? `/${path}` : '')
); );
}; };
/* Takes a parseable LBRY URI and converts it to standard, canonical format (currently this just /* Takes a parseable LBRY URI and converts it to standard, canonical format (currently this just
* consists of adding the lbry:// prefix if needed) */ * consists of adding the lbry:// prefix if needed) */
lbryuri.normalize = function(uri) { Lbryuri.normalize = function(uri) {
if (uri.match(/pending_claim/)) return uri; if (uri.match(/pending_claim/)) return uri;
const { name, path, bidPosition, claimSequence, claimId } = lbryuri.parse( const { name, path, bidPosition, claimSequence, claimId } = Lbryuri.parse(uri);
uri return Lbryuri.build({ name, path, claimSequence, bidPosition, claimId });
);
return lbryuri.build({ name, path, claimSequence, bidPosition, claimId });
}; };
lbryuri.isValid = function(uri) { Lbryuri.isValid = function(uri) {
let parts; let parts;
try { try {
parts = lbryuri.parse(lbryuri.normalize(uri)); parts = Lbryuri.parse(Lbryuri.normalize(uri));
} catch (error) { } catch (error) {
return false; return false;
} }
return parts && parts.name; return parts && parts.name;
}; };
lbryuri.isValidName = function(name, checkCase = true) { Lbryuri.isValidName = function(name, checkCase = true) {
const regexp = new RegExp("^[a-z0-9-]+$", checkCase ? "" : "i"); const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i');
return regexp.test(name); return regexp.test(name);
}; };
lbryuri.isClaimable = function(uri) { Lbryuri.isClaimable = function(uri) {
let parts; let parts;
try { try {
parts = lbryuri.parse(lbryuri.normalize(uri)); parts = Lbryuri.parse(Lbryuri.normalize(uri));
} catch (error) { } catch (error) {
return false; return false;
} }
@ -247,5 +232,5 @@ lbryuri.isClaimable = function(uri) {
); );
}; };
window.lbryuri = lbryuri; window.lbryuri = Lbryuri;
export default lbryuri; export default Lbryuri;

View file

@ -1,5 +1,5 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import lbry from "lbry"; import Lbry from 'lbry';
import { import {
selectUpdateUrl, selectUpdateUrl,
selectUpgradeDownloadPath, selectUpgradeDownloadPath,
@ -7,27 +7,26 @@ import {
selectUpgradeFilename, selectUpgradeFilename,
selectIsUpgradeSkipped, selectIsUpgradeSkipped,
selectRemoteVersion, selectRemoteVersion,
} from "redux/selectors/app"; selectCurrentModal,
import { doFetchDaemonSettings } from "redux/actions/settings"; } from 'redux/selectors/app';
import { doBalanceSubscribe, doFetchTransactions } from "redux/actions/wallet"; import { doFetchDaemonSettings } from 'redux/actions/settings';
import { doAuthenticate } from "redux/actions/user"; import { doBalanceSubscribe, doFetchTransactions } from 'redux/actions/wallet';
import { doFetchFileInfosAndPublishedClaims } from "redux/actions/file_info"; import { doAuthenticate } from 'redux/actions/user';
import * as modals from "constants/modal_types"; import { doFetchFileInfosAndPublishedClaims } from 'redux/actions/file_info';
import { doFetchRewardedContent } from "redux/actions/content"; import * as MODALS from 'constants/modal_types';
import { selectCurrentModal } from "redux/selectors/app"; import { doFetchRewardedContent } from 'redux/actions/content';
import { ipcRenderer, remote } from 'electron';
import Path from 'path';
const { remote, ipcRenderer, shell } = require("electron"); const { download } = remote.require('electron-dl');
const path = require("path"); const Fs = remote.require('fs');
const { lbrySettings: config } = require('package.json');
const { download } = remote.require("electron-dl");
const fs = remote.require("fs");
const { lbrySettings: config } = require("package.json");
const CHECK_UPGRADE_INTERVAL = 10 * 60 * 1000; const CHECK_UPGRADE_INTERVAL = 10 * 60 * 1000;
export function doOpenModal(modal, modalProps = {}) { export function doOpenModal(modal, modalProps = {}) {
return { return {
type: types.OPEN_MODAL, type: ACTIONS.OPEN_MODAL,
data: { data: {
modal, modal,
modalProps, modalProps,
@ -37,13 +36,13 @@ export function doOpenModal(modal, modalProps = {}) {
export function doCloseModal() { export function doCloseModal() {
return { return {
type: types.CLOSE_MODAL, type: ACTIONS.CLOSE_MODAL,
}; };
} }
export function doUpdateDownloadProgress(percent) { export function doUpdateDownloadProgress(percent) {
return { return {
type: types.UPGRADE_DOWNLOAD_PROGRESSED, type: ACTIONS.UPGRADE_DOWNLOAD_PROGRESSED,
data: { data: {
percent, percent,
}, },
@ -52,7 +51,7 @@ export function doUpdateDownloadProgress(percent) {
export function doSkipUpgrade() { export function doSkipUpgrade() {
return { return {
type: types.SKIP_UPGRADE, type: ACTIONS.SKIP_UPGRADE,
}; };
} }
@ -61,7 +60,7 @@ export function doStartUpgrade() {
const state = getState(); const state = getState();
const upgradeDownloadPath = selectUpgradeDownloadPath(state); const upgradeDownloadPath = selectUpgradeDownloadPath(state);
ipcRenderer.send("upgrade", upgradeDownloadPath); ipcRenderer.send('upgrade', upgradeDownloadPath);
}; };
} }
@ -69,40 +68,36 @@ export function doDownloadUpgrade() {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(); const state = getState();
// Make a new directory within temp directory so the filename is guaranteed to be available // Make a new directory within temp directory so the filename is guaranteed to be available
const dir = fs.mkdtempSync( const dir = Fs.mkdtempSync(remote.app.getPath('temp') + Path.sep);
remote.app.getPath("temp") + require("path").sep const upgradeFilename = selectUpgradeFilename(state);
),
upgradeFilename = selectUpgradeFilename(state);
const options = { const options = {
onProgress: p => dispatch(doUpdateDownloadProgress(Math.round(p * 100))), onProgress: p => dispatch(doUpdateDownloadProgress(Math.round(p * 100))),
directory: dir, directory: dir,
}; };
download(remote.getCurrentWindow(), selectUpdateUrl(state), options).then( download(remote.getCurrentWindow(), selectUpdateUrl(state), options).then(downloadItem => {
downloadItem => { /**
/** * TODO: get the download path directly from the download object. It should just be
* TODO: get the download path directly from the download object. It should just be * downloadItem.getSavePath(), but the copy on the main process is being garbage collected
* downloadItem.getSavePath(), but the copy on the main process is being garbage collected * too soon.
* too soon. */
*/
dispatch({ dispatch({
type: types.UPGRADE_DOWNLOAD_COMPLETED, type: ACTIONS.UPGRADE_DOWNLOAD_COMPLETED,
data: { data: {
downloadItem, downloadItem,
path: path.join(dir, upgradeFilename), path: Path.join(dir, upgradeFilename),
}, },
}); });
} });
);
dispatch({ dispatch({
type: types.UPGRADE_DOWNLOAD_STARTED, type: ACTIONS.UPGRADE_DOWNLOAD_STARTED,
}); });
dispatch({ dispatch({
type: types.OPEN_MODAL, type: ACTIONS.OPEN_MODAL,
data: { data: {
modal: modals.DOWNLOADING, modal: MODALS.DOWNLOADING,
}, },
}); });
}; };
@ -127,7 +122,7 @@ export function doCancelUpgrade() {
} }
} }
dispatch({ type: types.UPGRADE_CANCELLED }); dispatch({ type: ACTIONS.UPGRADE_CANCELLED });
}; };
} }
@ -135,12 +130,12 @@ export function doCheckUpgradeAvailable() {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(); const state = getState();
dispatch({ dispatch({
type: types.CHECK_UPGRADE_START, type: ACTIONS.CHECK_UPGRADE_START,
}); });
const success = ({ remoteVersion, upgradeAvailable }) => { const success = ({ remoteVersion, upgradeAvailable }) => {
dispatch({ dispatch({
type: types.CHECK_UPGRADE_SUCCESS, type: ACTIONS.CHECK_UPGRADE_SUCCESS,
data: { data: {
upgradeAvailable, upgradeAvailable,
remoteVersion, remoteVersion,
@ -150,13 +145,12 @@ export function doCheckUpgradeAvailable() {
if ( if (
upgradeAvailable && upgradeAvailable &&
!selectCurrentModal(state) && !selectCurrentModal(state) &&
(!selectIsUpgradeSkipped(state) || (!selectIsUpgradeSkipped(state) || remoteVersion !== selectRemoteVersion(state))
remoteVersion !== selectRemoteVersion(state))
) { ) {
dispatch({ dispatch({
type: types.OPEN_MODAL, type: ACTIONS.OPEN_MODAL,
data: { data: {
modal: modals.UPGRADE, modal: MODALS.UPGRADE,
}, },
}); });
} }
@ -164,11 +158,11 @@ export function doCheckUpgradeAvailable() {
const fail = () => { const fail = () => {
dispatch({ dispatch({
type: types.CHECK_UPGRADE_FAIL, type: ACTIONS.CHECK_UPGRADE_FAIL,
}); });
}; };
lbry.getAppVersionInfo().then(success, fail); Lbry.getAppVersionInfo().then(success, fail);
}; };
} }
@ -182,32 +176,31 @@ export function doCheckUpgradeSubscribe() {
CHECK_UPGRADE_INTERVAL CHECK_UPGRADE_INTERVAL
); );
dispatch({ dispatch({
type: types.CHECK_UPGRADE_SUBSCRIBE, type: ACTIONS.CHECK_UPGRADE_SUBSCRIBE,
data: { checkUpgradeTimer }, data: { checkUpgradeTimer },
}); });
}; };
} }
export function doCheckDaemonVersion() { export function doCheckDaemonVersion() {
return function(dispatch, getState) { return function(dispatch) {
lbry.version().then(({ lbrynet_version }) => { Lbry.version().then(({ lbrynet_version: lbrynetVersion }) => {
dispatch({ dispatch({
type: type:
config.lbrynetDaemonVersion == lbrynet_version config.lbrynetDaemonVersion === lbrynetVersion
? types.DAEMON_VERSION_MATCH ? ACTIONS.DAEMON_VERSION_MATCH
: types.DAEMON_VERSION_MISMATCH, : ACTIONS.DAEMON_VERSION_MISMATCH,
}); });
}); });
}; };
} }
export function doAlertError(errorList) { export function doAlertError(errorList) {
return function(dispatch, getState) { return function(dispatch) {
const state = getState();
dispatch({ dispatch({
type: types.OPEN_MODAL, type: ACTIONS.OPEN_MODAL,
data: { data: {
modal: modals.ERROR, modal: MODALS.ERROR,
modalProps: { error: errorList }, modalProps: { error: errorList },
}, },
}); });
@ -219,7 +212,7 @@ export function doDaemonReady() {
const state = getState(); const state = getState();
dispatch(doAuthenticate()); dispatch(doAuthenticate());
dispatch({ type: types.DAEMON_READY }); dispatch({ type: ACTIONS.DAEMON_READY });
dispatch(doFetchDaemonSettings()); dispatch(doFetchDaemonSettings());
dispatch(doBalanceSubscribe()); dispatch(doBalanceSubscribe());
dispatch(doFetchFileInfosAndPublishedClaims()); dispatch(doFetchFileInfosAndPublishedClaims());
@ -234,19 +227,19 @@ export function doDaemonReady() {
export function doShowSnackBar(data) { export function doShowSnackBar(data) {
return { return {
type: types.SHOW_SNACKBAR, type: ACTIONS.SHOW_SNACKBAR,
data, data,
}; };
} }
export function doRemoveSnackBarSnack() { export function doRemoveSnackBarSnack() {
return { return {
type: types.REMOVE_SNACKBAR_SNACK, type: ACTIONS.REMOVE_SNACKBAR_SNACK,
}; };
} }
export function doClearCache() { export function doClearCache() {
return function(dispatch, getState) { return function() {
window.cacheStore.purge(); window.cacheStore.purge();
return Promise.resolve(); return Promise.resolve();
@ -254,15 +247,15 @@ export function doClearCache() {
} }
export function doQuit() { export function doQuit() {
return function(dispatch, getState) { return function() {
remote.app.quit(); remote.app.quit();
}; };
} }
export function doChangeVolume(volume) { export function doChangeVolume(volume) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.VOLUME_CHANGED, type: ACTIONS.VOLUME_CHANGED,
data: { data: {
volume, volume,
}, },

View file

@ -1,32 +1,27 @@
import * as types from "constants/action_types"; // eslint-disable-next-line import/prefer-default-export
import lbry from "lbry"; export function doFetchAvailability() {
import { selectFetchingAvailability } from "redux/selectors/availability"; return function() {
export function doFetchAvailability(uri) {
return function(dispatch, getState) {
/* /*
this is disabled atm - Jeremy this is disabled atm - Jeremy
*/ */
return; // const state = getState();
// const alreadyFetching = !!selectFetchingAvailability(state)[uri];
const state = getState(); //
const alreadyFetching = !!selectFetchingAvailability(state)[uri]; // if (!alreadyFetching) {
// dispatch({
if (!alreadyFetching) { // type: ACTIONS.FETCH_AVAILABILITY_STARTED,
dispatch({ // data: { uri },
type: types.FETCH_AVAILABILITY_STARTED, // });
data: { uri }, //
}); // lbry.get_availability({ uri }).then(availability => {
// dispatch({
lbry.get_availability({ uri }).then(availability => { // type: ACTIONS.FETCH_AVAILABILITY_COMPLETED,
dispatch({ // data: {
type: types.FETCH_AVAILABILITY_COMPLETED, // availability,
data: { // uri,
availability, // },
uri, // });
}, // });
}); // }
});
}
}; };
} }

View file

@ -1,65 +1,63 @@
import * as types from "constants/action_types"; import { ipcRenderer } from 'electron';
import * as settings from "constants/settings"; import * as ACTIONS from 'constants/action_types';
import lbry from "lbry"; import * as SETTINGS from 'constants/settings';
import lbryio from "lbryio"; import Lbry from 'lbry';
import lbryuri from "lbryuri"; import Lbryio from 'lbryio';
import { makeSelectClientSetting } from "redux/selectors/settings"; import Lbryuri from 'lbryuri';
import { selectBalance, selectTransactionItems } from "redux/selectors/wallet"; import { makeSelectClientSetting } from 'redux/selectors/settings';
import { selectBalance, selectTransactionItems } from 'redux/selectors/wallet';
import { import {
makeSelectFileInfoForUri, makeSelectFileInfoForUri,
selectDownloadingByOutpoint, selectDownloadingByOutpoint,
} from "redux/selectors/file_info"; selectTotalDownloadProgress,
import { selectResolvingUris } from "redux/selectors/content"; } from 'redux/selectors/file_info';
import { makeSelectCostInfoForUri } from "redux/selectors/cost_info"; import { selectResolvingUris } from 'redux/selectors/content';
import { doAlertError, doOpenModal } from "redux/actions/app"; import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
import { doClaimEligiblePurchaseRewards } from "redux/actions/rewards"; import { doAlertError, doOpenModal } from 'redux/actions/app';
import { selectBadgeNumber } from "redux/selectors/app"; import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
import { selectTotalDownloadProgress } from "redux/selectors/file_info"; import { selectBadgeNumber } from 'redux/selectors/app';
import setBadge from "util/setBadge"; import setBadge from 'util/setBadge';
import setProgressBar from "util/setProgressBar"; import setProgressBar from 'util/setProgressBar';
import batchActions from "util/batchActions"; import batchActions from 'util/batchActions';
import * as modals from "constants/modal_types"; import * as MODALS from 'constants/modal_types';
const { ipcRenderer } = require("electron");
const DOWNLOAD_POLL_INTERVAL = 250; const DOWNLOAD_POLL_INTERVAL = 250;
export function doResolveUris(uris) { export function doResolveUris(uris) {
return function(dispatch, getState) { return function(dispatch, getState) {
uris = uris.map(lbryuri.normalize); const normalizedUris = uris.map(Lbryuri.normalize);
const state = getState(); const state = getState();
// Filter out URIs that are already resolving // Filter out URIs that are already resolving
const resolvingUris = selectResolvingUris(state); const resolvingUris = selectResolvingUris(state);
const urisToResolve = uris.filter(uri => !resolvingUris.includes(uri)); const urisToResolve = normalizedUris.filter(uri => !resolvingUris.includes(uri));
if (urisToResolve.length === 0) { if (urisToResolve.length === 0) {
return; return;
} }
dispatch({ dispatch({
type: types.RESOLVE_URIS_STARTED, type: ACTIONS.RESOLVE_URIS_STARTED,
data: { uris }, data: { uris: normalizedUris },
}); });
const resolveInfo = {}; const resolveInfo = {};
lbry.resolve({ uris: urisToResolve }).then(result => { Lbry.resolve({ uris: urisToResolve }).then(result => {
for (const [uri, uriResolveInfo] of Object.entries(result)) { Object.entries(result).forEach(([uri, uriResolveInfo]) => {
const fallbackResolveInfo = { const fallbackResolveInfo = {
claim: null, claim: null,
claims_in_channel: null, claimsInChannel: null,
certificate: null, certificate: null,
}; };
const { claim, certificate, claims_in_channel } = const { claim, certificate, claims_in_channel: claimsInChannel } =
uriResolveInfo && !uriResolveInfo.error uriResolveInfo && !uriResolveInfo.error ? uriResolveInfo : fallbackResolveInfo;
? uriResolveInfo
: fallbackResolveInfo; resolveInfo[uri] = { claim, certificate, claimsInChannel };
resolveInfo[uri] = { claim, certificate, claims_in_channel }; });
}
dispatch({ dispatch({
type: types.RESOLVE_URIS_COMPLETED, type: ACTIONS.RESOLVE_URIS_COMPLETED,
data: { resolveInfo }, data: { resolveInfo },
}); });
}); });
@ -71,23 +69,21 @@ export function doResolveUri(uri) {
} }
export function doFetchFeaturedUris() { export function doFetchFeaturedUris() {
return function(dispatch, getState) { return function(dispatch) {
const state = getState();
dispatch({ dispatch({
type: types.FETCH_FEATURED_CONTENT_STARTED, type: ACTIONS.FETCH_FEATURED_CONTENT_STARTED,
}); });
const success = ({ Uris }) => { const success = ({ Uris }) => {
let urisToResolve = []; let urisToResolve = [];
for (const category in Uris) { Object.keys(Uris).forEach(category => {
urisToResolve = [...urisToResolve, ...Uris[category]]; urisToResolve = [...urisToResolve, ...Uris[category]];
} });
const actions = [ const actions = [
doResolveUris(urisToResolve), doResolveUris(urisToResolve),
{ {
type: types.FETCH_FEATURED_CONTENT_COMPLETED, type: ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED,
data: { data: {
uris: Uris, uris: Uris,
success: true, success: true,
@ -99,24 +95,22 @@ export function doFetchFeaturedUris() {
const failure = () => { const failure = () => {
dispatch({ dispatch({
type: types.FETCH_FEATURED_CONTENT_COMPLETED, type: ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED,
data: { data: {
uris: {}, uris: {},
}, },
}); });
}; };
lbryio.call("file", "list_homepage").then(success, failure); Lbryio.call('file', 'list_homepage').then(success, failure);
}; };
} }
export function doFetchRewardedContent() { export function doFetchRewardedContent() {
return function(dispatch, getState) { return function(dispatch) {
const state = getState();
const success = nameToClaimId => { const success = nameToClaimId => {
dispatch({ dispatch({
type: types.FETCH_REWARD_CONTENT_COMPLETED, type: ACTIONS.FETCH_REWARD_CONTENT_COMPLETED,
data: { data: {
claimIds: Object.values(nameToClaimId), claimIds: Object.values(nameToClaimId),
success: true, success: true,
@ -126,7 +120,7 @@ export function doFetchRewardedContent() {
const failure = () => { const failure = () => {
dispatch({ dispatch({
type: types.FETCH_REWARD_CONTENT_COMPLETED, type: ACTIONS.FETCH_REWARD_CONTENT_COMPLETED,
data: { data: {
claimIds: [], claimIds: [],
success: false, success: false,
@ -134,73 +128,69 @@ export function doFetchRewardedContent() {
}); });
}; };
lbryio.call("reward", "list_featured").then(success, failure); Lbryio.call('reward', 'list_featured').then(success, failure);
}; };
} }
export function doUpdateLoadStatus(uri, outpoint) { export function doUpdateLoadStatus(uri, outpoint) {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(); Lbry.file_list({
outpoint,
full_status: true,
}).then(([fileInfo]) => {
if (!fileInfo || fileInfo.written_bytes === 0) {
// download hasn't started yet
setTimeout(() => {
dispatch(doUpdateLoadStatus(uri, outpoint));
}, DOWNLOAD_POLL_INTERVAL);
} else if (fileInfo.completed) {
// TODO this isn't going to get called if they reload the client before
// the download finished
dispatch({
type: ACTIONS.DOWNLOADING_COMPLETED,
data: {
uri,
outpoint,
fileInfo,
},
});
lbry const badgeNumber = selectBadgeNumber(getState());
.file_list({ setBadge(badgeNumber === 0 ? '' : `${badgeNumber}`);
outpoint,
full_status: true,
})
.then(([fileInfo]) => {
if (!fileInfo || fileInfo.written_bytes == 0) {
// download hasn't started yet
setTimeout(() => {
dispatch(doUpdateLoadStatus(uri, outpoint));
}, DOWNLOAD_POLL_INTERVAL);
} else if (fileInfo.completed) {
// TODO this isn't going to get called if they reload the client before
// the download finished
dispatch({
type: types.DOWNLOADING_COMPLETED,
data: {
uri,
outpoint,
fileInfo,
},
});
const badgeNumber = selectBadgeNumber(getState()); const totalProgress = selectTotalDownloadProgress(getState());
setBadge(badgeNumber === 0 ? "" : `${badgeNumber}`); setProgressBar(totalProgress);
const totalProgress = selectTotalDownloadProgress(getState()); const notif = new window.Notification('LBRY Download Complete', {
setProgressBar(totalProgress); body: fileInfo.metadata.stream.metadata.title,
silent: false,
});
notif.onclick = () => {
ipcRenderer.send('focusWindow', 'main');
};
} else {
// ready to play
const { total_bytes: totalBytes, written_bytes: writtenBytes } = fileInfo;
const progress = writtenBytes / totalBytes * 100;
const notif = new window.Notification("LBRY Download Complete", { dispatch({
body: fileInfo.metadata.stream.metadata.title, type: ACTIONS.DOWNLOADING_PROGRESSED,
silent: false, data: {
}); uri,
notif.onclick = () => { outpoint,
ipcRenderer.send("focusWindow", "main"); fileInfo,
}; progress,
} else { },
// ready to play });
const { total_bytes, written_bytes } = fileInfo;
const progress = written_bytes / total_bytes * 100;
dispatch({ const totalProgress = selectTotalDownloadProgress(getState());
type: types.DOWNLOADING_PROGRESSED, setProgressBar(totalProgress);
data: {
uri,
outpoint,
fileInfo,
progress,
},
});
const totalProgress = selectTotalDownloadProgress(getState()); setTimeout(() => {
setProgressBar(totalProgress); dispatch(doUpdateLoadStatus(uri, outpoint));
}, DOWNLOAD_POLL_INTERVAL);
setTimeout(() => { }
dispatch(doUpdateLoadStatus(uri, outpoint)); });
}, DOWNLOAD_POLL_INTERVAL);
}
});
}; };
} }
@ -209,16 +199,16 @@ export function doStartDownload(uri, outpoint) {
const state = getState(); const state = getState();
if (!outpoint) { if (!outpoint) {
throw new Error("outpoint is required to begin a download"); throw new Error('outpoint is required to begin a download');
} }
const { downloadingByOutpoint = {} } = state.fileInfo; const { downloadingByOutpoint = {} } = state.fileInfo;
if (downloadingByOutpoint[outpoint]) return; if (downloadingByOutpoint[outpoint]) return;
lbry.file_list({ outpoint, full_status: true }).then(([fileInfo]) => { Lbry.file_list({ outpoint, full_status: true }).then(([fileInfo]) => {
dispatch({ dispatch({
type: types.DOWNLOADING_STARTED, type: ACTIONS.DOWNLOADING_STARTED,
data: { data: {
uri, uri,
outpoint, outpoint,
@ -232,50 +222,50 @@ export function doStartDownload(uri, outpoint) {
} }
export function doDownloadFile(uri, streamInfo) { export function doDownloadFile(uri, streamInfo) {
return function(dispatch, getState) { return function(dispatch) {
const state = getState();
dispatch(doStartDownload(uri, streamInfo.outpoint)); dispatch(doStartDownload(uri, streamInfo.outpoint));
lbryio Lbryio.call('file', 'view', {
.call("file", "view", { uri,
uri, outpoint: streamInfo.outpoint,
outpoint: streamInfo.outpoint, claim_id: streamInfo.claim_id,
claim_id: streamInfo.claim_id, }).catch(() => {});
})
.catch(() => {});
dispatch(doClaimEligiblePurchaseRewards()); dispatch(doClaimEligiblePurchaseRewards());
}; };
} }
export function doLoadVideo(uri) { export function doSetPlayingUri(uri) {
return function(dispatch, getState) { return function(dispatch) {
const state = getState();
dispatch({ dispatch({
type: types.LOADING_VIDEO_STARTED, type: ACTIONS.SET_PLAYING_URI,
data: { uri },
});
};
}
export function doLoadVideo(uri) {
return function(dispatch) {
dispatch({
type: ACTIONS.LOADING_VIDEO_STARTED,
data: { data: {
uri, uri,
}, },
}); });
lbry Lbry.get({ uri })
.get({ uri })
.then(streamInfo => { .then(streamInfo => {
const timeout = const timeout =
streamInfo === null || streamInfo === null || typeof streamInfo !== 'object' || streamInfo.error === 'Timeout';
typeof streamInfo !== "object" ||
streamInfo.error == "Timeout";
if (timeout) { if (timeout) {
dispatch(doSetPlayingUri(null)); dispatch(doSetPlayingUri(null));
dispatch({ dispatch({
type: types.LOADING_VIDEO_FAILED, type: ACTIONS.LOADING_VIDEO_FAILED,
data: { uri }, data: { uri },
}); });
dispatch(doOpenModal(modals.FILE_TIMEOUT, { uri })); dispatch(doOpenModal(MODALS.FILE_TIMEOUT, { uri }));
} else { } else {
dispatch(doDownloadFile(uri, streamInfo)); dispatch(doDownloadFile(uri, streamInfo));
} }
@ -283,7 +273,7 @@ export function doLoadVideo(uri) {
.catch(() => { .catch(() => {
dispatch(doSetPlayingUri(null)); dispatch(doSetPlayingUri(null));
dispatch({ dispatch({
type: types.LOADING_VIDEO_FAILED, type: ACTIONS.LOADING_VIDEO_FAILED,
data: { uri }, data: { uri },
}); });
dispatch( dispatch(
@ -303,12 +293,11 @@ export function doPurchaseUri(uri) {
const balance = selectBalance(state); const balance = selectBalance(state);
const fileInfo = makeSelectFileInfoForUri(uri)(state); const fileInfo = makeSelectFileInfoForUri(uri)(state);
const downloadingByOutpoint = selectDownloadingByOutpoint(state); const downloadingByOutpoint = selectDownloadingByOutpoint(state);
const alreadyDownloading = const alreadyDownloading = fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];
fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];
function attemptPlay(cost, instantPurchaseMax = null) { function attemptPlay(cost, instantPurchaseMax = null) {
if (cost > 0 && (!instantPurchaseMax || cost > instantPurchaseMax)) { if (cost > 0 && (!instantPurchaseMax || cost > instantPurchaseMax)) {
dispatch(doOpenModal(modals.AFFIRM_PURCHASE, { uri })); dispatch(doOpenModal(MODALS.AFFIRM_PURCHASE, { uri }));
} else { } else {
dispatch(doLoadVideo(uri)); dispatch(doLoadVideo(uri));
} }
@ -321,12 +310,14 @@ export function doPurchaseUri(uri) {
// doLoadVideo action to reconstruct the file from the blobs // doLoadVideo action to reconstruct the file from the blobs
if (!fileInfo.written_bytes) dispatch(doLoadVideo(uri)); if (!fileInfo.written_bytes) dispatch(doLoadVideo(uri));
return Promise.resolve(); Promise.resolve();
return;
} }
// we are already downloading the file // we are already downloading the file
if (alreadyDownloading) { if (alreadyDownloading) {
return Promise.resolve(); Promise.resolve();
return;
} }
const costInfo = makeSelectCostInfoForUri(uri)(state); const costInfo = makeSelectCostInfoForUri(uri)(state);
@ -334,25 +325,21 @@ export function doPurchaseUri(uri) {
if (cost > balance) { if (cost > balance) {
dispatch(doSetPlayingUri(null)); dispatch(doSetPlayingUri(null));
dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS)); dispatch(doOpenModal(MODALS.INSUFFICIENT_CREDITS));
return Promise.resolve(); Promise.resolve();
return;
} }
if ( if (cost === 0 || !makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED)(state)) {
cost == 0 ||
!makeSelectClientSetting(settings.INSTANT_PURCHASE_ENABLED)(state)
) {
attemptPlay(cost); attemptPlay(cost);
} else { } else {
const instantPurchaseMax = makeSelectClientSetting( const instantPurchaseMax = makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_MAX)(state);
settings.INSTANT_PURCHASE_MAX if (instantPurchaseMax.currency === 'LBC') {
)(state);
if (instantPurchaseMax.currency == "LBC") {
attemptPlay(cost, instantPurchaseMax.amount); attemptPlay(cost, instantPurchaseMax.amount);
} else { } else {
// Need to convert currency of instant purchase maximum before trying to play // Need to convert currency of instant purchase maximum before trying to play
lbryio.getExchangeRates().then(({ lbc_usd }) => { Lbryio.getExchangeRates().then(({ LBC_USD }) => {
attemptPlay(cost, instantPurchaseMax.amount / lbc_usd); attemptPlay(cost, instantPurchaseMax.amount / LBC_USD);
}); });
} }
} }
@ -360,22 +347,22 @@ export function doPurchaseUri(uri) {
} }
export function doFetchClaimsByChannel(uri, page) { export function doFetchClaimsByChannel(uri, page) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.FETCH_CHANNEL_CLAIMS_STARTED, type: ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED,
data: { uri, page }, data: { uri, page },
}); });
lbry.claim_list_by_channel({ uri, page: page || 1 }).then(result => { Lbry.claim_list_by_channel({ uri, page: page || 1 }).then(result => {
const claimResult = result[uri] || {}; const claimResult = result[uri] || {};
const { claims_in_channel, returned_page } = claimResult; const { claims_in_channel: claimsInChannel, returned_page: returnedPage } = claimResult;
dispatch({ dispatch({
type: types.FETCH_CHANNEL_CLAIMS_COMPLETED, type: ACTIONS.FETCH_CHANNEL_CLAIMS_COMPLETED,
data: { data: {
uri, uri,
claims: claims_in_channel || [], claims: claimsInChannel || [],
page: returned_page || undefined, page: returnedPage || undefined,
}, },
}); });
}); });
@ -383,18 +370,18 @@ export function doFetchClaimsByChannel(uri, page) {
} }
export function doFetchClaimCountByChannel(uri) { export function doFetchClaimCountByChannel(uri) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.FETCH_CHANNEL_CLAIM_COUNT_STARTED, type: ACTIONS.FETCH_CHANNEL_CLAIM_COUNT_STARTED,
data: { uri }, data: { uri },
}); });
lbry.claim_list_by_channel({ uri }).then(result => { Lbry.claim_list_by_channel({ uri }).then(result => {
const claimResult = result[uri], const claimResult = result[uri];
totalClaims = claimResult ? claimResult.claims_in_channel : 0; const totalClaims = claimResult ? claimResult.claims_in_channel : 0;
dispatch({ dispatch({
type: types.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED, type: ACTIONS.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED,
data: { data: {
uri, uri,
totalClaims, totalClaims,
@ -405,14 +392,14 @@ export function doFetchClaimCountByChannel(uri) {
} }
export function doFetchClaimListMine() { export function doFetchClaimListMine() {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.FETCH_CLAIM_LIST_MINE_STARTED, type: ACTIONS.FETCH_CLAIM_LIST_MINE_STARTED,
}); });
lbry.claim_list_mine().then(claims => { Lbry.claim_list_mine().then(claims => {
dispatch({ dispatch({
type: types.FETCH_CLAIM_LIST_MINE_COMPLETED, type: ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED,
data: { data: {
claims, claims,
}, },
@ -422,69 +409,59 @@ export function doFetchClaimListMine() {
} }
export function doPlayUri(uri) { export function doPlayUri(uri) {
return function(dispatch, getState) { return function(dispatch) {
dispatch(doSetPlayingUri(uri)); dispatch(doSetPlayingUri(uri));
dispatch(doPurchaseUri(uri)); dispatch(doPurchaseUri(uri));
}; };
} }
export function doSetPlayingUri(uri) {
return function(dispatch, getState) {
dispatch({
type: types.SET_PLAYING_URI,
data: { uri },
});
};
}
export function doFetchChannelListMine() { export function doFetchChannelListMine() {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.FETCH_CHANNEL_LIST_MINE_STARTED, type: ACTIONS.FETCH_CHANNEL_LIST_MINE_STARTED,
}); });
const callback = channels => { const callback = channels => {
dispatch({ dispatch({
type: types.FETCH_CHANNEL_LIST_MINE_COMPLETED, type: ACTIONS.FETCH_CHANNEL_LIST_MINE_COMPLETED,
data: { claims: channels }, data: { claims: channels },
}); });
}; };
lbry.channel_list_mine().then(callback); Lbry.channel_list_mine().then(callback);
}; };
} }
export function doCreateChannel(name, amount) { export function doCreateChannel(name, amount) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.CREATE_CHANNEL_STARTED, type: ACTIONS.CREATE_CHANNEL_STARTED,
}); });
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
lbry Lbry.channel_new({
.channel_new({ channel_name: name,
channel_name: name, amount: parseFloat(amount),
amount: parseFloat(amount), }).then(
}) channelClaim => {
.then( const newChannelClaim = channelClaim;
channelClaim => { newChannelClaim.name = name;
channelClaim.name = name; dispatch({
dispatch({ type: ACTIONS.CREATE_CHANNEL_COMPLETED,
type: types.CREATE_CHANNEL_COMPLETED, data: { newChannelClaim },
data: { channelClaim }, });
}); resolve(newChannelClaim);
resolve(channelClaim); },
}, error => {
err => { reject(error);
reject(err); }
} );
);
}); });
}; };
} }
export function doPublish(params) { export function doPublish(params) {
return function(dispatch, getState) { return function(dispatch) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const success = claim => { const success = claim => {
resolve(claim); resolve(claim);
@ -497,7 +474,7 @@ export function doPublish(params) {
}; };
const failure = err => reject(err); const failure = err => reject(err);
lbry.publishDeprecated(params, null, success, failure); Lbry.publishDeprecated(params, null, success, failure);
}); });
}; };
} }
@ -507,40 +484,38 @@ export function doAbandonClaim(txid, nout) {
const state = getState(); const state = getState();
const transactionItems = selectTransactionItems(state); const transactionItems = selectTransactionItems(state);
const { claim_id: claimId, claim_name: name } = transactionItems.find( const { claim_id: claimId, claim_name: name } = transactionItems.find(
claim => claim.txid == txid && claim.nout == nout claim => claim.txid === txid && claim.nout === nout
); );
dispatch({ dispatch({
type: types.ABANDON_CLAIM_STARTED, type: ACTIONS.ABANDON_CLAIM_STARTED,
data: { data: {
claimId, claimId,
}, },
}); });
const errorCallback = error => { const errorCallback = () => {
dispatch(doOpenModal(modals.TRANSACTION_FAILED)); dispatch(doOpenModal(MODALS.TRANSACTION_FAILED));
}; };
const successCallback = results => { const successCallback = results => {
if (results.txid) { if (results.txid) {
dispatch({ dispatch({
type: types.ABANDON_CLAIM_SUCCEEDED, type: ACTIONS.ABANDON_CLAIM_SUCCEEDED,
data: { data: {
claimId, claimId,
}, },
}); });
dispatch(doResolveUri(lbryuri.build({ name, claimId }))); dispatch(doResolveUri(Lbryuri.build({ name, claimId })));
dispatch(doFetchClaimListMine()); dispatch(doFetchClaimListMine());
} else { } else {
dispatch(doOpenModal(modals.TRANSACTION_FAILED)); dispatch(doOpenModal(MODALS.TRANSACTION_FAILED));
} }
}; };
lbry Lbry.claim_abandon({
.claim_abandon({ txid,
txid, nout,
nout, }).then(successCallback, errorCallback);
})
.then(successCallback, errorCallback);
}; };
} }

View file

@ -1,31 +1,18 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import lbry from "lbry"; import Lbryio from 'lbryio';
import lbryio from "lbryio"; import { selectClaimsByUri } from 'redux/selectors/claims';
import { doResolveUri } from "redux/actions/content";
import { selectResolvingUris } from "redux/selectors/content";
import { selectClaimsByUri } from "redux/selectors/claims";
import { selectSettingsIsGenerous } from "redux/selectors/settings";
// eslint-disable-next-line import/prefer-default-export
export function doFetchCostInfoForUri(uri) { export function doFetchCostInfoForUri(uri) {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(), const state = getState();
claim = selectClaimsByUri(state)[uri], const claim = selectClaimsByUri(state)[uri];
isGenerous = selectSettingsIsGenerous(state);
if (!claim) return null; if (!claim) return;
function begin() {
dispatch({
type: types.FETCH_COST_INFO_STARTED,
data: {
uri,
},
});
}
function resolve(costInfo) { function resolve(costInfo) {
dispatch({ dispatch({
type: types.FETCH_COST_INFO_COMPLETED, type: ACTIONS.FETCH_COST_INFO_COMPLETED,
data: { data: {
uri, uri,
costInfo, costInfo,
@ -33,18 +20,6 @@ export function doFetchCostInfoForUri(uri) {
}); });
} }
/**
* "Generous" check below is disabled. We're no longer attempting to include or estimate data fees regardless of settings.
*
* This should be modified when lbry.stream_cost_estimate is reliable and performant.
*/
/*
lbry.stream_cost_estimate({ uri }).then(cost => {
cacheAndResolve(cost);
}, reject);
*/
const fee = const fee =
claim.value && claim.value.stream && claim.value.stream.metadata claim.value && claim.value.stream && claim.value.stream.metadata
? claim.value.stream.metadata.fee ? claim.value.stream.metadata.fee
@ -52,35 +27,12 @@ export function doFetchCostInfoForUri(uri) {
if (fee === undefined) { if (fee === undefined) {
resolve({ cost: 0, includesData: true }); resolve({ cost: 0, includesData: true });
} else if (fee.currency == "LBC") { } else if (fee.currency === 'LBC') {
resolve({ cost: fee.amount, includesData: true }); resolve({ cost: fee.amount, includesData: true });
} else { } else {
// begin(); Lbryio.getExchangeRates().then(({ LBC_USD }) => {
lbryio.getExchangeRates().then(({ lbc_usd }) => { resolve({ cost: fee.amount / LBC_USD, includesData: true });
resolve({ cost: fee.amount / lbc_usd, includesData: true });
}); });
} }
// if (isGenerous && claim) {
// let cost;
// const fee = claim.value &&
// claim.value.stream &&
// claim.value.stream.metadata
// ? claim.value.stream.metadata.fee
// : undefined;
// if (fee === undefined) {
// resolve({ cost: 0, includesData: true });
// } else if (fee.currency == "LBC") {
// resolve({ cost: fee.amount, includesData: true });
// } else {
// // begin();
// lbryio.getExchangeRates().then(({ lbc_usd }) => {
// resolve({ cost: fee.amount / lbc_usd, includesData: true });
// });
// }
// } else {
// begin();
// lbry.getCostInfo(uri).then(resolve);
// }
}; };
} }

View file

@ -1,23 +1,22 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import lbry from "lbry"; import Lbry from 'lbry';
import { doFetchClaimListMine, doAbandonClaim } from "redux/actions/content"; import { doFetchClaimListMine, doAbandonClaim } from 'redux/actions/content';
import { import {
selectClaimsByUri, selectClaimsByUri,
selectIsFetchingClaimListMine, selectIsFetchingClaimListMine,
selectMyClaimsOutpoints, selectMyClaimsOutpoints,
} from "redux/selectors/claims"; } from 'redux/selectors/claims';
import { import {
selectIsFetchingFileList, selectIsFetchingFileList,
selectFileInfosByOutpoint, selectFileInfosByOutpoint,
selectUrisLoading, selectUrisLoading,
selectTotalDownloadProgress, selectTotalDownloadProgress,
} from "redux/selectors/file_info"; } from 'redux/selectors/file_info';
import { doCloseModal } from "redux/actions/app"; import { doCloseModal } from 'redux/actions/app';
import { doNavigate, doHistoryBack } from "redux/actions/navigation"; import { doHistoryBack } from 'redux/actions/navigation';
import setProgressBar from "util/setProgressBar"; import setProgressBar from 'util/setProgressBar';
import batchActions from "util/batchActions"; import batchActions from 'util/batchActions';
import { shell } from 'electron';
const { shell } = require("electron");
export function doFetchFileInfo(uri) { export function doFetchFileInfo(uri) {
return function(dispatch, getState) { return function(dispatch, getState) {
@ -28,15 +27,15 @@ export function doFetchFileInfo(uri) {
if (!alreadyFetching) { if (!alreadyFetching) {
dispatch({ dispatch({
type: types.FETCH_FILE_INFO_STARTED, type: ACTIONS.FETCH_FILE_INFO_STARTED,
data: { data: {
outpoint, outpoint,
}, },
}); });
lbry.file_list({ outpoint, full_status: true }).then(fileInfos => { Lbry.file_list({ outpoint, full_status: true }).then(fileInfos => {
dispatch({ dispatch({
type: types.FETCH_FILE_INFO_COMPLETED, type: ACTIONS.FETCH_FILE_INFO_COMPLETED,
data: { data: {
outpoint, outpoint,
fileInfo: fileInfos && fileInfos.length ? fileInfos[0] : null, fileInfo: fileInfos && fileInfos.length ? fileInfos[0] : null,
@ -54,12 +53,12 @@ export function doFileList() {
if (!isFetching) { if (!isFetching) {
dispatch({ dispatch({
type: types.FILE_LIST_STARTED, type: ACTIONS.FILE_LIST_STARTED,
}); });
lbry.file_list().then(fileInfos => { Lbry.file_list().then(fileInfos => {
dispatch({ dispatch({
type: types.FILE_LIST_SUCCEEDED, type: ACTIONS.FILE_LIST_SUCCEEDED,
data: { data: {
fileInfos, fileInfos,
}, },
@ -69,8 +68,14 @@ export function doFileList() {
}; };
} }
export function doOpenFileInFolder(path) {
return function() {
shell.showItemInFolder(path);
};
}
export function doOpenFileInShell(path) { export function doOpenFileInShell(path) {
return function(dispatch, getState) { return function(dispatch) {
const success = shell.openItem(path); const success = shell.openItem(path);
if (!success) { if (!success) {
dispatch(doOpenFileInFolder(path)); dispatch(doOpenFileInFolder(path));
@ -78,17 +83,11 @@ export function doOpenFileInShell(path) {
}; };
} }
export function doOpenFileInFolder(path) {
return function(dispatch, getState) {
shell.showItemInFolder(path);
};
}
export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) { export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(); const state = getState();
lbry.file_delete({ Lbry.file_delete({
outpoint, outpoint,
delete_from_download_dir: deleteFromComputer, delete_from_download_dir: deleteFromComputer,
}); });
@ -108,7 +107,7 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
} }
dispatch({ dispatch({
type: types.FILE_DELETE, type: ACTIONS.FILE_DELETE,
data: { data: {
outpoint, outpoint,
}, },
@ -119,12 +118,8 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
}; };
} }
export function doDeleteFileAndGoBack( export function doDeleteFileAndGoBack(fileInfo, deleteFromComputer, abandonClaim) {
fileInfo, return function(dispatch) {
deleteFromComputer,
abandonClaim
) {
return function(dispatch, getState) {
const actions = []; const actions = [];
actions.push(doCloseModal()); actions.push(doCloseModal());
actions.push(doHistoryBack()); actions.push(doHistoryBack());
@ -135,9 +130,9 @@ export function doDeleteFileAndGoBack(
export function doFetchFileInfosAndPublishedClaims() { export function doFetchFileInfosAndPublishedClaims() {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(), const state = getState();
isFetchingClaimListMine = selectIsFetchingClaimListMine(state), const isFetchingClaimListMine = selectIsFetchingClaimListMine(state);
isFetchingFileInfo = selectIsFetchingFileList(state); const isFetchingFileInfo = selectIsFetchingFileList(state);
if (!isFetchingClaimListMine) dispatch(doFetchClaimListMine()); if (!isFetchingClaimListMine) dispatch(doFetchClaimListMine());
if (!isFetchingFileInfo) dispatch(doFileList()); if (!isFetchingFileInfo) dispatch(doFileList());

View file

@ -1,18 +1,10 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import { import { selectHistoryStack, selectHistoryIndex } from 'redux/selectors/navigation';
computePageFromPath, import { toQueryString } from 'util/query_params';
selectPageTitle, import amplitude from 'amplitude-js';
selectCurrentPage,
selectCurrentParams,
selectHistoryStack,
selectHistoryIndex,
} from "redux/selectors/navigation";
import { doSearch } from "redux/actions/search";
import { toQueryString } from "util/query_params";
import amplitude from "amplitude-js";
export function doNavigate(path, params = {}, options = {}) { export function doNavigate(path, params = {}, options = {}) {
return function(dispatch, getState) { return function(dispatch) {
if (!path) { if (!path) {
return; return;
} }
@ -22,40 +14,38 @@ export function doNavigate(path, params = {}, options = {}) {
url += `?${toQueryString(params)}`; url += `?${toQueryString(params)}`;
} }
const scrollY = options.scrollY; const { scrollY } = options;
amplitude.getInstance().logEvent("NAVIGATION", { destination: url }); amplitude.getInstance().logEvent('NAVIGATION', { destination: url });
dispatch({ dispatch({
type: types.HISTORY_NAVIGATE, type: ACTIONS.HISTORY_NAVIGATE,
data: { url, index: options.index, scrollY }, data: { url, index: options.index, scrollY },
}); });
}; };
} }
export function doAuthNavigate(pathAfterAuth = null, params = {}) { export function doAuthNavigate(pathAfterAuth = null, params = {}) {
return function(dispatch, getState) { return function(dispatch) {
if (pathAfterAuth) { if (pathAfterAuth) {
dispatch({ dispatch({
type: types.CHANGE_AFTER_AUTH_PATH, type: ACTIONS.CHANGE_AFTER_AUTH_PATH,
data: { data: {
path: `${pathAfterAuth}?${toQueryString(params)}`, path: `${pathAfterAuth}?${toQueryString(params)}`,
}, },
}); });
} }
dispatch(doNavigate("/auth")); dispatch(doNavigate('/auth'));
}; };
} }
export function doHistoryTraverse(dispatch, state, modifier) { export function doHistoryTraverse(dispatch, state, modifier) {
const stack = selectHistoryStack(state), const stack = selectHistoryStack(state);
index = selectHistoryIndex(state) + modifier; const index = selectHistoryIndex(state) + modifier;
if (index >= 0 && index < stack.length) { if (index >= 0 && index < stack.length) {
const historyItem = stack[index]; const historyItem = stack[index];
return dispatch( dispatch(doNavigate(historyItem.path, {}, { scrollY: historyItem.scrollY, index }));
doNavigate(historyItem.path, {}, { scrollY: historyItem.scrollY, index })
);
} }
} }
@ -72,9 +62,9 @@ export function doHistoryForward() {
} }
export function doRecordScroll(scroll) { export function doRecordScroll(scroll) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.WINDOW_SCROLLED, type: ACTIONS.WINDOW_SCROLLED,
data: { scrollY: scroll }, data: { scrollY: scroll },
}); });
}; };

View file

@ -1,30 +1,26 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import * as modals from "constants/modal_types"; import * as MODALS from 'constants/modal_types';
import lbryio from "lbryio"; import Lbryio from 'lbryio';
import rewards from "rewards"; import rewards from 'rewards';
import { selectUnclaimedRewardsByType } from "redux/selectors/rewards"; import { selectUnclaimedRewardsByType } from 'redux/selectors/rewards';
import { selectUserIsRewardApproved } from "redux/selectors/user"; import { selectUserIsRewardApproved } from 'redux/selectors/user';
import { selectClaimedRewardsById } from "../selectors/rewards";
export function doRewardList() { export function doRewardList() {
return function(dispatch, getState) { return function(dispatch) {
const state = getState();
dispatch({ dispatch({
type: types.FETCH_REWARDS_STARTED, type: ACTIONS.FETCH_REWARDS_STARTED,
}); });
lbryio Lbryio.call('reward', 'list', { multiple_rewards_per_type: true })
.call("reward", "list", { multiple_rewards_per_type: true })
.then(userRewards => { .then(userRewards => {
dispatch({ dispatch({
type: types.FETCH_REWARDS_COMPLETED, type: ACTIONS.FETCH_REWARDS_COMPLETED,
data: { userRewards }, data: { userRewards },
}); });
}) })
.catch(() => { .catch(() => {
dispatch({ dispatch({
type: types.FETCH_REWARDS_COMPLETED, type: ACTIONS.FETCH_REWARDS_COMPLETED,
data: { userRewards: [] }, data: { userRewards: [] },
}); });
}); });
@ -33,10 +29,10 @@ export function doRewardList() {
export function doClaimRewardType(rewardType) { export function doClaimRewardType(rewardType) {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(), const state = getState();
rewardsByType = selectUnclaimedRewardsByType(state), const rewardsByType = selectUnclaimedRewardsByType(state);
reward = rewardsByType[rewardType], const reward = rewardsByType[rewardType];
userIsRewardApproved = selectUserIsRewardApproved(state); const userIsRewardApproved = selectUserIsRewardApproved(state);
if (!reward || reward.transaction_id) { if (!reward || reward.transaction_id) {
// already claimed or doesn't exist, do nothing // already claimed or doesn't exist, do nothing
@ -44,35 +40,37 @@ export function doClaimRewardType(rewardType) {
} }
if (!userIsRewardApproved && rewardType !== rewards.TYPE_CONFIRM_EMAIL) { if (!userIsRewardApproved && rewardType !== rewards.TYPE_CONFIRM_EMAIL) {
return dispatch({ dispatch({
type: types.OPEN_MODAL, type: ACTIONS.OPEN_MODAL,
data: { modal: modals.REWARD_APPROVAL_REQUIRED }, data: { modal: MODALS.REWARD_APPROVAL_REQUIRED },
}); });
return;
} }
dispatch({ dispatch({
type: types.CLAIM_REWARD_STARTED, type: ACTIONS.CLAIM_REWARD_STARTED,
data: { reward }, data: { reward },
}); });
const success = reward => { const success = successReward => {
dispatch({ dispatch({
type: types.CLAIM_REWARD_SUCCESS, type: ACTIONS.CLAIM_REWARD_SUCCESS,
data: { data: {
reward, reward: successReward,
}, },
}); });
if (reward.reward_type == rewards.TYPE_CONFIRM_EMAIL) { if (successReward.reward_type === rewards.TYPE_CONFIRM_EMAIL) {
dispatch({ dispatch({
type: types.OPEN_MODAL, type: ACTIONS.OPEN_MODAL,
data: { modal: modals.FIRST_REWARD }, data: { modal: MODALS.FIRST_REWARD },
}); });
} }
}; };
const failure = error => { const failure = error => {
dispatch({ dispatch({
type: types.CLAIM_REWARD_FAILURE, type: ACTIONS.CLAIM_REWARD_FAILURE,
data: { reward, error }, data: { reward, error },
}); });
}; };
@ -83,30 +81,28 @@ export function doClaimRewardType(rewardType) {
export function doClaimEligiblePurchaseRewards() { export function doClaimEligiblePurchaseRewards() {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(), const state = getState();
rewardsByType = selectUnclaimedRewardsByType(state), const rewardsByType = selectUnclaimedRewardsByType(state);
userIsRewardApproved = selectUserIsRewardApproved(state); const userIsRewardApproved = selectUserIsRewardApproved(state);
if (!userIsRewardApproved || !lbryio.enabled) { if (!userIsRewardApproved || !Lbryio.enabled) {
return; return;
} }
if (rewardsByType[rewards.TYPE_FIRST_STREAM]) { if (rewardsByType[rewards.TYPE_FIRST_STREAM]) {
dispatch(doClaimRewardType(rewards.TYPE_FIRST_STREAM)); dispatch(doClaimRewardType(rewards.TYPE_FIRST_STREAM));
} else { } else {
[rewards.TYPE_MANY_DOWNLOADS, rewards.TYPE_FEATURED_DOWNLOAD].forEach( [rewards.TYPE_MANY_DOWNLOADS, rewards.TYPE_FEATURED_DOWNLOAD].forEach(type => {
type => { dispatch(doClaimRewardType(type));
dispatch(doClaimRewardType(type)); });
}
);
} }
}; };
} }
export function doClaimRewardClearError(reward) { export function doClaimRewardClearError(reward) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.CLAIM_REWARD_CLEAR_ERROR, type: ACTIONS.CLAIM_REWARD_CLEAR_ERROR,
data: { reward }, data: { reward },
}); });
}; };

View file

@ -1,30 +1,32 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import lbryuri from "lbryuri"; import Lbryuri from 'lbryuri';
import { doResolveUri } from "redux/actions/content"; import { doResolveUri } from 'redux/actions/content';
import { doNavigate } from "redux/actions/navigation"; import { doNavigate } from 'redux/actions/navigation';
import { selectCurrentPage } from "redux/selectors/navigation"; import { selectCurrentPage } from 'redux/selectors/navigation';
import batchActions from "util/batchActions"; import batchActions from 'util/batchActions';
// eslint-disable-next-line import/prefer-default-export
export function doSearch(rawQuery) { export function doSearch(rawQuery) {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(); const state = getState();
const page = selectCurrentPage(state); const page = selectCurrentPage(state);
const query = rawQuery.replace(/^lbry:\/\//i, ""); const query = rawQuery.replace(/^lbry:\/\//i, '');
if (!query) { if (!query) {
return dispatch({ dispatch({
type: types.SEARCH_CANCELLED, type: ACTIONS.SEARCH_CANCELLED,
}); });
return;
} }
dispatch({ dispatch({
type: types.SEARCH_STARTED, type: ACTIONS.SEARCH_STARTED,
data: { query }, data: { query },
}); });
if (page != "search") { if (page !== 'search') {
dispatch(doNavigate("search", { query })); dispatch(doNavigate('search', { query }));
} else { } else {
fetch(`https://lighthouse.lbry.io/search?s=${query}`) fetch(`https://lighthouse.lbry.io/search?s=${query}`)
.then( .then(
@ -38,7 +40,7 @@ export function doSearch(rawQuery) {
const actions = []; const actions = [];
data.forEach(result => { data.forEach(result => {
const uri = lbryuri.build({ const uri = Lbryuri.build({
name: result.name, name: result.name,
claimId: result.claimId, claimId: result.claimId,
}); });
@ -47,7 +49,7 @@ export function doSearch(rawQuery) {
}); });
actions.push({ actions.push({
type: types.SEARCH_COMPLETED, type: ACTIONS.SEARCH_COMPLETED,
data: { data: {
query, query,
uris, uris,
@ -55,9 +57,9 @@ export function doSearch(rawQuery) {
}); });
dispatch(batchActions(...actions)); dispatch(batchActions(...actions));
}) })
.catch(err => { .catch(() => {
dispatch({ dispatch({
type: types.SEARCH_CANCELLED, type: ACTIONS.SEARCH_CANCELLED,
}); });
}); });
} }

View file

@ -1,17 +1,15 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import * as settings from "constants/settings"; import * as SETTINGS from 'constants/settings';
import { doAlertError } from "redux/actions/app";
import batchActions from "util/batchActions";
import lbry from "lbry"; import Lbry from 'lbry';
import fs from "fs"; import Fs from 'fs';
import http from "http"; import Http from 'http';
export function doFetchDaemonSettings() { export function doFetchDaemonSettings() {
return function(dispatch, getState) { return function(dispatch) {
lbry.settings_get().then(settings => { Lbry.settings_get().then(settings => {
dispatch({ dispatch({
type: types.DAEMON_SETTINGS_RECEIVED, type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
data: { data: {
settings, settings,
}, },
@ -21,15 +19,15 @@ export function doFetchDaemonSettings() {
} }
export function doSetDaemonSetting(key, value) { export function doSetDaemonSetting(key, value) {
return function(dispatch, getState) { return function(dispatch) {
const settings = {}; const settings = {};
settings[key] = value; settings[key] = value;
lbry.settings_set(settings).then(settings); Lbry.settings_set(settings).then(settings);
lbry.settings_get().then(settings => { Lbry.settings_get().then(remoteSettings => {
dispatch({ dispatch({
type: types.DAEMON_SETTINGS_RECEIVED, type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
data: { data: {
settings, remoteSettings,
}, },
}); });
}); });
@ -38,7 +36,7 @@ export function doSetDaemonSetting(key, value) {
export function doSetClientSetting(key, value) { export function doSetClientSetting(key, value) {
return { return {
type: types.CLIENT_SETTING_CHANGED, type: ACTIONS.CLIENT_SETTING_CHANGED,
data: { data: {
key, key,
value, value,
@ -47,100 +45,98 @@ export function doSetClientSetting(key, value) {
} }
export function doGetThemes() { export function doGetThemes() {
return function(dispatch, getState) { return function(dispatch) {
const themes = ["light", "dark"]; const themes = ['light', 'dark'];
dispatch(doSetClientSetting(settings.THEMES, themes)); dispatch(doSetClientSetting(SETTINGS.THEMES, themes));
}; };
} }
export function doDownloadLanguage(langFile) { export function doDownloadLanguage(langFile) {
return function(dispatch, getState) { return function(dispatch) {
const destinationPath = `${app.i18n.directory}/${langFile}`; const destinationPath = `${app.i18n.directory}/${langFile}`;
const language = langFile.replace(".json", ""); const language = langFile.replace('.json', '');
const req = http.get( const errorHandler = () => {
Fs.unlink(destinationPath, () => {}); // Delete the file async. (But we don't check the result)
dispatch({
type: ACTIONS.DOWNLOAD_LANGUAGE_FAILED,
data: { language },
});
};
const req = Http.get(
{ {
headers: { headers: {
"Content-Type": "text/html", 'Content-Type': 'text/html',
}, },
host: "i18n.lbry.io", host: 'i18n.lbry.io',
path: `/langs/${langFile}`, path: `/langs/${langFile}`,
}, },
response => { response => {
if (response.statusCode === 200) { if (response.statusCode === 200) {
const file = fs.createWriteStream(destinationPath); const file = Fs.createWriteStream(destinationPath);
file.on("error", errorHandler); file.on('error', errorHandler);
file.on("finish", () => { file.on('finish', () => {
file.close(); file.close();
// push to our local list // push to our local list
dispatch({ dispatch({
type: types.DOWNLOAD_LANGUAGE_SUCCEEDED, type: ACTIONS.DOWNLOAD_LANGUAGE_SUCCEEDED,
data: { language }, data: { language },
}); });
}); });
response.pipe(file); response.pipe(file);
} else { } else {
errorHandler(new Error("Language request failed.")); errorHandler(new Error('Language request failed.'));
} }
} }
); );
const errorHandler = err => {
fs.unlink(destinationPath, () => {}); // Delete the file async. (But we don't check the result)
dispatch({
type: types.DOWNLOAD_LANGUAGE_FAILED,
data: { language },
});
};
req.setTimeout(30000, () => { req.setTimeout(30000, () => {
req.abort(); req.abort();
}); });
req.on("error", errorHandler); req.on('error', errorHandler);
req.end(); req.end();
}; };
} }
export function doDownloadLanguages() { export function doDownloadLanguages() {
return function(dispatch, getState) { return function() {
// temporarily disable i18n so I can get a working build out -- Jeremy // temporarily disable i18n so I can get a working build out -- Jeremy
return; // if (!Fs.existsSync(app.i18n.directory)) {
// Fs.mkdirSync(app.i18n.directory);
if (!fs.existsSync(app.i18n.directory)) { // }
fs.mkdirSync(app.i18n.directory); //
} // function checkStatus(response) {
// if (response.status >= 200 && response.status < 300) {
function checkStatus(response) { // return response;
if (response.status >= 200 && response.status < 300) { // }
return response; // throw new Error(
} // __("The list of available languages could not be retrieved.")
throw new Error( // );
__("The list of available languages could not be retrieved.") // }
); //
} // function parseJSON(response) {
// return response.json();
function parseJSON(response) { // }
return response.json(); //
} // return fetch("http://i18n.lbry.io")
// .then(checkStatus)
return fetch("http://i18n.lbry.io") // .then(parseJSON)
.then(checkStatus) // .then(files => {
.then(parseJSON) // const actions = files.map(doDownloadLanguage);
.then(files => { // dispatch(batchActions(...actions));
const actions = files.map(doDownloadLanguage); // });
dispatch(batchActions(...actions));
});
}; };
} }
export function doChangeLanguage(language) { export function doChangeLanguage(language) {
return function(dispatch, getState) { return function(dispatch) {
dispatch(doSetClientSetting(settings.LANGUAGE, language)); dispatch(doSetClientSetting(SETTINGS.LANGUAGE, language));
app.i18n.setLocale(language); app.i18n.setLocale(language);
}; };
} }

View file

@ -1,7 +1,7 @@
// @flow // @flow
import Promise from "bluebird"; import Promise from 'bluebird';
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import { coinRegexPatterns } from "util/shape_shift"; import { coinRegexPatterns } from 'util/shape_shift';
import type { import type {
GetSupportedCoinsSuccess, GetSupportedCoinsSuccess,
GetCoinStatsStart, GetCoinStatsStart,
@ -11,26 +11,26 @@ import type {
PrepareShapeShiftFail, PrepareShapeShiftFail,
GetActiveShiftSuccess, GetActiveShiftSuccess,
GetActiveShiftFail, GetActiveShiftFail,
} from "redux/reducers/shape_shift"; } from 'redux/reducers/shape_shift';
import type { FormikActions } from "types/common"; import type { FormikActions } from 'types/common';
// use promise chains instead of callbacks for shapeshift api // use promise chains instead of callbacks for shapeshift api
const shapeShift = Promise.promisifyAll(require("shapeshift.io")); const shapeShift = Promise.promisifyAll(require('shapeshift.io'));
// All ShapeShift actions // All ShapeShift actions
// Action types defined in the reducer will contain some payload // Action types defined in the reducer will contain some payload
export type Action = export type Action =
| { type: types.GET_SUPPORTED_COINS_START } | { type: ACTIONS.GET_SUPPORTED_COINS_START }
| { type: types.GET_SUPPORTED_COINS_FAIL } | { type: ACTIONS.GET_SUPPORTED_COINS_FAIL }
| GetSupportedCoinsSuccess | GetSupportedCoinsSuccess
| GetCoinStatsStart | GetCoinStatsStart
| { type: types.GET_COIN_STATS_START } | { type: ACTIONS.GET_COIN_STATS_START }
| GetCoinStatsFail | GetCoinStatsFail
| GetCoinStatsSuccess | GetCoinStatsSuccess
| { type: types.PREPARE_SHAPE_SHIFT_START } | { type: ACTIONS.PREPARE_SHAPE_SHIFT_START }
| PrepareShapeShiftFail | PrepareShapeShiftFail
| PrepareShapeShiftSuccess | PrepareShapeShiftSuccess
| { type: types.GET_ACTIVE_SHIFT_START } | { type: ACTIONS.GET_ACTIVE_SHIFT_START }
| GetActiveShiftFail | GetActiveShiftFail
| GetActiveShiftSuccess; | GetActiveShiftSuccess;
@ -38,10 +38,8 @@ export type Action =
// It would be nice to import these from types/common // It would be nice to import these from types/common
// Not sure how that would work since they rely on the Action type // Not sure how that would work since they rely on the Action type
type PromiseAction = Promise<Action>; type PromiseAction = Promise<Action>;
export type Dispatch = (action: Action | PromiseAction | Array<Action>) => any;
type ThunkAction = (dispatch: Dispatch) => any; type ThunkAction = (dispatch: Dispatch) => any;
export type Dispatch = (
action: Action | ThunkAction | PromiseAction | Array<Action>
) => any;
// ShapeShift form values // ShapeShift form values
export type ShapeShiftFormValues = { export type ShapeShiftFormValues = {
@ -50,15 +48,26 @@ export type ShapeShiftFormValues = {
receiveAddress: string, receiveAddress: string,
}; };
export const getCoinStats = (coin: string) => (dispatch: Dispatch): ThunkAction => {
const pair = `${coin.toLowerCase()}_lbc`;
dispatch({ type: ACTIONS.GET_COIN_STATS_START, data: coin });
return shapeShift
.marketInfoAsync(pair)
.then(marketInfo => dispatch({ type: ACTIONS.GET_COIN_STATS_SUCCESS, data: marketInfo }))
.catch(err => dispatch({ type: ACTIONS.GET_COIN_STATS_FAIL, data: err }));
};
export const shapeShiftInit = () => (dispatch: Dispatch): ThunkAction => { export const shapeShiftInit = () => (dispatch: Dispatch): ThunkAction => {
dispatch({ type: types.GET_SUPPORTED_COINS_START }); dispatch({ type: ACTIONS.GET_SUPPORTED_COINS_START });
return shapeShift return shapeShift
.coinsAsync() .coinsAsync()
.then(coinData => { .then(coinData => {
let supportedCoins = []; let supportedCoins = [];
Object.keys(coinData).forEach(symbol => { Object.keys(coinData).forEach(symbol => {
if (coinData[symbol].status === "available") { if (coinData[symbol].status === 'available') {
supportedCoins.push(coinData[symbol]); supportedCoins.push(coinData[symbol]);
} }
}); });
@ -69,69 +78,43 @@ export const shapeShiftInit = () => (dispatch: Dispatch): ThunkAction => {
.map(coin => coin.symbol); .map(coin => coin.symbol);
dispatch({ dispatch({
type: types.GET_SUPPORTED_COINS_SUCCESS, type: ACTIONS.GET_SUPPORTED_COINS_SUCCESS,
data: supportedCoins, data: supportedCoins,
}); });
dispatch(getCoinStats(supportedCoins[0])); dispatch(getCoinStats(supportedCoins[0]));
}) })
.catch(err => .catch(err => dispatch({ type: ACTIONS.GET_SUPPORTED_COINS_FAIL, data: err }));
dispatch({ type: types.GET_SUPPORTED_COINS_FAIL, data: err })
);
}; };
export const getCoinStats = (coin: string) => ( export const createShapeShift = (values: ShapeShiftFormValues, actions: FormikActions) => (
dispatch: Dispatch dispatch: Dispatch
): ThunkAction => { ): ThunkAction => {
const pair = `${coin.toLowerCase()}_lbc`; const { originCoin, returnAddress, receiveAddress: withdrawalAddress } = values;
dispatch({ type: types.GET_COIN_STATS_START, data: coin });
return shapeShift
.marketInfoAsync(pair)
.then(marketInfo =>
dispatch({ type: types.GET_COIN_STATS_SUCCESS, data: marketInfo })
)
.catch(err => dispatch({ type: types.GET_COIN_STATS_FAIL, data: err }));
};
export const createShapeShift = (
values: ShapeShiftFormValues,
actions: FormikActions
) => (dispatch: Dispatch): ThunkAction => {
const {
originCoin,
returnAddress,
receiveAddress: withdrawalAddress,
} = values;
const pair = `${originCoin.toLowerCase()}_lbc`; const pair = `${originCoin.toLowerCase()}_lbc`;
const options = { const options = {
returnAddress, returnAddress,
}; };
dispatch({ type: types.PREPARE_SHAPE_SHIFT_START }); dispatch({ type: ACTIONS.PREPARE_SHAPE_SHIFT_START });
return shapeShift return shapeShift
.shiftAsync(withdrawalAddress, pair, options) .shiftAsync(withdrawalAddress, pair, options)
.then(res => .then(res => dispatch({ type: ACTIONS.PREPARE_SHAPE_SHIFT_SUCCESS, data: res }))
dispatch({ type: types.PREPARE_SHAPE_SHIFT_SUCCESS, data: res })
)
.catch(err => { .catch(err => {
dispatch({ type: types.PREPARE_SHAPE_SHIFT_FAIL, data: err }); dispatch({ type: ACTIONS.PREPARE_SHAPE_SHIFT_FAIL, data: err });
// for formik to stop the submit // for formik to stop the submit
actions.setSubmitting(false); actions.setSubmitting(false);
}); });
}; };
export const getActiveShift = (depositAddress: string) => ( export const getActiveShift = (depositAddress: string) => (dispatch: Dispatch): ThunkAction => {
dispatch: Dispatch dispatch({ type: ACTIONS.GET_ACTIVE_SHIFT_START });
): ThunkAction => {
dispatch({ type: types.GET_ACTIVE_SHIFT_START });
return shapeShift return shapeShift
.statusAsync(depositAddress) .statusAsync(depositAddress)
.then(res => dispatch({ type: types.GET_ACTIVE_SHIFT_SUCCESS, data: res })) .then(res => dispatch({ type: ACTIONS.GET_ACTIVE_SHIFT_SUCCESS, data: res }))
.catch(err => dispatch({ type: types.GET_ACTIVE_SHIFT_FAIL, data: err })); .catch(err => dispatch({ type: ACTIONS.GET_ACTIVE_SHIFT_FAIL, data: err }));
}; };
export const clearShapeShift = () => (dispatch: Dispatch): Action => export const clearShapeShift = () => (dispatch: Dispatch): Action =>
dispatch({ type: types.CLEAR_SHAPE_SHIFT }); dispatch({ type: ACTIONS.CLEAR_SHAPE_SHIFT });

View file

@ -1,27 +1,18 @@
// @flow // @flow
import * as actions from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import type { import type { Subscription, Dispatch } from 'redux/reducers/subscriptions';
Subscription,
Action,
Dispatch,
} from "redux/reducers/subscriptions";
import lbry from "lbry";
export const doChannelSubscribe = (subscription: Subscription) => ( export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dispatch) =>
dispatch: Dispatch
) =>
dispatch({ dispatch({
type: actions.CHANNEL_SUBSCRIBE, type: ACTIONS.CHANNEL_SUBSCRIBE,
data: subscription, data: subscription,
}); });
export const doChannelUnsubscribe = (subscription: Subscription) => ( export const doChannelUnsubscribe = (subscription: Subscription) => (dispatch: Dispatch) =>
dispatch: Dispatch
) =>
dispatch({ dispatch({
type: actions.CHANNEL_UNSUBSCRIBE, type: ACTIONS.CHANNEL_UNSUBSCRIBE,
data: subscription, data: subscription,
}); });
export const setHasFetchedSubscriptions = () => (dispatch: Dispatch) => export const setHasFetchedSubscriptions = () => (dispatch: Dispatch) =>
dispatch({ type: actions.HAS_FETCHED_SUBSCRIPTIONS }); dispatch({ type: ACTIONS.HAS_FETCHED_SUBSCRIPTIONS });

View file

@ -1,30 +1,54 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import * as modals from "constants/modal_types"; import * as MODALS from 'constants/modal_types';
import lbryio from "lbryio"; import Lbryio from 'lbryio';
import { doOpenModal, doShowSnackBar } from "redux/actions/app"; import { doOpenModal, doShowSnackBar } from 'redux/actions/app';
import { doRewardList, doClaimRewardType } from "redux/actions/rewards"; import { doRewardList, doClaimRewardType } from 'redux/actions/rewards';
import { selectEmailToVerify, selectUser } from "redux/selectors/user"; import { selectEmailToVerify } from 'redux/selectors/user';
import rewards from "rewards"; import rewards from 'rewards';
export function doFetchInviteStatus() {
return function(dispatch) {
dispatch({
type: ACTIONS.USER_INVITE_STATUS_FETCH_STARTED,
});
Lbryio.call('user', 'invite_status')
.then(status => {
dispatch({
type: ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS,
data: {
invitesRemaining: status.invites_remaining ? status.invites_remaining : 0,
invitees: status.invitees,
},
});
})
.catch(error => {
dispatch({
type: ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE,
data: { error },
});
});
};
}
export function doAuthenticate() { export function doAuthenticate() {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.AUTHENTICATION_STARTED, type: ACTIONS.AUTHENTICATION_STARTED,
}); });
lbryio Lbryio.authenticate()
.authenticate()
.then(user => { .then(user => {
dispatch({ dispatch({
type: types.AUTHENTICATION_SUCCESS, type: ACTIONS.AUTHENTICATION_SUCCESS,
data: { user }, data: { user },
}); });
dispatch(doRewardList()); dispatch(doRewardList());
dispatch(doFetchInviteStatus()); dispatch(doFetchInviteStatus());
}) })
.catch(error => { .catch(error => {
dispatch(doOpenModal(modals.AUTHENTICATION_FAILURE)); dispatch(doOpenModal(MODALS.AUTHENTICATION_FAILURE));
dispatch({ dispatch({
type: types.AUTHENTICATION_FAILURE, type: ACTIONS.AUTHENTICATION_FAILURE,
data: { error }, data: { error },
}); });
}); });
@ -32,23 +56,22 @@ export function doAuthenticate() {
} }
export function doUserFetch() { export function doUserFetch() {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.USER_FETCH_STARTED, type: ACTIONS.USER_FETCH_STARTED,
}); });
lbryio Lbryio.getCurrentUser()
.getCurrentUser()
.then(user => { .then(user => {
dispatch(doRewardList()); dispatch(doRewardList());
dispatch({ dispatch({
type: types.USER_FETCH_SUCCESS, type: ACTIONS.USER_FETCH_SUCCESS,
data: { user }, data: { user },
}); });
}) })
.catch(error => { .catch(error => {
dispatch({ dispatch({
type: types.USER_FETCH_FAILURE, type: ACTIONS.USER_FETCH_FAILURE,
data: { error }, data: { error },
}); });
}); });
@ -56,15 +79,15 @@ export function doUserFetch() {
} }
export function doUserEmailNew(email) { export function doUserEmailNew(email) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.USER_EMAIL_NEW_STARTED, type: ACTIONS.USER_EMAIL_NEW_STARTED,
email, email,
}); });
const success = () => { const success = () => {
dispatch({ dispatch({
type: types.USER_EMAIL_NEW_SUCCESS, type: ACTIONS.USER_EMAIL_NEW_SUCCESS,
data: { email }, data: { email },
}); });
dispatch(doUserFetch()); dispatch(doUserFetch());
@ -72,28 +95,20 @@ export function doUserEmailNew(email) {
const failure = error => { const failure = error => {
dispatch({ dispatch({
type: types.USER_EMAIL_NEW_FAILURE, type: ACTIONS.USER_EMAIL_NEW_FAILURE,
data: { error }, data: { error },
}); });
}; };
lbryio Lbryio.call('user_email', 'new', { email, send_verification_email: true }, 'post')
.call(
"user_email",
"new",
{ email, send_verification_email: true },
"post"
)
.catch(error => { .catch(error => {
if (error.response && error.response.status == 409) { if (error.response && error.response.status === 409) {
return lbryio return Lbryio.call(
.call( 'user_email',
"user_email", 'resend_token',
"resend_token", { email, only_if_expired: true },
{ email, only_if_expired: true }, 'post'
"post" ).then(success, failure);
)
.then(success, failure);
} }
throw error; throw error;
}) })
@ -104,35 +119,34 @@ export function doUserEmailNew(email) {
export function doUserEmailVerify(verificationToken) { export function doUserEmailVerify(verificationToken) {
return function(dispatch, getState) { return function(dispatch, getState) {
const email = selectEmailToVerify(getState()); const email = selectEmailToVerify(getState());
verificationToken = verificationToken.toString().trim(); const trimmedVerificationToken = verificationToken.toString().trim();
dispatch({ dispatch({
type: types.USER_EMAIL_VERIFY_STARTED, type: ACTIONS.USER_EMAIL_VERIFY_STARTED,
code: verificationToken, code: trimmedVerificationToken,
}); });
lbryio Lbryio.call(
.call( 'user_email',
"user_email", 'confirm',
"confirm", { verification_token: trimmedVerificationToken, email },
{ verification_token: verificationToken, email }, 'post'
"post" )
)
.then(userEmail => { .then(userEmail => {
if (userEmail.is_verified) { if (userEmail.is_verified) {
dispatch({ dispatch({
type: types.USER_EMAIL_VERIFY_SUCCESS, type: ACTIONS.USER_EMAIL_VERIFY_SUCCESS,
data: { email }, data: { email },
}); });
dispatch(doClaimRewardType(rewards.TYPE_CONFIRM_EMAIL)), dispatch(doClaimRewardType(rewards.TYPE_CONFIRM_EMAIL));
dispatch(doUserFetch()); dispatch(doUserFetch());
} else { } else {
throw new Error("Your email is still not verified."); // shouldn't happen throw new Error('Your email is still not verified.'); // shouldn't happen
} }
}) })
.catch(error => { .catch(error => {
dispatch({ dispatch({
type: types.USER_EMAIL_VERIFY_FAILURE, type: ACTIONS.USER_EMAIL_VERIFY_FAILURE,
data: { error }, data: { error },
}); });
}); });
@ -140,30 +154,27 @@ export function doUserEmailVerify(verificationToken) {
} }
export function doUserIdentityVerify(stripeToken) { export function doUserIdentityVerify(stripeToken) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.USER_IDENTITY_VERIFY_STARTED, type: ACTIONS.USER_IDENTITY_VERIFY_STARTED,
token: stripeToken, token: stripeToken,
}); });
lbryio Lbryio.call('user', 'verify_identity', { stripe_token: stripeToken }, 'post')
.call("user", "verify_identity", { stripe_token: stripeToken }, "post")
.then(user => { .then(user => {
if (user.is_identity_verified) { if (user.is_identity_verified) {
dispatch({ dispatch({
type: types.USER_IDENTITY_VERIFY_SUCCESS, type: ACTIONS.USER_IDENTITY_VERIFY_SUCCESS,
data: { user }, data: { user },
}); });
dispatch(doClaimRewardType(rewards.TYPE_NEW_USER)); dispatch(doClaimRewardType(rewards.TYPE_NEW_USER));
} else { } else {
throw new Error( throw new Error('Your identity is still not verified. This should not happen.'); // shouldn't happen
"Your identity is still not verified. This should not happen."
); // shouldn't happen
} }
}) })
.catch(error => { .catch(error => {
dispatch({ dispatch({
type: types.USER_IDENTITY_VERIFY_FAILURE, type: ACTIONS.USER_IDENTITY_VERIFY_FAILURE,
data: { error: error.toString() }, data: { error: error.toString() },
}); });
}); });
@ -171,61 +182,32 @@ export function doUserIdentityVerify(stripeToken) {
} }
export function doFetchAccessToken() { export function doFetchAccessToken() {
return function(dispatch, getState) { return function(dispatch) {
const success = token => const success = token =>
dispatch({ dispatch({
type: types.FETCH_ACCESS_TOKEN_SUCCESS, type: ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS,
data: { token }, data: { token },
}); });
lbryio.getAuthToken().then(success); Lbryio.getAuthToken().then(success);
};
}
export function doFetchInviteStatus() {
return function(dispatch, getState) {
dispatch({
type: types.USER_INVITE_STATUS_FETCH_STARTED,
});
lbryio
.call("user", "invite_status")
.then(status => {
dispatch({
type: types.USER_INVITE_STATUS_FETCH_SUCCESS,
data: {
invitesRemaining: status.invites_remaining
? status.invites_remaining
: 0,
invitees: status.invitees,
},
});
})
.catch(error => {
dispatch({
type: types.USER_INVITE_STATUS_FETCH_FAILURE,
data: { error },
});
});
}; };
} }
export function doUserInviteNew(email) { export function doUserInviteNew(email) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.USER_INVITE_NEW_STARTED, type: ACTIONS.USER_INVITE_NEW_STARTED,
}); });
lbryio Lbryio.call('user', 'invite', { email }, 'post')
.call("user", "invite", { email }, "post") .then(() => {
.then(invite => {
dispatch({ dispatch({
type: types.USER_INVITE_NEW_SUCCESS, type: ACTIONS.USER_INVITE_NEW_SUCCESS,
data: { email }, data: { email },
}); });
dispatch( dispatch(
doShowSnackBar({ doShowSnackBar({
message: __("Invite sent to %s", email), message: __('Invite sent to %s', email),
}) })
); );
@ -233,7 +215,7 @@ export function doUserInviteNew(email) {
}) })
.catch(error => { .catch(error => {
dispatch({ dispatch({
type: types.USER_INVITE_NEW_FAILURE, type: ACTIONS.USER_INVITE_NEW_FAILURE,
data: { error }, data: { error },
}); });
}); });

View file

@ -1,19 +1,19 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import lbry from "lbry"; import Lbry from 'lbry';
import { import {
selectDraftTransaction, selectDraftTransaction,
selectDraftTransactionAmount, selectDraftTransactionAmount,
selectBalance, selectBalance,
} from "redux/selectors/wallet"; } from 'redux/selectors/wallet';
import { doOpenModal, doShowSnackBar } from "redux/actions/app"; import { doOpenModal, doShowSnackBar } from 'redux/actions/app';
import { doNavigate } from "redux/actions/navigation"; import { doNavigate } from 'redux/actions/navigation';
import * as modals from "constants/modal_types"; import * as MODALS from 'constants/modal_types';
export function doUpdateBalance() { export function doUpdateBalance() {
return function(dispatch, getState) { return function(dispatch) {
lbry.wallet_balance().then(balance => Lbry.wallet_balance().then(balance =>
dispatch({ dispatch({
type: types.UPDATE_BALANCE, type: ACTIONS.UPDATE_BALANCE,
data: { data: {
balance, balance,
}, },
@ -23,36 +23,34 @@ export function doUpdateBalance() {
} }
export function doBalanceSubscribe() { export function doBalanceSubscribe() {
return function(dispatch, getState) { return function(dispatch) {
dispatch(doUpdateBalance()); dispatch(doUpdateBalance());
setInterval(() => dispatch(doUpdateBalance()), 5000); setInterval(() => dispatch(doUpdateBalance()), 5000);
}; };
} }
export function doFetchTransactions(fetch_tip_info = true) { export function doFetchTransactions(fetch_tip_info = true) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.FETCH_TRANSACTIONS_STARTED, type: ACTIONS.FETCH_TRANSACTIONS_STARTED,
}); });
lbry Lbry.transaction_list({ include_tip_info: fetch_tip_info }).then(results => {
.transaction_list({ include_tip_info: fetch_tip_info }) dispatch({
.then(results => { type: ACTIONS.FETCH_TRANSACTIONS_COMPLETED,
dispatch({ data: {
type: types.FETCH_TRANSACTIONS_COMPLETED, transactions: results,
data: { },
transactions: results,
},
});
}); });
});
}; };
} }
export function doFetchBlock(height) { export function doFetchBlock(height) {
return function(dispatch, getState) { return function(dispatch) {
lbry.block_show({ height }).then(block => { Lbry.block_show({ height }).then(block => {
dispatch({ dispatch({
type: types.FETCH_BLOCK_SUCCESS, type: ACTIONS.FETCH_BLOCK_SUCCESS,
data: { block }, data: { block },
}); });
}); });
@ -60,15 +58,15 @@ export function doFetchBlock(height) {
} }
export function doGetNewAddress() { export function doGetNewAddress() {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.GET_NEW_ADDRESS_STARTED, type: ACTIONS.GET_NEW_ADDRESS_STARTED,
}); });
lbry.wallet_new_address().then(address => { Lbry.wallet_new_address().then(address => {
localStorage.setItem("wallet_address", address); localStorage.setItem('wallet_address', address);
dispatch({ dispatch({
type: types.GET_NEW_ADDRESS_COMPLETED, type: ACTIONS.GET_NEW_ADDRESS_COMPLETED,
data: { address }, data: { address },
}); });
}); });
@ -76,16 +74,16 @@ export function doGetNewAddress() {
} }
export function doCheckAddressIsMine(address) { export function doCheckAddressIsMine(address) {
return function(dispatch, getState) { return function(dispatch) {
dispatch({ dispatch({
type: types.CHECK_ADDRESS_IS_MINE_STARTED, type: ACTIONS.CHECK_ADDRESS_IS_MINE_STARTED,
}); });
lbry.wallet_is_address_mine({ address }).then(isMine => { Lbry.wallet_is_address_mine({ address }).then(isMine => {
if (!isMine) dispatch(doGetNewAddress()); if (!isMine) dispatch(doGetNewAddress());
dispatch({ dispatch({
type: types.CHECK_ADDRESS_IS_MINE_COMPLETED, type: ACTIONS.CHECK_ADDRESS_IS_MINE_COMPLETED,
}); });
}); });
}; };
@ -99,113 +97,111 @@ export function doSendDraftTransaction() {
const amount = selectDraftTransactionAmount(state); const amount = selectDraftTransactionAmount(state);
if (balance - amount <= 0) { if (balance - amount <= 0) {
return dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS)); dispatch(doOpenModal(MODALS.INSUFFICIENT_CREDITS));
return;
} }
dispatch({ dispatch({
type: types.SEND_TRANSACTION_STARTED, type: ACTIONS.SEND_TRANSACTION_STARTED,
}); });
const successCallback = results => { const successCallback = results => {
if (results === true) { if (results === true) {
dispatch({ dispatch({
type: types.SEND_TRANSACTION_COMPLETED, type: ACTIONS.SEND_TRANSACTION_COMPLETED,
}); });
dispatch( dispatch(
doShowSnackBar({ doShowSnackBar({
message: __(`You sent ${amount} LBC`), message: __(`You sent ${amount} LBC`),
linkText: __("History"), linkText: __('History'),
linkTarget: __("/wallet"), linkTarget: __('/wallet'),
}) })
); );
} else { } else {
dispatch({ dispatch({
type: types.SEND_TRANSACTION_FAILED, type: ACTIONS.SEND_TRANSACTION_FAILED,
data: { error: results }, data: { error: results },
}); });
dispatch(doOpenModal(modals.TRANSACTION_FAILED)); dispatch(doOpenModal(MODALS.TRANSACTION_FAILED));
} }
}; };
const errorCallback = error => { const errorCallback = error => {
dispatch({ dispatch({
type: types.SEND_TRANSACTION_FAILED, type: ACTIONS.SEND_TRANSACTION_FAILED,
data: { error: error.message }, data: { error: error.message },
}); });
dispatch(doOpenModal(modals.TRANSACTION_FAILED)); dispatch(doOpenModal(MODALS.TRANSACTION_FAILED));
}; };
lbry Lbry.wallet_send({
.wallet_send({ amount: draftTx.amount,
amount: draftTx.amount, address: draftTx.address,
address: draftTx.address, }).then(successCallback, errorCallback);
})
.then(successCallback, errorCallback);
}; };
} }
export function doSetDraftTransactionAmount(amount) { export function doSetDraftTransactionAmount(amount) {
return { return {
type: types.SET_DRAFT_TRANSACTION_AMOUNT, type: ACTIONS.SET_DRAFT_TRANSACTION_AMOUNT,
data: { amount }, data: { amount },
}; };
} }
export function doSetDraftTransactionAddress(address) { export function doSetDraftTransactionAddress(address) {
return { return {
type: types.SET_DRAFT_TRANSACTION_ADDRESS, type: ACTIONS.SET_DRAFT_TRANSACTION_ADDRESS,
data: { address }, data: { address },
}; };
} }
export function doSendSupport(amount, claim_id, uri) { export function doSendSupport(amount, claimId, uri) {
return function(dispatch, getState) { return function(dispatch, getState) {
const state = getState(); const state = getState();
const balance = selectBalance(state); const balance = selectBalance(state);
if (balance - amount <= 0) { if (balance - amount <= 0) {
return dispatch(doOpenModal(modals.INSUFFICIENT_CREDITS)); dispatch(doOpenModal(MODALS.INSUFFICIENT_CREDITS));
return;
} }
dispatch({ dispatch({
type: types.SUPPORT_TRANSACTION_STARTED, type: ACTIONS.SUPPORT_TRANSACTION_STARTED,
}); });
const successCallback = results => { const successCallback = results => {
if (results.txid) { if (results.txid) {
dispatch({ dispatch({
type: types.SUPPORT_TRANSACTION_COMPLETED, type: ACTIONS.SUPPORT_TRANSACTION_COMPLETED,
}); });
dispatch( dispatch(
doShowSnackBar({ doShowSnackBar({
message: __(`You sent ${amount} LBC as support, Mahalo!`), message: __(`You sent ${amount} LBC as support, Mahalo!`),
linkText: __("History"), linkText: __('History'),
linkTarget: __("/wallet"), linkTarget: __('/wallet'),
}) })
); );
dispatch(doNavigate("/show", { uri })); dispatch(doNavigate('/show', { uri }));
} else { } else {
dispatch({ dispatch({
type: types.SUPPORT_TRANSACTION_FAILED, type: ACTIONS.SUPPORT_TRANSACTION_FAILED,
data: { error: results.code }, data: { error: results.code },
}); });
dispatch(doOpenModal(modals.TRANSACTION_FAILED)); dispatch(doOpenModal(MODALS.TRANSACTION_FAILED));
} }
}; };
const errorCallback = error => { const errorCallback = error => {
dispatch({ dispatch({
type: types.SUPPORT_TRANSACTION_FAILED, type: ACTIONS.SUPPORT_TRANSACTION_FAILED,
data: { error: error.code }, data: { error: error.code },
}); });
dispatch(doOpenModal(modals.TRANSACTION_FAILED)); dispatch(doOpenModal(MODALS.TRANSACTION_FAILED));
}; };
lbry Lbry.wallet_send({
.wallet_send({ claimId,
claim_id, amount,
amount, }).then(successCallback, errorCallback);
})
.then(successCallback, errorCallback);
}; };
} }

View file

@ -1,11 +1,10 @@
// @flow // @flow
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import * as modalTypes from "constants/modal_types"; import * as MODALS from 'constants/modal_types';
const { remote } = require("electron"); const { remote } = require('electron');
const application = remote.app;
const win = remote.BrowserWindow.getFocusedWindow(); const win = remote.BrowserWindow.getFocusedWindow();
const reducers = {}; const reducers = {};
@ -41,12 +40,12 @@ const defaultState: AppState = {
modal: null, modal: null,
modalProps: {}, modalProps: {},
platform: process.platform, platform: process.platform,
upgradeSkipped: sessionStorage.getItem("upgradeSkipped") === "true", upgradeSkipped: sessionStorage.getItem('upgradeSkipped') === 'true',
daemonVersionMatched: null, daemonVersionMatched: null,
daemonReady: false, daemonReady: false,
hasSignature: false, hasSignature: false,
badgeNumber: 0, badgeNumber: 0,
volume: Number(sessionStorage.getItem("volume")) || 1, volume: Number(sessionStorage.getItem('volume')) || 1,
downloadProgress: undefined, downloadProgress: undefined,
upgradeDownloading: undefined, upgradeDownloading: undefined,
@ -57,26 +56,26 @@ const defaultState: AppState = {
snackBar: undefined, snackBar: undefined,
}; };
reducers[types.DAEMON_READY] = function(state, action) { reducers[ACTIONS.DAEMON_READY] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
daemonReady: true, daemonReady: true,
}); });
}; };
reducers[types.DAEMON_VERSION_MATCH] = function(state, action) { reducers[ACTIONS.DAEMON_VERSION_MATCH] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
daemonVersionMatched: true, daemonVersionMatched: true,
}); });
}; };
reducers[types.DAEMON_VERSION_MISMATCH] = function(state, action) { reducers[ACTIONS.DAEMON_VERSION_MISMATCH] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
daemonVersionMatched: false, daemonVersionMatched: false,
modal: modalTypes.INCOMPATIBLE_DAEMON, modal: MODALS.INCOMPATIBLE_DAEMON,
}); });
}; };
reducers[types.UPGRADE_CANCELLED] = function(state, action) { reducers[ACTIONS.UPGRADE_CANCELLED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
downloadProgress: null, downloadProgress: null,
upgradeDownloadComplete: false, upgradeDownloadComplete: false,
@ -84,7 +83,7 @@ reducers[types.UPGRADE_CANCELLED] = function(state, action) {
}); });
}; };
reducers[types.UPGRADE_DOWNLOAD_COMPLETED] = function(state, action) { reducers[ACTIONS.UPGRADE_DOWNLOAD_COMPLETED] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
downloadPath: action.data.path, downloadPath: action.data.path,
upgradeDownloading: false, upgradeDownloading: false,
@ -92,14 +91,14 @@ reducers[types.UPGRADE_DOWNLOAD_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.UPGRADE_DOWNLOAD_STARTED] = function(state, action) { reducers[ACTIONS.UPGRADE_DOWNLOAD_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
upgradeDownloading: true, upgradeDownloading: true,
}); });
}; };
reducers[types.SKIP_UPGRADE] = function(state, action) { reducers[ACTIONS.SKIP_UPGRADE] = function(state) {
sessionStorage.setItem("upgradeSkipped", "true"); sessionStorage.setItem('upgradeSkipped', 'true');
return Object.assign({}, state, { return Object.assign({}, state, {
isUpgradeSkipped: true, isUpgradeSkipped: true,
@ -107,46 +106,46 @@ reducers[types.SKIP_UPGRADE] = function(state, action) {
}); });
}; };
reducers[types.UPDATE_VERSION] = function(state, action) { reducers[ACTIONS.UPDATE_VERSION] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
version: action.data.version, version: action.data.version,
}); });
}; };
reducers[types.CHECK_UPGRADE_SUCCESS] = function(state, action) { reducers[ACTIONS.CHECK_UPGRADE_SUCCESS] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
isUpgradeAvailable: action.data.upgradeAvailable, isUpgradeAvailable: action.data.upgradeAvailable,
remoteVersion: action.data.remoteVersion, remoteVersion: action.data.remoteVersion,
}); });
}; };
reducers[types.CHECK_UPGRADE_SUBSCRIBE] = function(state, action) { reducers[ACTIONS.CHECK_UPGRADE_SUBSCRIBE] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
checkUpgradeTimer: action.data.checkUpgradeTimer, checkUpgradeTimer: action.data.checkUpgradeTimer,
}); });
}; };
reducers[types.OPEN_MODAL] = function(state, action) { reducers[ACTIONS.OPEN_MODAL] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
modal: action.data.modal, modal: action.data.modal,
modalProps: action.data.modalProps || {}, modalProps: action.data.modalProps || {},
}); });
}; };
reducers[types.CLOSE_MODAL] = function(state, action) { reducers[ACTIONS.CLOSE_MODAL] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
modal: undefined, modal: undefined,
modalProps: {}, modalProps: {},
}); });
}; };
reducers[types.UPGRADE_DOWNLOAD_PROGRESSED] = function(state, action) { reducers[ACTIONS.UPGRADE_DOWNLOAD_PROGRESSED] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
downloadProgress: action.data.percent, downloadProgress: action.data.percent,
}); });
}; };
reducers[types.SHOW_SNACKBAR] = function(state, action) { reducers[ACTIONS.SHOW_SNACKBAR] = function(state, action) {
const { message, linkText, linkTarget, isError } = action.data; const { message, linkText, linkTarget, isError } = action.data;
const snackBar = Object.assign({}, state.snackBar); const snackBar = Object.assign({}, state.snackBar);
const snacks = Object.assign([], snackBar.snacks); const snacks = Object.assign([], snackBar.snacks);
@ -165,7 +164,7 @@ reducers[types.SHOW_SNACKBAR] = function(state, action) {
}); });
}; };
reducers[types.REMOVE_SNACKBAR_SNACK] = function(state, action) { reducers[ACTIONS.REMOVE_SNACKBAR_SNACK] = function(state) {
const snackBar = Object.assign({}, state.snackBar); const snackBar = Object.assign({}, state.snackBar);
const snacks = Object.assign([], snackBar.snacks); const snacks = Object.assign([], snackBar.snacks);
snacks.shift(); snacks.shift();
@ -179,8 +178,8 @@ reducers[types.REMOVE_SNACKBAR_SNACK] = function(state, action) {
}); });
}; };
reducers[types.DOWNLOADING_COMPLETED] = function(state, action) { reducers[ACTIONS.DOWNLOADING_COMPLETED] = function(state) {
const badgeNumber = state.badgeNumber; const { badgeNumber } = state;
// Don't update the badge number if the window is focused // Don't update the badge number if the window is focused
if (win && win.isFocused()) return Object.assign({}, state); if (win && win.isFocused()) return Object.assign({}, state);
@ -190,13 +189,13 @@ reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.WINDOW_FOCUSED] = function(state, action) { reducers[ACTIONS.WINDOW_FOCUSED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
badgeNumber: 0, badgeNumber: 0,
}); });
}; };
reducers[types.VOLUME_CHANGED] = function(state, action) { reducers[ACTIONS.VOLUME_CHANGED] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
volume: action.data.volume, volume: action.data.volume,
}); });

View file

@ -1,9 +1,9 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
const reducers = {}; const reducers = {};
const defaultState = {}; const defaultState = {};
reducers[types.FETCH_AVAILABILITY_STARTED] = function(state, action) { reducers[ACTIONS.FETCH_AVAILABILITY_STARTED] = function(state, action) {
const { uri } = action.data; const { uri } = action.data;
const newFetching = Object.assign({}, state.fetching); const newFetching = Object.assign({}, state.fetching);
@ -14,7 +14,7 @@ reducers[types.FETCH_AVAILABILITY_STARTED] = function(state, action) {
}); });
}; };
reducers[types.FETCH_AVAILABILITY_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_AVAILABILITY_COMPLETED] = function(state, action) {
const { uri, availability } = action.data; const { uri, availability } = action.data;
const newFetching = Object.assign({}, state.fetching); const newFetching = Object.assign({}, state.fetching);

View file

@ -1,15 +1,15 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
const reducers = {}; const reducers = {};
const defaultState = {}; const defaultState = {};
reducers[types.RESOLVE_URIS_COMPLETED] = function(state, action) { reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = function(state, action) {
const { resolveInfo } = action.data; const { resolveInfo } = action.data;
const byUri = Object.assign({}, state.claimsByUri); const byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId); const byId = Object.assign({}, state.byId);
for (const [uri, { certificate, claim }] of Object.entries(resolveInfo)) { Object.entries(resolveInfo).forEach(([uri, { certificate, claim }]) => {
if (claim) { if (claim) {
byId[claim.claim_id] = claim; byId[claim.claim_id] = claim;
byUri[uri] = claim.claim_id; byUri[uri] = claim.claim_id;
@ -25,7 +25,7 @@ reducers[types.RESOLVE_URIS_COMPLETED] = function(state, action) {
} else { } else {
byUri[uri] = null; byUri[uri] = null;
} }
} });
return Object.assign({}, state, { return Object.assign({}, state, {
byId, byId,
@ -33,37 +33,32 @@ reducers[types.RESOLVE_URIS_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.FETCH_CLAIM_LIST_MINE_STARTED] = function(state, action) { reducers[ACTIONS.FETCH_CLAIM_LIST_MINE_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
isFetchingClaimListMine: true, isFetchingClaimListMine: true,
}); });
}; };
reducers[types.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
const { claims } = action.data; const { claims } = action.data;
const byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId); const byId = Object.assign({}, state.byId);
const pendingById = Object.assign({}, state.pendingById); const pendingById = Object.assign({}, state.pendingById);
const abandoningById = Object.assign({}, state.abandoningById);
claims claims.filter(claim => claim.category && claim.category.match(/claim/)).forEach(claim => {
.filter(claim => claim.category && claim.category.match(/claim/)) byId[claim.claim_id] = claim;
.forEach(claim => {
byId[claim.claim_id] = claim;
const pending = Object.values(pendingById).find( const pending = Object.values(pendingById).find(
pendingClaim => pendingClaim =>
pendingClaim.name == claim.name && pendingClaim.name === claim.name && pendingClaim.channel_name === claim.channel_name
pendingClaim.channel_name == claim.channel_name );
);
if (pending) { if (pending) {
delete pendingById[pending.claim_id]; delete pendingById[pending.claim_id];
} }
}); });
// Remove old timed out pending publishes // Remove old timed out pending publishes
const old = Object.values(pendingById) Object.values(pendingById)
.filter(pendingClaim => Date.now() - pendingClaim.time >= 20 * 60 * 1000) .filter(pendingClaim => Date.now() - pendingClaim.time >= 20 * 60 * 1000)
.forEach(pendingClaim => { .forEach(pendingClaim => {
delete pendingById[pendingClaim.claim_id]; delete pendingById[pendingClaim.claim_id];
@ -77,11 +72,11 @@ reducers[types.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.FETCH_CHANNEL_LIST_MINE_STARTED] = function(state, action) { reducers[ACTIONS.FETCH_CHANNEL_LIST_MINE_STARTED] = function(state) {
return Object.assign({}, state, { fetchingMyChannels: true }); return Object.assign({}, state, { fetchingMyChannels: true });
}; };
reducers[types.FETCH_CHANNEL_LIST_MINE_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_CHANNEL_LIST_MINE_COMPLETED] = function(state, action) {
const { claims } = action.data; const { claims } = action.data;
const myChannelClaims = new Set(state.myChannelClaims); const myChannelClaims = new Set(state.myChannelClaims);
const byId = Object.assign({}, state.byId); const byId = Object.assign({}, state.byId);
@ -98,7 +93,7 @@ reducers[types.FETCH_CHANNEL_LIST_MINE_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.FETCH_CHANNEL_CLAIMS_STARTED] = function(state, action) { reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED] = function(state, action) {
const { uri, page } = action.data; const { uri, page } = action.data;
const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims); const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims);
@ -109,7 +104,7 @@ reducers[types.FETCH_CHANNEL_CLAIMS_STARTED] = function(state, action) {
}); });
}; };
reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
const { uri, claims, page } = action.data; const { uri, claims, page } = action.data;
const claimsByChannel = Object.assign({}, state.claimsByChannel); const claimsByChannel = Object.assign({}, state.claimsByChannel);
@ -139,7 +134,7 @@ reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.ABANDON_CLAIM_STARTED] = function(state, action) { reducers[ACTIONS.ABANDON_CLAIM_STARTED] = function(state, action) {
const { claimId } = action.data; const { claimId } = action.data;
const abandoningById = Object.assign({}, state.abandoningById); const abandoningById = Object.assign({}, state.abandoningById);
@ -150,7 +145,7 @@ reducers[types.ABANDON_CLAIM_STARTED] = function(state, action) {
}); });
}; };
reducers[types.ABANDON_CLAIM_SUCCEEDED] = function(state, action) { reducers[ACTIONS.ABANDON_CLAIM_SUCCEEDED] = function(state, action) {
const { claimId } = action.data; const { claimId } = action.data;
const byId = Object.assign({}, state.byId); const byId = Object.assign({}, state.byId);
const claimsByUri = Object.assign({}, state.claimsByUri); const claimsByUri = Object.assign({}, state.claimsByUri);
@ -169,7 +164,7 @@ reducers[types.ABANDON_CLAIM_SUCCEEDED] = function(state, action) {
}); });
}; };
reducers[types.CREATE_CHANNEL_COMPLETED] = function(state, action) { reducers[ACTIONS.CREATE_CHANNEL_COMPLETED] = function(state, action) {
const { channelClaim } = action.data; const { channelClaim } = action.data;
const byId = Object.assign({}, state.byId); const byId = Object.assign({}, state.byId);
const myChannelClaims = new Set(state.myChannelClaims); const myChannelClaims = new Set(state.myChannelClaims);

View file

@ -1,4 +1,4 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
const reducers = {}; const reducers = {};
const defaultState = { const defaultState = {
@ -7,13 +7,13 @@ const defaultState = {
channelClaimCounts: {}, channelClaimCounts: {},
}; };
reducers[types.FETCH_FEATURED_CONTENT_STARTED] = function(state, action) { reducers[ACTIONS.FETCH_FEATURED_CONTENT_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
fetchingFeaturedContent: true, fetchingFeaturedContent: true,
}); });
}; };
reducers[types.FETCH_FEATURED_CONTENT_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED] = function(state, action) {
const { uris, success } = action.data; const { uris, success } = action.data;
return Object.assign({}, state, { return Object.assign({}, state, {
@ -23,42 +23,40 @@ reducers[types.FETCH_FEATURED_CONTENT_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.FETCH_REWARD_CONTENT_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_REWARD_CONTENT_COMPLETED] = function(state, action) {
const { claimIds, success } = action.data; const { claimIds } = action.data;
return Object.assign({}, state, { return Object.assign({}, state, {
rewardedContentClaimIds: claimIds, rewardedContentClaimIds: claimIds,
}); });
}; };
reducers[types.RESOLVE_URIS_STARTED] = function(state, action) { reducers[ACTIONS.RESOLVE_URIS_STARTED] = function(state, action) {
const { uris } = action.data; const { uris } = action.data;
const oldResolving = state.resolvingUris || []; const oldResolving = state.resolvingUris || [];
const newResolving = Object.assign([], oldResolving); const newResolving = Object.assign([], oldResolving);
for (const uri of uris) { uris.forEach(uri => {
if (!newResolving.includes(uri)) { if (!newResolving.includes(uri)) {
newResolving.push(uri); newResolving.push(uri);
} }
} });
return Object.assign({}, state, { return Object.assign({}, state, {
resolvingUris: newResolving, resolvingUris: newResolving,
}); });
}; };
reducers[types.RESOLVE_URIS_COMPLETED] = function(state, action) { reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = function(state, action) {
const { resolveInfo } = action.data; const { resolveInfo } = action.data;
const channelClaimCounts = Object.assign({}, state.channelClaimCounts); const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
for (const [uri, { certificate, claims_in_channel }] of Object.entries( Object.entries(resolveInfo).forEach(([uri, { certificate, claimsInChannel }]) => {
resolveInfo if (certificate && !Number.isNaN(claimsInChannel)) {
)) { channelClaimCounts[uri] = claimsInChannel;
if (certificate && !isNaN(claims_in_channel)) {
channelClaimCounts[uri] = claims_in_channel;
} }
} });
return Object.assign({}, state, { return Object.assign({}, state, {
channelClaimCounts, channelClaimCounts,
@ -66,12 +64,12 @@ reducers[types.RESOLVE_URIS_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.SET_PLAYING_URI] = (state, action) => reducers[ACTIONS.SET_PLAYING_URI] = (state, action) =>
Object.assign({}, state, { Object.assign({}, state, {
playingUri: action.data.uri, playingUri: action.data.uri,
}); });
reducers[types.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED] = function(state, action) {
const channelClaimCounts = Object.assign({}, state.channelClaimCounts); const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
const { uri, totalClaims } = action.data; const { uri, totalClaims } = action.data;

View file

@ -1,9 +1,9 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
const reducers = {}; const reducers = {};
const defaultState = {}; const defaultState = {};
reducers[types.FETCH_COST_INFO_STARTED] = function(state, action) { reducers[ACTIONS.FETCH_COST_INFO_STARTED] = function(state, action) {
const { uri } = action.data; const { uri } = action.data;
const newFetching = Object.assign({}, state.fetching); const newFetching = Object.assign({}, state.fetching);
newFetching[uri] = true; newFetching[uri] = true;
@ -13,7 +13,7 @@ reducers[types.FETCH_COST_INFO_STARTED] = function(state, action) {
}); });
}; };
reducers[types.FETCH_COST_INFO_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_COST_INFO_COMPLETED] = function(state, action) {
const { uri, costInfo } = action.data; const { uri, costInfo } = action.data;
const newByUri = Object.assign({}, state.byUri); const newByUri = Object.assign({}, state.byUri);
const newFetching = Object.assign({}, state.fetching); const newFetching = Object.assign({}, state.fetching);

View file

@ -1,16 +1,15 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import lbryuri from "lbryuri";
const reducers = {}; const reducers = {};
const defaultState = {}; const defaultState = {};
reducers[types.FILE_LIST_STARTED] = function(state, action) { reducers[ACTIONS.FILE_LIST_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
isFetchingFileList: true, isFetchingFileList: true,
}); });
}; };
reducers[types.FILE_LIST_SUCCEEDED] = function(state, action) { reducers[ACTIONS.FILE_LIST_SUCCEEDED] = function(state, action) {
const { fileInfos } = action.data; const { fileInfos } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); const newByOutpoint = Object.assign({}, state.byOutpoint);
const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint); const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
@ -28,7 +27,7 @@ reducers[types.FILE_LIST_SUCCEEDED] = function(state, action) {
}); });
}; };
reducers[types.FETCH_FILE_INFO_STARTED] = function(state, action) { reducers[ACTIONS.FETCH_FILE_INFO_STARTED] = function(state, action) {
const { outpoint } = action.data; const { outpoint } = action.data;
const newFetching = Object.assign({}, state.fetching); const newFetching = Object.assign({}, state.fetching);
@ -39,7 +38,7 @@ reducers[types.FETCH_FILE_INFO_STARTED] = function(state, action) {
}); });
}; };
reducers[types.FETCH_FILE_INFO_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
const { fileInfo, outpoint } = action.data; const { fileInfo, outpoint } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); const newByOutpoint = Object.assign({}, state.byOutpoint);
@ -54,7 +53,7 @@ reducers[types.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.DOWNLOADING_STARTED] = function(state, action) { reducers[ACTIONS.DOWNLOADING_STARTED] = function(state, action) {
const { uri, outpoint, fileInfo } = action.data; const { uri, outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); const newByOutpoint = Object.assign({}, state.byOutpoint);
@ -72,8 +71,8 @@ reducers[types.DOWNLOADING_STARTED] = function(state, action) {
}); });
}; };
reducers[types.DOWNLOADING_PROGRESSED] = function(state, action) { reducers[ACTIONS.DOWNLOADING_PROGRESSED] = function(state, action) {
const { uri, outpoint, fileInfo } = action.data; const { outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); const newByOutpoint = Object.assign({}, state.byOutpoint);
const newDownloading = Object.assign({}, state.downloadingByOutpoint); const newDownloading = Object.assign({}, state.downloadingByOutpoint);
@ -87,8 +86,8 @@ reducers[types.DOWNLOADING_PROGRESSED] = function(state, action) {
}); });
}; };
reducers[types.DOWNLOADING_COMPLETED] = function(state, action) { reducers[ACTIONS.DOWNLOADING_COMPLETED] = function(state, action) {
const { uri, outpoint, fileInfo } = action.data; const { outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); const newByOutpoint = Object.assign({}, state.byOutpoint);
const newDownloading = Object.assign({}, state.downloadingByOutpoint); const newDownloading = Object.assign({}, state.downloadingByOutpoint);
@ -102,7 +101,7 @@ reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.FILE_DELETE] = function(state, action) { reducers[ACTIONS.FILE_DELETE] = function(state, action) {
const { outpoint } = action.data; const { outpoint } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); const newByOutpoint = Object.assign({}, state.byOutpoint);
@ -117,7 +116,7 @@ reducers[types.FILE_DELETE] = function(state, action) {
}); });
}; };
reducers[types.LOADING_VIDEO_STARTED] = function(state, action) { reducers[ACTIONS.LOADING_VIDEO_STARTED] = function(state, action) {
const { uri } = action.data; const { uri } = action.data;
const newLoading = Object.assign({}, state.urisLoading); const newLoading = Object.assign({}, state.urisLoading);
@ -129,7 +128,7 @@ reducers[types.LOADING_VIDEO_STARTED] = function(state, action) {
}); });
}; };
reducers[types.LOADING_VIDEO_FAILED] = function(state, action) { reducers[ACTIONS.LOADING_VIDEO_FAILED] = function(state, action) {
const { uri } = action.data; const { uri } = action.data;
const newLoading = Object.assign({}, state.urisLoading); const newLoading = Object.assign({}, state.urisLoading);
@ -141,13 +140,14 @@ reducers[types.LOADING_VIDEO_FAILED] = function(state, action) {
}); });
}; };
reducers[types.FETCH_DATE] = function(state, action) { reducers[ACTIONS.FETCH_DATE] = function(state, action) {
const { time } = action.data; const { time } = action.data;
if (time) { if (time) {
return Object.assign({}, state, { return Object.assign({}, state, {
publishedDate: time, publishedDate: time,
}); });
} }
return null;
}; };
export default function reducer(state = defaultState, action) { export default function reducer(state = defaultState, action) {

View file

@ -1,38 +1,36 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import { parseQueryParams } from "util/query_params";
const currentPath = () => { const getCurrentPath = () => {
const hash = document.location.hash; const { hash } = document.location;
if (hash !== "") return hash.replace(/^#/, ""); if (hash !== '') return hash.replace(/^#/, '');
return "/discover"; return '/discover';
}; };
const reducers = {}; const reducers = {};
const defaultState = { const defaultState = {
currentPath: currentPath(), currentPath: getCurrentPath(),
pathAfterAuth: "/discover", pathAfterAuth: '/discover',
index: 0, index: 0,
stack: [], stack: [],
}; };
reducers[types.DAEMON_READY] = function(state, action) { reducers[ACTIONS.DAEMON_READY] = function(state) {
const { currentPath } = state; const { currentPath } = state;
const params = parseQueryParams(currentPath.split("?")[1] || "");
return Object.assign({}, state, { return Object.assign({}, state, {
stack: [{ path: currentPath, scrollY: 0 }], stack: [{ path: currentPath, scrollY: 0 }],
}); });
}; };
reducers[types.CHANGE_AFTER_AUTH_PATH] = function(state, action) { reducers[ACTIONS.CHANGE_AFTER_AUTH_PATH] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
pathAfterAuth: action.data.path, pathAfterAuth: action.data.path,
}); });
}; };
reducers[types.HISTORY_NAVIGATE] = (state, action) => { reducers[ACTIONS.HISTORY_NAVIGATE] = (state, action) => {
const { stack, index } = state; const { stack, index } = state;
const { url: path, index: newIndex, scrollY } = action.data; const { url: path, index: newIndex } = action.data;
const newState = { const newState = {
currentPath: path, currentPath: path,
@ -46,12 +44,11 @@ reducers[types.HISTORY_NAVIGATE] = (state, action) => {
newState.index = newState.stack.length - 1; newState.index = newState.stack.length - 1;
} }
history.replaceState(null, null, `#${path}`); // this allows currentPath() to retain the URL on reload
return Object.assign({}, state, newState); return Object.assign({}, state, newState);
}; };
reducers[types.WINDOW_SCROLLED] = (state, action) => { reducers[ACTIONS.WINDOW_SCROLLED] = (state, action) => {
const { stack, index } = state; const { index } = state;
const { scrollY } = action.data; const { scrollY } = action.data;
return Object.assign({}, state, { return Object.assign({}, state, {

View file

@ -1,4 +1,4 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
const reducers = {}; const reducers = {};
const defaultState = { const defaultState = {
@ -9,17 +9,17 @@ const defaultState = {
claimErrorsByType: {}, claimErrorsByType: {},
}; };
reducers[types.FETCH_REWARDS_STARTED] = function(state, action) { reducers[ACTIONS.FETCH_REWARDS_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
fetching: true, fetching: true,
}); });
}; };
reducers[types.FETCH_REWARDS_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = function(state, action) {
const { userRewards } = action.data; const { userRewards } = action.data;
let unclaimedRewards = {}, const unclaimedRewards = {};
claimedRewards = {}; const claimedRewards = {};
userRewards.forEach(reward => { userRewards.forEach(reward => {
if (reward.transaction_id) { if (reward.transaction_id) {
claimedRewards[reward.id] = reward; claimedRewards[reward.id] = reward;
@ -35,7 +35,7 @@ reducers[types.FETCH_REWARDS_COMPLETED] = function(state, action) {
}); });
}; };
function setClaimRewardState(state, reward, isClaiming, errorMessage = "") { function setClaimRewardState(state, reward, isClaiming, errorMessage = '') {
const newClaimPendingByType = Object.assign({}, state.claimPendingByType); const newClaimPendingByType = Object.assign({}, state.claimPendingByType);
const newClaimErrorsByType = Object.assign({}, state.claimErrorsByType); const newClaimErrorsByType = Object.assign({}, state.claimErrorsByType);
if (isClaiming) { if (isClaiming) {
@ -55,21 +55,17 @@ function setClaimRewardState(state, reward, isClaiming, errorMessage = "") {
}); });
} }
reducers[types.CLAIM_REWARD_STARTED] = function(state, action) { reducers[ACTIONS.CLAIM_REWARD_STARTED] = function(state, action) {
const { reward } = action.data; const { reward } = action.data;
return setClaimRewardState(state, reward, true, ""); return setClaimRewardState(state, reward, true, '');
}; };
reducers[types.CLAIM_REWARD_SUCCESS] = function(state, action) { reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = function(state, action) {
const { reward } = action.data; const { reward } = action.data;
const unclaimedRewardsByType = Object.assign( const unclaimedRewardsByType = Object.assign({}, state.unclaimedRewardsByType);
{},
state.unclaimedRewardsByType
);
const existingReward = unclaimedRewardsByType[reward.reward_type]; const existingReward = unclaimedRewardsByType[reward.reward_type];
delete state.unclaimedRewardsByType[reward.reward_type];
const newReward = Object.assign({}, reward, { const newReward = Object.assign({}, reward, {
reward_title: existingReward.reward_title, reward_title: existingReward.reward_title,
@ -84,24 +80,19 @@ reducers[types.CLAIM_REWARD_SUCCESS] = function(state, action) {
claimedRewardsById, claimedRewardsById,
}); });
return setClaimRewardState(newState, newReward, false, ""); return setClaimRewardState(newState, newReward, false, '');
}; };
reducers[types.CLAIM_REWARD_FAILURE] = function(state, action) { reducers[ACTIONS.CLAIM_REWARD_FAILURE] = function(state, action) {
const { reward, error } = action.data; const { reward, error } = action.data;
return setClaimRewardState(state, reward, false, error ? error.message : ""); return setClaimRewardState(state, reward, false, error ? error.message : '');
}; };
reducers[types.CLAIM_REWARD_CLEAR_ERROR] = function(state, action) { reducers[ACTIONS.CLAIM_REWARD_CLEAR_ERROR] = function(state, action) {
const { reward } = action.data; const { reward } = action.data;
return setClaimRewardState( return setClaimRewardState(state, reward, state.claimPendingByType[reward.reward_type], '');
state,
reward,
state.claimPendingByType[reward.reward_type],
""
);
}; };
export default function reducer(state = defaultState, action) { export default function reducer(state = defaultState, action) {

View file

@ -1,4 +1,4 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
const reducers = {}; const reducers = {};
const defaultState = { const defaultState = {
@ -6,15 +6,13 @@ const defaultState = {
searching: false, searching: false,
}; };
reducers[types.SEARCH_STARTED] = function(state, action) { reducers[ACTIONS.SEARCH_STARTED] = function(state) {
const { query } = action.data;
return Object.assign({}, state, { return Object.assign({}, state, {
searching: true, searching: true,
}); });
}; };
reducers[types.SEARCH_COMPLETED] = function(state, action) { reducers[ACTIONS.SEARCH_COMPLETED] = function(state, action) {
const { query, uris } = action.data; const { query, uris } = action.data;
return Object.assign({}, state, { return Object.assign({}, state, {
@ -23,7 +21,7 @@ reducers[types.SEARCH_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.SEARCH_CANCELLED] = function(state, action) { reducers[ACTIONS.SEARCH_CANCELLED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
searching: false, searching: false,
}); });

View file

@ -1,6 +1,6 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import * as settings from "constants/settings"; import * as SETTINGS from 'constants/settings';
import LANGUAGES from "constants/languages"; import LANGUAGES from 'constants/languages';
function getLocalStorageSetting(setting, fallback) { function getLocalStorageSetting(setting, fallback) {
const localStorageVal = localStorage.getItem(`setting_${setting}`); const localStorageVal = localStorage.getItem(`setting_${setting}`);
@ -10,38 +10,30 @@ function getLocalStorageSetting(setting, fallback) {
const reducers = {}; const reducers = {};
const defaultState = { const defaultState = {
clientSettings: { clientSettings: {
instantPurchaseEnabled: getLocalStorageSetting( instantPurchaseEnabled: getLocalStorageSetting(SETTINGS.INSTANT_PURCHASE_ENABLED, false),
settings.INSTANT_PURCHASE_ENABLED, instantPurchaseMax: getLocalStorageSetting(SETTINGS.INSTANT_PURCHASE_MAX, {
false currency: 'LBC',
),
instantPurchaseMax: getLocalStorageSetting(settings.INSTANT_PURCHASE_MAX, {
currency: "LBC",
amount: 0.1, amount: 0.1,
}), }),
showNsfw: getLocalStorageSetting(settings.SHOW_NSFW, false), showNsfw: getLocalStorageSetting(SETTINGS.SHOW_NSFW, false),
showUnavailable: getLocalStorageSetting(settings.SHOW_UNAVAILABLE, true), showUnavailable: getLocalStorageSetting(SETTINGS.SHOW_UNAVAILABLE, true),
welcome_acknowledged: getLocalStorageSetting( welcome_acknowledged: getLocalStorageSetting(SETTINGS.NEW_USER_ACKNOWLEDGED, false),
settings.NEW_USER_ACKNOWLEDGED, email_collection_acknowledged: getLocalStorageSetting(SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED),
false
),
email_collection_acknowledged: getLocalStorageSetting(
settings.EMAIL_COLLECTION_ACKNOWLEDGED
),
credit_required_acknowledged: false, // this needs to be re-acknowledged every run credit_required_acknowledged: false, // this needs to be re-acknowledged every run
language: getLocalStorageSetting(settings.LANGUAGE, "en"), language: getLocalStorageSetting(SETTINGS.LANGUAGE, 'en'),
theme: getLocalStorageSetting(settings.THEME, "light"), theme: getLocalStorageSetting(SETTINGS.THEME, 'light'),
themes: getLocalStorageSetting(settings.THEMES, []), themes: getLocalStorageSetting(SETTINGS.THEMES, []),
}, },
languages: {}, languages: {},
}; };
reducers[types.DAEMON_SETTINGS_RECEIVED] = function(state, action) { reducers[ACTIONS.DAEMON_SETTINGS_RECEIVED] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
daemonSettings: action.data.settings, daemonSettings: action.data.settings,
}); });
}; };
reducers[types.CLIENT_SETTING_CHANGED] = function(state, action) { reducers[ACTIONS.CLIENT_SETTING_CHANGED] = function(state, action) {
const { key, value } = action.data; const { key, value } = action.data;
const clientSettings = Object.assign({}, state.clientSettings); const clientSettings = Object.assign({}, state.clientSettings);
@ -55,16 +47,14 @@ reducers[types.CLIENT_SETTING_CHANGED] = function(state, action) {
}); });
}; };
reducers[types.DOWNLOAD_LANGUAGE_SUCCEEDED] = function(state, action) { reducers[ACTIONS.DOWNLOAD_LANGUAGE_SUCCEEDED] = function(state, action) {
const languages = Object.assign({}, state.languages); const languages = Object.assign({}, state.languages);
const language = action.data.language; const { language } = action.data;
const langCode = language.substring(0, 2); const langCode = language.substring(0, 2);
if (LANGUAGES[langCode]) { if (LANGUAGES[langCode]) {
languages[language] = `${LANGUAGES[langCode][0]} (${ languages[language] = `${LANGUAGES[langCode][0]} (${LANGUAGES[langCode][1]})`;
LANGUAGES[langCode][1]
})`;
} else { } else {
languages[langCode] = langCode; languages[langCode] = langCode;
} }
@ -72,7 +62,7 @@ reducers[types.DOWNLOAD_LANGUAGE_SUCCEEDED] = function(state, action) {
return Object.assign({}, state, { languages }); return Object.assign({}, state, { languages });
}; };
reducers[types.DOWNLOAD_LANGUAGE_FAILED] = function(state, action) { reducers[ACTIONS.DOWNLOAD_LANGUAGE_FAILED] = function(state, action) {
const languages = Object.assign({}, state.languages); const languages = Object.assign({}, state.languages);
delete languages[action.data.language]; delete languages[action.data.language];
return Object.assign({}, state, { languages }); return Object.assign({}, state, { languages });

View file

@ -1,7 +1,7 @@
// @flow // @flow
import { handleActions } from "util/redux-utils"; import handleActions from 'util/redux-utils';
import * as actions from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import * as statuses from "constants/shape_shift"; import * as STATUSES from 'constants/shape_shift';
export type ShapeShiftState = { export type ShapeShiftState = {
loading: boolean, loading: boolean,
@ -24,40 +24,6 @@ export type ShapeShiftState = {
shapeShiftRate: ?number, shapeShiftRate: ?number,
}; };
// All ShapeShift actions that will have some payload
export type GetSupportedCoinsSuccess = {
type: actions.GET_SUPPORTED_COINS_SUCCESS,
data: Array<string>,
};
export type GetCoinStatsStart = {
type: actions.GET_SUPPORTED_COINS_SUCCESS,
data: string,
};
export type GetCoinStatsSuccess = {
type: actions.GET_COIN_STATS_SUCCESS,
data: ShapeShiftMarketInfo,
};
export type GetCoinStatsFail = {
type: actions.GET_COIN_STATS_FAIL,
data: string,
};
export type PrepareShapeShiftSuccess = {
type: actions.PREPARE_SHAPE_SHIFT_SUCCESS,
data: ActiveShiftInfo,
};
export type PrepareShapeShiftFail = {
type: actions.PREPARE_SHAPE_SHIFT_FAIL,
data: ShapeShiftErrorResponse,
};
export type GetActiveShiftSuccess = {
type: actions.GET_ACTIVE_SHIFT_SUCCESS,
data: string,
};
export type GetActiveShiftFail = {
type: actions.GET_ACTIVE_SHIFT_FAIL,
data: ShapeShiftErrorResponse,
};
// ShapeShift sub-types // ShapeShift sub-types
// Defined for actions that contain an object in the payload // Defined for actions that contain an object in the payload
type ShapeShiftMarketInfo = { type ShapeShiftMarketInfo = {
@ -78,6 +44,40 @@ type ShapeShiftErrorResponse = {
message: string, message: string,
}; };
// All ShapeShift actions that will have some payload
export type GetSupportedCoinsSuccess = {
type: ACTIONS.GET_SUPPORTED_COINS_SUCCESS,
data: Array<string>,
};
export type GetCoinStatsStart = {
type: ACTIONS.GET_SUPPORTED_COINS_SUCCESS,
data: string,
};
export type GetCoinStatsSuccess = {
type: ACTIONS.GET_COIN_STATS_SUCCESS,
data: ShapeShiftMarketInfo,
};
export type GetCoinStatsFail = {
type: ACTIONS.GET_COIN_STATS_FAIL,
data: string,
};
export type PrepareShapeShiftSuccess = {
type: ACTIONS.PREPARE_SHAPE_SHIFT_SUCCESS,
data: ActiveShiftInfo,
};
export type PrepareShapeShiftFail = {
type: ACTIONS.PREPARE_SHAPE_SHIFT_FAIL,
data: ShapeShiftErrorResponse,
};
export type GetActiveShiftSuccess = {
type: ACTIONS.GET_ACTIVE_SHIFT_SUCCESS,
data: string,
};
export type GetActiveShiftFail = {
type: ACTIONS.GET_ACTIVE_SHIFT_FAIL,
data: ShapeShiftErrorResponse,
};
const defaultState: ShapeShiftState = { const defaultState: ShapeShiftState = {
loading: true, loading: true,
updating: false, updating: false,
@ -98,14 +98,12 @@ const defaultState: ShapeShiftState = {
export default handleActions( export default handleActions(
{ {
[actions.GET_SUPPORTED_COINS_START]: ( [ACTIONS.GET_SUPPORTED_COINS_START]: (state: ShapeShiftState): ShapeShiftState => ({
state: ShapeShiftState
): ShapeShiftState => ({
...state, ...state,
loading: true, loading: true,
error: undefined, error: undefined,
}), }),
[actions.GET_SUPPORTED_COINS_SUCCESS]: ( [ACTIONS.GET_SUPPORTED_COINS_SUCCESS]: (
state: ShapeShiftState, state: ShapeShiftState,
action: GetSupportedCoinsSuccess action: GetSupportedCoinsSuccess
): ShapeShiftState => { ): ShapeShiftState => {
@ -116,15 +114,13 @@ export default handleActions(
shiftSupportedCoins, shiftSupportedCoins,
}; };
}, },
[actions.GET_SUPPORTED_COINS_FAIL]: ( [ACTIONS.GET_SUPPORTED_COINS_FAIL]: (state: ShapeShiftState): ShapeShiftState => ({
state: ShapeShiftState
): ShapeShiftState => ({
...state, ...state,
loading: false, loading: false,
error: "Error getting available coins", error: 'Error getting available coins',
}), }),
[actions.GET_COIN_STATS_START]: ( [ACTIONS.GET_COIN_STATS_START]: (
state: ShapeShiftState, state: ShapeShiftState,
action: GetCoinStatsStart action: GetCoinStatsStart
): ShapeShiftState => { ): ShapeShiftState => {
@ -135,7 +131,7 @@ export default handleActions(
originCoin: coin, originCoin: coin,
}; };
}, },
[actions.GET_COIN_STATS_SUCCESS]: ( [ACTIONS.GET_COIN_STATS_SUCCESS]: (
state: ShapeShiftState, state: ShapeShiftState,
action: GetCoinStatsSuccess action: GetCoinStatsSuccess
): ShapeShiftState => { ): ShapeShiftState => {
@ -148,14 +144,12 @@ export default handleActions(
originCoinDepositMax: marketInfo.limit, originCoinDepositMax: marketInfo.limit,
// this will come in scientific notation // this will come in scientific notation
// toFixed shows the real number, then regex to remove trailing zeros // toFixed shows the real number, then regex to remove trailing zeros
originCoinDepositMin: marketInfo.minimum originCoinDepositMin: marketInfo.minimum.toFixed(10).replace(/\.?0+$/, ''),
.toFixed(10)
.replace(/\.?0+$/, ""),
originCoinDepositFee: marketInfo.minerFee, originCoinDepositFee: marketInfo.minerFee,
shapeShiftRate: marketInfo.rate, shapeShiftRate: marketInfo.rate,
}; };
}, },
[actions.GET_COIN_STATS_FAIL]: ( [ACTIONS.GET_COIN_STATS_FAIL]: (
state: ShapeShiftState, state: ShapeShiftState,
action: GetCoinStatsFail action: GetCoinStatsFail
): ShapeShiftState => { ): ShapeShiftState => {
@ -167,13 +161,11 @@ export default handleActions(
}; };
}, },
[actions.PREPARE_SHAPE_SHIFT_START]: ( [ACTIONS.PREPARE_SHAPE_SHIFT_START]: (state: ShapeShiftState): ShapeShiftState => ({
state: ShapeShiftState
): ShapeShiftState => ({
...state, ...state,
error: undefined, error: undefined,
}), }),
[actions.PREPARE_SHAPE_SHIFT_SUCCESS]: ( [ACTIONS.PREPARE_SHAPE_SHIFT_SUCCESS]: (
state: ShapeShiftState, state: ShapeShiftState,
action: PrepareShapeShiftSuccess action: PrepareShapeShiftSuccess
) => { ) => {
@ -185,13 +177,10 @@ export default handleActions(
shiftCoinType: activeShiftInfo.depositType, shiftCoinType: activeShiftInfo.depositType,
shiftReturnAddress: activeShiftInfo.returnAddress, shiftReturnAddress: activeShiftInfo.returnAddress,
shiftOrderId: activeShiftInfo.orderId, shiftOrderId: activeShiftInfo.orderId,
shiftState: statuses.NO_DEPOSITS, shiftState: STATUSES.NO_DEPOSITS,
}; };
}, },
[actions.PREPARE_SHAPE_SHIFT_FAIL]: ( [ACTIONS.PREPARE_SHAPE_SHIFT_FAIL]: (state: ShapeShiftState, action: PrepareShapeShiftFail) => {
state: ShapeShiftState,
action: PrepareShapeShiftFail
) => {
const error: ShapeShiftErrorResponse = action.data; const error: ShapeShiftErrorResponse = action.data;
return { return {
...state, ...state,
@ -199,7 +188,7 @@ export default handleActions(
}; };
}, },
[actions.CLEAR_SHAPE_SHIFT]: (state: ShapeShiftState): ShapeShiftState => ({ [ACTIONS.CLEAR_SHAPE_SHIFT]: (state: ShapeShiftState): ShapeShiftState => ({
...state, ...state,
loading: false, loading: false,
updating: false, updating: false,
@ -211,14 +200,12 @@ export default handleActions(
originCoin: state.shiftSupportedCoins[0], originCoin: state.shiftSupportedCoins[0],
}), }),
[actions.GET_ACTIVE_SHIFT_START]: ( [ACTIONS.GET_ACTIVE_SHIFT_START]: (state: ShapeShiftState): ShapeShiftState => ({
state: ShapeShiftState
): ShapeShiftState => ({
...state, ...state,
error: undefined, error: undefined,
updating: true, updating: true,
}), }),
[actions.GET_ACTIVE_SHIFT_SUCCESS]: ( [ACTIONS.GET_ACTIVE_SHIFT_SUCCESS]: (
state: ShapeShiftState, state: ShapeShiftState,
action: GetActiveShiftSuccess action: GetActiveShiftSuccess
): ShapeShiftState => { ): ShapeShiftState => {
@ -229,7 +216,7 @@ export default handleActions(
shiftState: status, shiftState: status,
}; };
}, },
[actions.GET_ACTIVE_SHIFT_FAIL]: ( [ACTIONS.GET_ACTIVE_SHIFT_FAIL]: (
state: ShapeShiftState, state: ShapeShiftState,
action: GetActiveShiftFail action: GetActiveShiftFail
): ShapeShiftState => { ): ShapeShiftState => {

View file

@ -1,6 +1,11 @@
// @flow // @flow
import * as actions from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
import { handleActions } from "util/redux-utils"; import handleActions from 'util/redux-utils';
export type Subscription = {
channelName: string,
uri: string,
};
// Subscription redux types // Subscription redux types
export type SubscriptionState = { export type SubscriptionState = {
@ -8,30 +13,22 @@ export type SubscriptionState = {
hasFetchedSubscriptions: boolean, hasFetchedSubscriptions: boolean,
}; };
export type Subscription = {
channelName: string,
uri: string,
};
// Subscription action types // Subscription action types
type doChannelSubscribe = { type doChannelSubscribe = {
type: actions.CHANNEL_SUBSCRIBE, type: ACTIONS.CHANNEL_SUBSCRIBE,
data: Subscription, data: Subscription,
}; };
type doChannelUnsubscribe = { type doChannelUnsubscribe = {
type: actions.CHANNEL_UNSUBSCRIBE, type: ACTIONS.CHANNEL_UNSUBSCRIBE,
data: Subscription, data: Subscription,
}; };
type HasFetchedSubscriptions = { type HasFetchedSubscriptions = {
type: actions.HAS_FETCHED_SUBSCRIPTIONS, type: ACTIONS.HAS_FETCHED_SUBSCRIPTIONS,
}; };
export type Action = export type Action = doChannelSubscribe | doChannelUnsubscribe | HasFetchedSubscriptions;
| doChannelSubscribe
| doChannelUnsubscribe
| HasFetchedSubscriptions;
export type Dispatch = (action: Action) => any; export type Dispatch = (action: Action) => any;
const defaultState = { const defaultState = {
@ -41,7 +38,7 @@ const defaultState = {
export default handleActions( export default handleActions(
{ {
[actions.CHANNEL_SUBSCRIBE]: ( [ACTIONS.CHANNEL_SUBSCRIBE]: (
state: SubscriptionState, state: SubscriptionState,
action: doChannelSubscribe action: doChannelSubscribe
): SubscriptionState => { ): SubscriptionState => {
@ -54,7 +51,7 @@ export default handleActions(
subscriptions: newSubscriptions, subscriptions: newSubscriptions,
}; };
}, },
[actions.CHANNEL_UNSUBSCRIBE]: ( [ACTIONS.CHANNEL_UNSUBSCRIBE]: (
state: SubscriptionState, state: SubscriptionState,
action: doChannelUnsubscribe action: doChannelUnsubscribe
): SubscriptionState => { ): SubscriptionState => {
@ -62,20 +59,14 @@ export default handleActions(
const newSubscriptions = state.subscriptions const newSubscriptions = state.subscriptions
.slice() .slice()
.filter( .filter(subscription => subscription.channelName !== subscriptionToRemove.channelName);
subscription =>
subscription.channelName !== subscriptionToRemove.channelName
);
return { return {
...state, ...state,
subscriptions: newSubscriptions, subscriptions: newSubscriptions,
}; };
}, },
[actions.HAS_FETCHED_SUBSCRIPTIONS]: ( [ACTIONS.HAS_FETCHED_SUBSCRIPTIONS]: (state: SubscriptionState): SubscriptionState => ({
state: SubscriptionState,
action: HasFetchedSubscriptions
): SubscriptionState => ({
...state, ...state,
hasFetchedSubscriptions: true, hasFetchedSubscriptions: true,
}), }),

View file

@ -1,4 +1,4 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
const reducers = {}; const reducers = {};
@ -6,9 +6,9 @@ const defaultState = {
authenticationIsPending: false, authenticationIsPending: false,
userIsPending: false, userIsPending: false,
emailNewIsPending: false, emailNewIsPending: false,
emailNewErrorMessage: "", emailNewErrorMessage: '',
emailToVerify: "", emailToVerify: '',
inviteNewErrorMessage: "", inviteNewErrorMessage: '',
inviteNewIsPending: false, inviteNewIsPending: false,
inviteStatusIsPending: false, inviteStatusIsPending: false,
invitesRemaining: undefined, invitesRemaining: undefined,
@ -16,7 +16,7 @@ const defaultState = {
user: undefined, user: undefined,
}; };
reducers[types.AUTHENTICATION_STARTED] = function(state, action) { reducers[ACTIONS.AUTHENTICATION_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
authenticationIsPending: true, authenticationIsPending: true,
userIsPending: true, userIsPending: true,
@ -24,7 +24,7 @@ reducers[types.AUTHENTICATION_STARTED] = function(state, action) {
}); });
}; };
reducers[types.AUTHENTICATION_SUCCESS] = function(state, action) { reducers[ACTIONS.AUTHENTICATION_SUCCESS] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
authenticationIsPending: false, authenticationIsPending: false,
userIsPending: false, userIsPending: false,
@ -32,7 +32,7 @@ reducers[types.AUTHENTICATION_SUCCESS] = function(state, action) {
}); });
}; };
reducers[types.AUTHENTICATION_FAILURE] = function(state, action) { reducers[ACTIONS.AUTHENTICATION_FAILURE] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
authenticationIsPending: false, authenticationIsPending: false,
userIsPending: false, userIsPending: false,
@ -40,35 +40,35 @@ reducers[types.AUTHENTICATION_FAILURE] = function(state, action) {
}); });
}; };
reducers[types.USER_FETCH_STARTED] = function(state, action) { reducers[ACTIONS.USER_FETCH_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
userIsPending: true, userIsPending: true,
user: defaultState.user, user: defaultState.user,
}); });
}; };
reducers[types.USER_FETCH_SUCCESS] = function(state, action) { reducers[ACTIONS.USER_FETCH_SUCCESS] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
userIsPending: false, userIsPending: false,
user: action.data.user, user: action.data.user,
}); });
}; };
reducers[types.USER_FETCH_FAILURE] = function(state, action) { reducers[ACTIONS.USER_FETCH_FAILURE] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
userIsPending: true, userIsPending: true,
user: null, user: null,
}); });
}; };
reducers[types.USER_EMAIL_NEW_STARTED] = function(state, action) { reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
emailNewIsPending: true, emailNewIsPending: true,
emailNewErrorMessage: "", emailNewErrorMessage: '',
}); });
}; };
reducers[types.USER_EMAIL_NEW_SUCCESS] = function(state, action) { reducers[ACTIONS.USER_EMAIL_NEW_SUCCESS] = function(state, action) {
const user = Object.assign({}, state.user); const user = Object.assign({}, state.user);
user.primary_email = action.data.email; user.primary_email = action.data.email;
return Object.assign({}, state, { return Object.assign({}, state, {
@ -78,68 +78,67 @@ reducers[types.USER_EMAIL_NEW_SUCCESS] = function(state, action) {
}); });
}; };
reducers[types.USER_EMAIL_NEW_EXISTS] = function(state, action) { reducers[ACTIONS.USER_EMAIL_NEW_EXISTS] = function(state, action) {
const user = Object.assign({}, state.user);
return Object.assign({}, state, { return Object.assign({}, state, {
emailToVerify: action.data.email, emailToVerify: action.data.email,
emailNewIsPending: false, emailNewIsPending: false,
}); });
}; };
reducers[types.USER_EMAIL_NEW_FAILURE] = function(state, action) { reducers[ACTIONS.USER_EMAIL_NEW_FAILURE] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
emailNewIsPending: false, emailNewIsPending: false,
emailNewErrorMessage: action.data.error, emailNewErrorMessage: action.data.error,
}); });
}; };
reducers[types.USER_EMAIL_VERIFY_STARTED] = function(state, action) { reducers[ACTIONS.USER_EMAIL_VERIFY_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
emailVerifyIsPending: true, emailVerifyIsPending: true,
emailVerifyErrorMessage: "", emailVerifyErrorMessage: '',
}); });
}; };
reducers[types.USER_EMAIL_VERIFY_SUCCESS] = function(state, action) { reducers[ACTIONS.USER_EMAIL_VERIFY_SUCCESS] = function(state, action) {
const user = Object.assign({}, state.user); const user = Object.assign({}, state.user);
user.primary_email = action.data.email; user.primary_email = action.data.email;
return Object.assign({}, state, { return Object.assign({}, state, {
emailToVerify: "", emailToVerify: '',
emailVerifyIsPending: false, emailVerifyIsPending: false,
user, user,
}); });
}; };
reducers[types.USER_EMAIL_VERIFY_FAILURE] = function(state, action) { reducers[ACTIONS.USER_EMAIL_VERIFY_FAILURE] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
emailVerifyIsPending: false, emailVerifyIsPending: false,
emailVerifyErrorMessage: action.data.error, emailVerifyErrorMessage: action.data.error,
}); });
}; };
reducers[types.USER_IDENTITY_VERIFY_STARTED] = function(state, action) { reducers[ACTIONS.USER_IDENTITY_VERIFY_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
identityVerifyIsPending: true, identityVerifyIsPending: true,
identityVerifyErrorMessage: "", identityVerifyErrorMessage: '',
}); });
}; };
reducers[types.USER_IDENTITY_VERIFY_SUCCESS] = function(state, action) { reducers[ACTIONS.USER_IDENTITY_VERIFY_SUCCESS] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
identityVerifyIsPending: false, identityVerifyIsPending: false,
identityVerifyErrorMessage: "", identityVerifyErrorMessage: '',
user: action.data.user, user: action.data.user,
}); });
}; };
reducers[types.USER_IDENTITY_VERIFY_FAILURE] = function(state, action) { reducers[ACTIONS.USER_IDENTITY_VERIFY_FAILURE] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
identityVerifyIsPending: false, identityVerifyIsPending: false,
identityVerifyErrorMessage: action.data.error, identityVerifyErrorMessage: action.data.error,
}); });
}; };
reducers[types.FETCH_ACCESS_TOKEN_SUCCESS] = function(state, action) { reducers[ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS] = function(state, action) {
const { token } = action.data; const { token } = action.data;
return Object.assign({}, state, { return Object.assign({}, state, {
@ -147,13 +146,13 @@ reducers[types.FETCH_ACCESS_TOKEN_SUCCESS] = function(state, action) {
}); });
}; };
reducers[types.USER_INVITE_STATUS_FETCH_STARTED] = function(state, action) { reducers[ACTIONS.USER_INVITE_STATUS_FETCH_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
inviteStatusIsPending: true, inviteStatusIsPending: true,
}); });
}; };
reducers[types.USER_INVITE_STATUS_FETCH_SUCCESS] = function(state, action) { reducers[ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
inviteStatusIsPending: false, inviteStatusIsPending: false,
invitesRemaining: action.data.invitesRemaining, invitesRemaining: action.data.invitesRemaining,
@ -161,28 +160,28 @@ reducers[types.USER_INVITE_STATUS_FETCH_SUCCESS] = function(state, action) {
}); });
}; };
reducers[types.USER_INVITE_NEW_STARTED] = function(state, action) { reducers[ACTIONS.USER_INVITE_NEW_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
inviteNewIsPending: true, inviteNewIsPending: true,
inviteNewErrorMessage: "", inviteNewErrorMessage: '',
}); });
}; };
reducers[types.USER_INVITE_NEW_SUCCESS] = function(state, action) { reducers[ACTIONS.USER_INVITE_NEW_SUCCESS] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
inviteNewIsPending: false, inviteNewIsPending: false,
inviteNewErrorMessage: "", inviteNewErrorMessage: '',
}); });
}; };
reducers[types.USER_INVITE_NEW_FAILURE] = function(state, action) { reducers[ACTIONS.USER_INVITE_NEW_FAILURE] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
inviteNewIsPending: false, inviteNewIsPending: false,
inviteNewErrorMessage: action.data.error.message, inviteNewErrorMessage: action.data.error.message,
}); });
}; };
reducers[types.USER_INVITE_STATUS_FETCH_FAILURE] = function(state, action) { reducers[ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
inviteStatusIsPending: false, inviteStatusIsPending: false,
invitesRemaining: null, invitesRemaining: null,

View file

@ -1,7 +1,7 @@
import * as types from "constants/action_types"; import * as ACTIONS from 'constants/action_types';
const reducers = {}; const reducers = {};
const address = localStorage.getItem("receiveAddress"); const receiveAddress = localStorage.getItem('receiveAddress');
const buildDraftTransaction = () => ({ const buildDraftTransaction = () => ({
amount: undefined, amount: undefined,
address: undefined, address: undefined,
@ -12,19 +12,19 @@ const defaultState = {
blocks: {}, blocks: {},
transactions: {}, transactions: {},
fetchingTransactions: false, fetchingTransactions: false,
receiveAddress: address, receiveAddress,
gettingNewAddress: false, gettingNewAddress: false,
draftTransaction: buildDraftTransaction(), draftTransaction: buildDraftTransaction(),
sendingSupport: false, sendingSupport: false,
}; };
reducers[types.FETCH_TRANSACTIONS_STARTED] = function(state, action) { reducers[ACTIONS.FETCH_TRANSACTIONS_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
fetchingTransactions: true, fetchingTransactions: true,
}); });
}; };
reducers[types.FETCH_TRANSACTIONS_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_TRANSACTIONS_COMPLETED] = function(state, action) {
const byId = Object.assign({}, state.transactions); const byId = Object.assign({}, state.transactions);
const { transactions } = action.data; const { transactions } = action.data;
@ -39,41 +39,41 @@ reducers[types.FETCH_TRANSACTIONS_COMPLETED] = function(state, action) {
}); });
}; };
reducers[types.GET_NEW_ADDRESS_STARTED] = function(state, action) { reducers[ACTIONS.GET_NEW_ADDRESS_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
gettingNewAddress: true, gettingNewAddress: true,
}); });
}; };
reducers[types.GET_NEW_ADDRESS_COMPLETED] = function(state, action) { reducers[ACTIONS.GET_NEW_ADDRESS_COMPLETED] = function(state, action) {
const { address } = action.data; const { address } = action.data;
localStorage.setItem("receiveAddress", address); localStorage.setItem('receiveAddress', address);
return Object.assign({}, state, { return Object.assign({}, state, {
gettingNewAddress: false, gettingNewAddress: false,
receiveAddress: address, receiveAddress: address,
}); });
}; };
reducers[types.UPDATE_BALANCE] = function(state, action) { reducers[ACTIONS.UPDATE_BALANCE] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
balance: action.data.balance, balance: action.data.balance,
}); });
}; };
reducers[types.CHECK_ADDRESS_IS_MINE_STARTED] = function(state, action) { reducers[ACTIONS.CHECK_ADDRESS_IS_MINE_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
checkingAddressOwnership: true, checkingAddressOwnership: true,
}); });
}; };
reducers[types.CHECK_ADDRESS_IS_MINE_COMPLETED] = function(state, action) { reducers[ACTIONS.CHECK_ADDRESS_IS_MINE_COMPLETED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
checkingAddressOwnership: false, checkingAddressOwnership: false,
}); });
}; };
reducers[types.SET_DRAFT_TRANSACTION_AMOUNT] = function(state, action) { reducers[ACTIONS.SET_DRAFT_TRANSACTION_AMOUNT] = function(state, action) {
const oldDraft = state.draftTransaction; const oldDraft = state.draftTransaction;
const newDraft = Object.assign({}, oldDraft, { const newDraft = Object.assign({}, oldDraft, {
amount: parseFloat(action.data.amount), amount: parseFloat(action.data.amount),
@ -84,7 +84,7 @@ reducers[types.SET_DRAFT_TRANSACTION_AMOUNT] = function(state, action) {
}); });
}; };
reducers[types.SET_DRAFT_TRANSACTION_ADDRESS] = function(state, action) { reducers[ACTIONS.SET_DRAFT_TRANSACTION_ADDRESS] = function(state, action) {
const oldDraft = state.draftTransaction; const oldDraft = state.draftTransaction;
const newDraft = Object.assign({}, oldDraft, { const newDraft = Object.assign({}, oldDraft, {
address: action.data.address, address: action.data.address,
@ -95,7 +95,7 @@ reducers[types.SET_DRAFT_TRANSACTION_ADDRESS] = function(state, action) {
}); });
}; };
reducers[types.SEND_TRANSACTION_STARTED] = function(state, action) { reducers[ACTIONS.SEND_TRANSACTION_STARTED] = function(state) {
const newDraftTransaction = Object.assign({}, state.draftTransaction, { const newDraftTransaction = Object.assign({}, state.draftTransaction, {
sending: true, sending: true,
}); });
@ -105,13 +105,13 @@ reducers[types.SEND_TRANSACTION_STARTED] = function(state, action) {
}); });
}; };
reducers[types.SEND_TRANSACTION_COMPLETED] = function(state, action) { reducers[ACTIONS.SEND_TRANSACTION_COMPLETED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
draftTransaction: buildDraftTransaction(), draftTransaction: buildDraftTransaction(),
}); });
}; };
reducers[types.SEND_TRANSACTION_FAILED] = function(state, action) { reducers[ACTIONS.SEND_TRANSACTION_FAILED] = function(state, action) {
const newDraftTransaction = Object.assign({}, state.draftTransaction, { const newDraftTransaction = Object.assign({}, state.draftTransaction, {
sending: false, sending: false,
error: action.data.error, error: action.data.error,
@ -122,28 +122,28 @@ reducers[types.SEND_TRANSACTION_FAILED] = function(state, action) {
}); });
}; };
reducers[types.SUPPORT_TRANSACTION_STARTED] = function(state, action) { reducers[ACTIONS.SUPPORT_TRANSACTION_STARTED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
sendingSupport: true, sendingSupport: true,
}); });
}; };
reducers[types.SUPPORT_TRANSACTION_COMPLETED] = function(state, action) { reducers[ACTIONS.SUPPORT_TRANSACTION_COMPLETED] = function(state) {
return Object.assign({}, state, { return Object.assign({}, state, {
sendingSupport: false, sendingSupport: false,
}); });
}; };
reducers[types.SUPPORT_TRANSACTION_FAILED] = function(state, action) { reducers[ACTIONS.SUPPORT_TRANSACTION_FAILED] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
error: action.data.error, error: action.data.error,
sendingSupport: false, sendingSupport: false,
}); });
}; };
reducers[types.FETCH_BLOCK_SUCCESS] = (state, action) => { reducers[ACTIONS.FETCH_BLOCK_SUCCESS] = (state, action) => {
const { block, block: { height } } = action.data, const { block, block: { height } } = action.data;
blocks = Object.assign({}, state.blocks); const blocks = Object.assign({}, state.blocks);
blocks[height] = block; blocks[height] = block;

View file

@ -1,32 +1,26 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
export const _selectState = state => state.app || {}; export const selectState = state => state.app || {};
export const selectPlatform = createSelector( export const selectPlatform = createSelector(selectState, state => state.platform);
_selectState,
state => state.platform
);
export const selectUpdateUrl = createSelector(selectPlatform, platform => { export const selectUpdateUrl = createSelector(selectPlatform, platform => {
switch (platform) { switch (platform) {
case "darwin": case 'darwin':
return "https://lbry.io/get/lbry.dmg"; return 'https://lbry.io/get/lbry.dmg';
case "linux": case 'linux':
return "https://lbry.io/get/lbry.deb"; return 'https://lbry.io/get/lbry.deb';
case "win32": case 'win32':
return "https://lbry.io/get/lbry.exe"; return 'https://lbry.io/get/lbry.exe';
default: default:
throw "Unknown platform"; throw Error('Unknown platform');
} }
}); });
export const selectRemoteVersion = createSelector( export const selectRemoteVersion = createSelector(selectState, state => state.remoteVersion);
_selectState,
state => state.remoteVersion
);
export const selectIsUpgradeAvailable = createSelector( export const selectIsUpgradeAvailable = createSelector(
_selectState, selectState,
state => state.isUpgradeAvailable state => state.isUpgradeAvailable
); );
@ -35,81 +29,54 @@ export const selectUpgradeFilename = createSelector(
selectRemoteVersion, selectRemoteVersion,
(platform, version) => { (platform, version) => {
switch (platform) { switch (platform) {
case "darwin": case 'darwin':
return `LBRY_${version}.dmg`; return `LBRY_${version}.dmg`;
case "linux": case 'linux':
return `LBRY_${version}_amd64.deb`; return `LBRY_${version}_amd64.deb`;
case "win32": case 'win32':
return `LBRY_${version}.exe`; return `LBRY_${version}.exe`;
default: default:
throw "Unknown platform"; throw Error('Unknown platform');
} }
} }
); );
export const selectCurrentModal = createSelector( export const selectCurrentModal = createSelector(selectState, state => state.modal);
_selectState,
state => state.modal
);
export const selectDownloadProgress = createSelector( export const selectDownloadProgress = createSelector(selectState, state => state.downloadProgress);
_selectState,
state => state.downloadProgress
);
export const selectDownloadComplete = createSelector( export const selectDownloadComplete = createSelector(
_selectState, selectState,
state => state.upgradeDownloadCompleted state => state.upgradeDownloadCompleted
); );
export const selectIsUpgradeSkipped = createSelector( export const selectIsUpgradeSkipped = createSelector(selectState, state => state.isUpgradeSkipped);
_selectState,
state => state.isUpgradeSkipped
);
export const selectUpgradeDownloadPath = createSelector( export const selectUpgradeDownloadPath = createSelector(selectState, state => state.downloadPath);
_selectState,
state => state.downloadPath
);
export const selectUpgradeDownloadItem = createSelector( export const selectUpgradeDownloadItem = createSelector(selectState, state => state.downloadItem);
_selectState,
state => state.downloadItem
);
export const selectModalProps = createSelector( export const selectModalProps = createSelector(selectState, state => state.modalProps);
_selectState,
state => state.modalProps
);
export const selectDaemonReady = createSelector( export const selectDaemonReady = createSelector(selectState, state => state.daemonReady);
_selectState,
state => state.daemonReady
);
export const selectDaemonVersionMatched = createSelector( export const selectDaemonVersionMatched = createSelector(
_selectState, selectState,
state => state.daemonVersionMatched state => state.daemonVersionMatched
); );
export const selectSnackBar = createSelector( export const selectSnackBar = createSelector(selectState, state => state.snackBar || {});
_selectState,
state => state.snackBar || {}
);
export const selectSnackBarSnacks = createSelector( export const selectSnackBarSnacks = createSelector(
selectSnackBar, selectSnackBar,
snackBar => snackBar.snacks || [] snackBar => snackBar.snacks || []
); );
export const selectBadgeNumber = createSelector( export const selectBadgeNumber = createSelector(selectState, state => state.badgeNumber);
_selectState,
state => state.badgeNumber
);
export const selectCurrentLanguage = createSelector( export const selectCurrentLanguage = createSelector(
_selectState, selectState,
() => app.i18n.getLocale() || "en" () => app.i18n.getLocale() || 'en'
); );
export const selectVolume = createSelector(_selectState, state => state.volume); export const selectVolume = createSelector(selectState, state => state.volume);

View file

@ -1,22 +1,7 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
const _selectState = state => state.availability; const selectState = state => state.availability;
export const selectAvailabilityByUri = createSelector( const selectFetchingAvailability = createSelector(selectState, state => state.fetching || {});
_selectState,
state => state.byUri || {}
);
export const makeSelectIsAvailableForUri = uri => export { selectFetchingAvailability as default };
createSelector(
selectAvailabilityByUri,
byUri => (!byUri || byUri[uri] === undefined ? undefined : byUri[uri] > 0)
);
export const selectFetchingAvailability = createSelector(
_selectState,
state => state.fetching || {}
);
export const makeSelectFetchingAvailabilityForUri = uri =>
createSelector(selectFetchingAvailability, byUri => byUri && byUri[uri]);

View file

@ -1,77 +1,79 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
import lbryuri from "lbryuri"; import Lbryuri from 'lbryuri';
import { makeSelectCurrentParam } from "./navigation"; import { makeSelectCurrentParam } from 'redux/selectors/navigation';
const _selectState = state => state.claims || {}; const selectState = state => state.claims || {};
export const selectClaimsById = createSelector( export const selectClaimsById = createSelector(selectState, state => state.byId || {});
_selectState,
state => state.byId || {}
);
export const selectClaimsByUri = createSelector( export const selectClaimsByUri = createSelector(selectState, selectClaimsById, (state, byId) => {
_selectState, const byUri = state.claimsByUri || {};
selectClaimsById, const claims = {};
(state, byId) => {
const byUri = state.claimsByUri || {};
const claims = {};
Object.keys(byUri).forEach(uri => { Object.keys(byUri).forEach(uri => {
const claimId = byUri[uri]; const claimId = byUri[uri];
// NOTE returning a null claim allows us to differentiate between an // NOTE returning a null claim allows us to differentiate between an
// undefined (never fetched claim) and one which just doesn't exist. Not // undefined (never fetched claim) and one which just doesn't exist. Not
// the cleanest solution but couldn't think of anything better right now // the cleanest solution but couldn't think of anything better right now
if (claimId === null) { if (claimId === null) {
claims[uri] = null; claims[uri] = null;
} else { } else {
const claim = byId[claimId]; const claim = byId[claimId];
claims[uri] = claim; claims[uri] = claim;
} }
}); });
return claims; return claims;
} });
);
export const selectAllClaimsByChannel = createSelector( export const selectAllClaimsByChannel = createSelector(
_selectState, selectState,
state => state.claimsByChannel || {} state => state.claimsByChannel || {}
); );
export const makeSelectClaimForUri = uri => export const makeSelectClaimForUri = uri =>
createSelector( createSelector(selectClaimsByUri, claims => claims && claims[Lbryuri.normalize(uri)]);
selectClaimsByUri,
claims => claims && claims[lbryuri.normalize(uri)] export const selectMyClaimsRaw = createSelector(selectState, state => state.myClaims);
);
export const selectAbandoningIds = createSelector(selectState, state =>
Object.keys(state.abandoningById || {})
);
export const selectMyActiveClaims = createSelector(
selectMyClaimsRaw,
selectAbandoningIds,
(claims, abandoningIds) =>
new Set(
claims &&
claims
.map(claim => claim.claim_id)
.filter(claimId => Object.keys(abandoningIds).indexOf(claimId) === -1)
)
);
export const makeSelectClaimIsMine = rawUri => { export const makeSelectClaimIsMine = rawUri => {
const uri = lbryuri.normalize(rawUri); const uri = Lbryuri.normalize(rawUri);
return createSelector( return createSelector(
selectClaimsByUri, selectClaimsByUri,
selectMyActiveClaims, selectMyActiveClaims,
(claims, myClaims) => (claims, myClaims) =>
claims && claims && claims[uri] && claims[uri].claim_id && myClaims.has(claims[uri].claim_id)
claims[uri] &&
claims[uri].claim_id &&
myClaims.has(claims[uri].claim_id)
); );
}; };
export const selectAllFetchingChannelClaims = createSelector( export const selectAllFetchingChannelClaims = createSelector(
_selectState, selectState,
state => state.fetchingChannelClaims || {} state => state.fetchingChannelClaims || {}
); );
export const makeSelectFetchingChannelClaims = uri => export const makeSelectFetchingChannelClaims = uri =>
createSelector( createSelector(selectAllFetchingChannelClaims, fetching => fetching && fetching[uri]);
selectAllFetchingChannelClaims,
fetching => fetching && fetching[uri]
);
export const makeSelectClaimsInChannelForCurrentPage = uri => { export const makeSelectClaimsInChannelForCurrentPage = uri => {
const pageSelector = makeSelectCurrentParam("page"); const pageSelector = makeSelectCurrentParam('page');
return createSelector( return createSelector(
selectClaimsById, selectClaimsById,
@ -90,53 +92,27 @@ export const makeSelectClaimsInChannelForCurrentPage = uri => {
export const makeSelectMetadataForUri = uri => export const makeSelectMetadataForUri = uri =>
createSelector(makeSelectClaimForUri(uri), claim => { createSelector(makeSelectClaimForUri(uri), claim => {
const metadata = const metadata = claim && claim.value && claim.value.stream && claim.value.stream.metadata;
claim && claim.value && claim.value.stream && claim.value.stream.metadata;
const value = metadata || (claim === undefined ? undefined : null); const value = metadata || (claim === undefined ? undefined : null);
return value; return value;
}); });
export const makeSelectTitleForUri = uri => export const makeSelectTitleForUri = uri =>
createSelector( createSelector(makeSelectMetadataForUri(uri), metadata => metadata && metadata.title);
makeSelectMetadataForUri(uri),
metadata => metadata && metadata.title
);
export const makeSelectContentTypeForUri = uri => export const makeSelectContentTypeForUri = uri =>
createSelector(makeSelectClaimForUri(uri), claim => { createSelector(makeSelectClaimForUri(uri), claim => {
const source = const source = claim && claim.value && claim.value.stream && claim.value.stream.source;
claim && claim.value && claim.value.stream && claim.value.stream.source;
return source ? source.contentType : undefined; return source ? source.contentType : undefined;
}); });
export const selectIsFetchingClaimListMine = createSelector( export const selectIsFetchingClaimListMine = createSelector(
_selectState, selectState,
state => !!state.isFetchingClaimListMine state => !!state.isFetchingClaimListMine
); );
export const selectMyClaimsRaw = createSelector( export const selectPendingClaims = createSelector(selectState, state =>
_selectState,
state => state.myClaims
);
export const selectAbandoningIds = createSelector(_selectState, state =>
Object.keys(state.abandoningById || {})
);
export const selectMyActiveClaims = createSelector(
selectMyClaimsRaw,
selectAbandoningIds,
(claims, abandoningIds) =>
new Set(
claims &&
claims
.map(claim => claim.claim_id)
.filter(claimId => Object.keys(abandoningIds).indexOf(claimId) === -1)
)
);
export const selectPendingClaims = createSelector(_selectState, state =>
Object.values(state.pendingById || {}) Object.values(state.pendingById || {})
); );
@ -151,46 +127,38 @@ export const selectMyClaims = createSelector(
myClaimIds.forEach(id => { myClaimIds.forEach(id => {
const claim = byId[id]; const claim = byId[id];
if (claim && abandoningIds.indexOf(id) == -1) claims.push(claim); if (claim && abandoningIds.indexOf(id) === -1) claims.push(claim);
}); });
return [...claims, ...pendingClaims]; return [...claims, ...pendingClaims];
} }
); );
export const selectMyClaimsWithoutChannels = createSelector( export const selectMyClaimsWithoutChannels = createSelector(selectMyClaims, myClaims =>
selectMyClaims, myClaims.filter(claim => !claim.name.match(/^@/))
myClaims => myClaims.filter(claim => !claim.name.match(/^@/))
); );
export const selectAllMyClaimsByOutpoint = createSelector( export const selectAllMyClaimsByOutpoint = createSelector(
selectMyClaimsRaw, selectMyClaimsRaw,
claims => claims =>
new Set( new Set(claims && claims.length ? claims.map(claim => `${claim.txid}:${claim.nout}`) : null)
claims && claims.length
? claims.map(claim => `${claim.txid}:${claim.nout}`)
: null
)
); );
export const selectMyClaimsOutpoints = createSelector( export const selectMyClaimsOutpoints = createSelector(selectMyClaims, myClaims => {
selectMyClaims, const outpoints = [];
myClaims => {
const outpoints = [];
myClaims.forEach(claim => outpoints.push(`${claim.txid}:${claim.nout}`)); myClaims.forEach(claim => outpoints.push(`${claim.txid}:${claim.nout}`));
return outpoints; return outpoints;
} });
);
export const selectFetchingMyChannels = createSelector( export const selectFetchingMyChannels = createSelector(
_selectState, selectState,
state => !!state.fetchingMyChannels state => !!state.fetchingMyChannels
); );
export const selectMyChannelClaims = createSelector( export const selectMyChannelClaims = createSelector(
_selectState, selectState,
selectClaimsById, selectClaimsById,
(state, byId) => { (state, byId) => {
const ids = state.myChannelClaims || []; const ids = state.myChannelClaims || [];

View file

@ -1,35 +1,26 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
export const _selectState = state => state.content || {}; export const selectState = state => state.content || {};
export const selectFeaturedUris = createSelector( export const selectFeaturedUris = createSelector(selectState, state => state.featuredUris);
_selectState,
state => state.featuredUris
);
export const selectFetchingFeaturedUris = createSelector( export const selectFetchingFeaturedUris = createSelector(
_selectState, selectState,
state => !!state.fetchingFeaturedContent state => !!state.fetchingFeaturedContent
); );
export const selectResolvingUris = createSelector( export const selectResolvingUris = createSelector(selectState, state => state.resolvingUris || []);
_selectState,
state => state.resolvingUris || []
);
export const selectPlayingUri = createSelector( export const selectPlayingUri = createSelector(selectState, state => state.playingUri);
_selectState,
state => state.playingUri
);
export const makeSelectIsUriResolving = uri => export const makeSelectIsUriResolving = uri =>
createSelector( createSelector(
selectResolvingUris, selectResolvingUris,
resolvingUris => resolvingUris && resolvingUris.indexOf(uri) != -1 resolvingUris => resolvingUris && resolvingUris.indexOf(uri) !== -1
); );
export const selectChannelClaimCounts = createSelector( export const selectChannelClaimCounts = createSelector(
_selectState, selectState,
state => state.channelClaimCounts || {} state => state.channelClaimCounts || {}
); );
@ -43,6 +34,6 @@ export const makeSelectTotalPagesForChannel = uri =>
); );
export const selectRewardContentClaimIds = createSelector( export const selectRewardContentClaimIds = createSelector(
_selectState, selectState,
state => state.rewardedContentClaimIds state => state.rewardedContentClaimIds
); );

View file

@ -1,33 +1,20 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
import { selectCurrentParams } from "redux/selectors/navigation"; import { selectCurrentParams } from 'redux/selectors/navigation';
export const _selectState = state => state.costInfo || {}; export const selectState = state => state.costInfo || {};
export const selectAllCostInfoByUri = createSelector( export const selectAllCostInfoByUri = createSelector(selectState, state => state.byUri || {});
_selectState,
state => state.byUri || {}
);
export const makeSelectCostInfoForUri = uri => export const makeSelectCostInfoForUri = uri =>
createSelector( createSelector(selectAllCostInfoByUri, costInfos => costInfos && costInfos[uri]);
selectAllCostInfoByUri,
costInfos => costInfos && costInfos[uri]
);
export const selectCostForCurrentPageUri = createSelector( export const selectCostForCurrentPageUri = createSelector(
selectAllCostInfoByUri, selectAllCostInfoByUri,
selectCurrentParams, selectCurrentParams,
(costInfo, params) => (costInfo, params) => (params.uri && costInfo[params.uri] ? costInfo[params.uri].cost : undefined)
params.uri && costInfo[params.uri] ? costInfo[params.uri].cost : undefined
); );
export const selectFetchingCostInfo = createSelector( export const selectFetchingCostInfo = createSelector(selectState, state => state.fetching || {});
_selectState,
state => state.fetching || {}
);
export const makeSelectFetchingCostInfoForUri = uri => export const makeSelectFetchingCostInfoForUri = uri =>
createSelector( createSelector(selectFetchingCostInfo, fetchingByUri => fetchingByUri && fetchingByUri[uri]);
selectFetchingCostInfo,
fetchingByUri => fetchingByUri && fetchingByUri[uri]
);

View file

@ -1,45 +1,39 @@
import lbry from "lbry"; import { createSelector } from 'reselect';
import { createSelector } from "reselect";
import { import {
selectClaimsByUri, selectClaimsByUri,
selectIsFetchingClaimListMine, selectIsFetchingClaimListMine,
selectMyClaims, selectMyClaims,
selectMyClaimsOutpoints, selectMyClaimsOutpoints,
} from "redux/selectors/claims"; } from 'redux/selectors/claims';
export const _selectState = state => state.fileInfo || {}; export const selectState = state => state.fileInfo || {};
export const selectFileInfosByOutpoint = createSelector( export const selectFileInfosByOutpoint = createSelector(
_selectState, selectState,
state => state.byOutpoint || {} state => state.byOutpoint || {}
); );
export const selectIsFetchingFileList = createSelector( export const selectIsFetchingFileList = createSelector(
_selectState, selectState,
state => !!state.isFetchingFileList state => state.isFetchingFileList
); );
export const selectIsFetchingFileListDownloadedOrPublished = createSelector( export const selectIsFetchingFileListDownloadedOrPublished = createSelector(
selectIsFetchingFileList, selectIsFetchingFileList,
selectIsFetchingClaimListMine, selectIsFetchingClaimListMine,
(isFetchingFileList, isFetchingClaimListMine) => (isFetchingFileList, isFetchingClaimListMine) => isFetchingFileList || isFetchingClaimListMine
isFetchingFileList || isFetchingClaimListMine
); );
export const makeSelectFileInfoForUri = uri => export const makeSelectFileInfoForUri = uri =>
createSelector( createSelector(selectClaimsByUri, selectFileInfosByOutpoint, (claims, byOutpoint) => {
selectClaimsByUri, const claim = claims[uri];
selectFileInfosByOutpoint, const outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined;
(claims, byOutpoint) => {
const claim = claims[uri],
outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined;
return outpoint ? byOutpoint[outpoint] : undefined; return outpoint ? byOutpoint[outpoint] : undefined;
} });
);
export const selectDownloadingByOutpoint = createSelector( export const selectDownloadingByOutpoint = createSelector(
_selectState, selectState,
state => state.downloadingByOutpoint || {} state => state.downloadingByOutpoint || {}
); );
@ -53,17 +47,13 @@ export const makeSelectDownloadingForUri = uri =>
} }
); );
export const selectUrisLoading = createSelector( export const selectUrisLoading = createSelector(selectState, state => state.urisLoading || {});
_selectState,
state => state.urisLoading || {}
);
export const makeSelectLoadingForUri = uri => export const makeSelectLoadingForUri = uri =>
createSelector(selectUrisLoading, byUri => byUri && byUri[uri]); createSelector(selectUrisLoading, byUri => byUri && byUri[uri]);
export const selectFileInfosPendingPublish = createSelector( export const selectFileInfosPendingPublish = createSelector(selectState, state =>
_selectState, Object.values(state.pendingByOutpoint || {})
state => Object.values(state.pendingByOutpoint || {})
); );
export const selectFileInfosDownloaded = createSelector( export const selectFileInfosDownloaded = createSelector(
@ -141,18 +131,15 @@ export const selectDownloadingFileInfos = createSelector(
} }
); );
export const selectTotalDownloadProgress = createSelector( export const selectTotalDownloadProgress = createSelector(selectDownloadingFileInfos, fileInfos => {
selectDownloadingFileInfos, const progress = [];
fileInfos => {
const progress = [];
fileInfos.forEach(fileInfo => { fileInfos.forEach(fileInfo => {
progress.push(fileInfo.written_bytes / fileInfo.total_bytes * 100); progress.push(fileInfo.written_bytes / fileInfo.total_bytes * 100);
}); });
const totalProgress = progress.reduce((a, b) => a + b, 0); const totalProgress = progress.reduce((a, b) => a + b, 0);
if (fileInfos.length > 0) return totalProgress / fileInfos.length / 100.0; if (fileInfos.length > 0) return totalProgress / fileInfos.length / 100.0;
return -1; return -1;
} });
);

View file

@ -1,17 +1,12 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
import { parseQueryParams, toQueryString } from "util/query_params"; import { parseQueryParams, toQueryString } from 'util/query_params';
import * as settings from "constants/settings.js"; import Lbryuri from 'lbryuri';
import lbryuri from "lbryuri";
export const _selectState = state => state.navigation || {}; export const selectState = state => state.navigation || {};
export const selectCurrentPath = createSelector( export const selectCurrentPath = createSelector(selectState, state => state.currentPath);
_selectState,
state => state.currentPath
);
export const computePageFromPath = path => export const computePageFromPath = path => path.replace(/^\//, '').split('?')[0];
path.replace(/^\//, "").split("?")[0];
export const selectCurrentPage = createSelector(selectCurrentPath, path => export const selectCurrentPage = createSelector(selectCurrentPath, path =>
computePageFromPath(path) computePageFromPath(path)
@ -21,50 +16,47 @@ export const selectCurrentParams = createSelector(selectCurrentPath, path => {
if (path === undefined) return {}; if (path === undefined) return {};
if (!path.match(/\?/)) return {}; if (!path.match(/\?/)) return {};
return parseQueryParams(path.split("?")[1]); return parseQueryParams(path.split('?')[1]);
}); });
export const makeSelectCurrentParam = param => export const makeSelectCurrentParam = param =>
createSelector( createSelector(selectCurrentParams, params => (params ? params[param] : undefined));
selectCurrentParams,
params => (params ? params[param] : undefined)
);
export const selectHeaderLinks = createSelector(selectCurrentPage, page => { export const selectHeaderLinks = createSelector(selectCurrentPage, page => {
// This contains intentional fall throughs // This contains intentional fall throughs
switch (page) { switch (page) {
case "wallet": case 'wallet':
case "history": case 'history':
case "send": case 'send':
case "getcredits": case 'getcredits':
case "invite": case 'invite':
case "rewards": case 'rewards':
case "backup": case 'backup':
return { return {
wallet: __("Overview"), wallet: __('Overview'),
getcredits: __("Get Credits"), getcredits: __('Get Credits'),
send: __("Send / Receive"), send: __('Send / Receive'),
rewards: __("Rewards"), rewards: __('Rewards'),
invite: __("Invites"), invite: __('Invites'),
history: __("History"), history: __('History'),
}; };
case "downloaded": case 'downloaded':
case "published": case 'published':
return { return {
downloaded: __("Downloaded"), downloaded: __('Downloaded'),
published: __("Published"), published: __('Published'),
}; };
case "settings": case 'settings':
case "help": case 'help':
return { return {
settings: __("Settings"), settings: __('Settings'),
help: __("Help"), help: __('Help'),
}; };
case "discover": case 'discover':
case "subscriptions": case 'subscriptions':
return { return {
discover: __("Discover"), discover: __('Discover'),
subscriptions: __("Subscriptions"), subscriptions: __('Subscriptions'),
}; };
default: default:
return null; return null;
@ -76,86 +68,72 @@ export const selectPageTitle = createSelector(
selectCurrentParams, selectCurrentParams,
(page, params) => { (page, params) => {
switch (page) { switch (page) {
case "settings": case 'settings':
return __("Settings"); return __('Settings');
case "report": case 'report':
return __("Report"); return __('Report');
case "wallet": case 'wallet':
return __("Wallet"); return __('Wallet');
case "send": case 'send':
return __("Send or Receive LBRY Credits"); return __('Send or Receive LBRY Credits');
case "getcredits": case 'getcredits':
return __("Get LBRY Credits"); return __('Get LBRY Credits');
case "backup": case 'backup':
return __("Backup Your Wallet"); return __('Backup Your Wallet');
case "rewards": case 'rewards':
return __("Rewards"); return __('Rewards');
case "invite": case 'invite':
return __("Invites"); return __('Invites');
case "start": case 'start':
return __("Start"); return __('Start');
case "publish": case 'publish':
return params.id ? __("Edit") : __("Publish"); return params.id ? __('Edit') : __('Publish');
case "help": case 'help':
return __("Help"); return __('Help');
case "developer": case 'developer':
return __("Developer"); return __('Developer');
case "show": { case 'show': {
const parts = [lbryuri.normalize(params.uri)]; const parts = [Lbryuri.normalize(params.uri)];
// If the params has any keys other than "uri" // If the params has any keys other than "uri"
if (Object.keys(params).length > 1) { if (Object.keys(params).length > 1) {
parts.push(toQueryString(Object.assign({}, params, { uri: null }))); parts.push(toQueryString(Object.assign({}, params, { uri: null })));
} }
return parts.join("?"); return parts.join('?');
} }
case "downloaded": case 'downloaded':
return __("Downloads & Purchases"); return __('Downloads & Purchases');
case "published": case 'published':
return __("Publishes"); return __('Publications');
case "search": case 'search':
return params.query return params.query ? __('Search results for %s', params.query) : __('Search');
? __("Search results for %s", params.query) case 'subscriptions':
: __("Search"); return __('Your Subscriptions');
case "subscriptions": case 'discover':
return __("Your Subscriptions");
case "discover":
case false: case false:
case null: case null:
case "": case '':
return ""; return '';
default: default:
return page[0].toUpperCase() + (page.length > 0 ? page.substr(1) : ""); return page[0].toUpperCase() + (page.length > 0 ? page.substr(1) : '');
} }
} }
); );
export const selectPathAfterAuth = createSelector( export const selectPathAfterAuth = createSelector(selectState, state => state.pathAfterAuth);
_selectState,
state => state.pathAfterAuth
);
export const selectIsBackDisabled = createSelector( export const selectIsBackDisabled = createSelector(selectState, state => state.index === 0);
_selectState,
state => state.index === 0
);
export const selectIsForwardDisabled = createSelector( export const selectIsForwardDisabled = createSelector(
_selectState, selectState,
state => state.index === state.stack.length - 1 state => state.index === state.stack.length - 1
); );
export const selectHistoryIndex = createSelector( export const selectHistoryIndex = createSelector(selectState, state => state.index);
_selectState,
state => state.index
);
export const selectHistoryStack = createSelector( export const selectHistoryStack = createSelector(selectState, state => state.stack);
_selectState,
state => state.stack
);
// returns current page attributes (scrollY, path) // returns current page attributes (scrollY, path)
export const selectActiveHistoryEntry = createSelector( export const selectActiveHistoryEntry = createSelector(
_selectState, selectState,
state => state.stack[state.index] state => state.stack[state.index]
); );

View file

@ -1,16 +1,15 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
import { selectUser } from "redux/selectors/user"; import REWARDS from 'rewards';
import rewards from "rewards";
const _selectState = state => state.rewards || {}; const selectState = state => state.rewards || {};
export const selectUnclaimedRewardsByType = createSelector( export const selectUnclaimedRewardsByType = createSelector(
_selectState, selectState,
state => state.unclaimedRewardsByType state => state.unclaimedRewardsByType
); );
export const selectClaimedRewardsById = createSelector( export const selectClaimedRewardsById = createSelector(
_selectState, selectState,
state => state.claimedRewardsById state => state.claimedRewardsById
); );
@ -19,13 +18,12 @@ export const selectClaimedRewards = createSelector(
byId => Object.values(byId) || [] byId => Object.values(byId) || []
); );
export const selectClaimedRewardsByTransactionId = createSelector( export const selectClaimedRewardsByTransactionId = createSelector(selectClaimedRewards, rewards =>
selectClaimedRewards, rewards.reduce((mapParam, reward) => {
rewards => const map = mapParam;
rewards.reduce((map, reward) => { map[reward.transaction_id] = reward;
map[reward.transaction_id] = reward; return map;
return map; }, {})
}, {})
); );
export const selectUnclaimedRewards = createSelector( export const selectUnclaimedRewards = createSelector(
@ -33,38 +31,20 @@ export const selectUnclaimedRewards = createSelector(
byType => byType =>
Object.values(byType).sort( Object.values(byType).sort(
(a, b) => (a, b) =>
rewards.SORT_ORDER.indexOf(a.reward_type) < REWARDS.SORT_ORDER.indexOf(a.reward_type) < REWARDS.SORT_ORDER.indexOf(b.reward_type)
rewards.SORT_ORDER.indexOf(b.reward_type)
? -1 ? -1
: 1 : 1
) || [] ) || []
); );
export const selectIsRewardEligible = createSelector( export const selectFetchingRewards = createSelector(selectState, state => !!state.fetching);
selectUser,
user => user.can_claim_rewards export const selectUnclaimedRewardValue = createSelector(selectUnclaimedRewards, rewards =>
rewards.reduce((sum, reward) => sum + reward.reward_amount, 0)
); );
export const selectFetchingRewards = createSelector(
_selectState,
state => !!state.fetching
);
export const selectUnclaimedRewardValue = createSelector(
selectUnclaimedRewards,
rewards => rewards.reduce((sum, reward) => sum + reward.reward_amount, 0)
);
export const selectHasClaimedReward = (state, props) => {
const reward = selectRewardsByType(state)[props.reward_type];
return reward && reward.transaction_id !== "";
};
export const makeSelectHasClaimedReward = () =>
createSelector(selectHasClaimedReward, claimed => claimed);
export const selectClaimsPendingByType = createSelector( export const selectClaimsPendingByType = createSelector(
_selectState, selectState,
state => state.claimPendingByType state => state.claimPendingByType
); );
@ -75,7 +55,7 @@ export const makeSelectIsRewardClaimPending = () =>
createSelector(selectIsClaimRewardPending, isClaiming => isClaiming); createSelector(selectIsClaimRewardPending, isClaiming => isClaiming);
export const selectClaimErrorsByType = createSelector( export const selectClaimErrorsByType = createSelector(
_selectState, selectState,
state => state.claimErrorsByType state => state.claimErrorsByType
); );
@ -85,14 +65,9 @@ const selectClaimRewardError = (state, props) =>
export const makeSelectClaimRewardError = () => export const makeSelectClaimRewardError = () =>
createSelector(selectClaimRewardError, errorMessage => errorMessage); createSelector(selectClaimRewardError, errorMessage => errorMessage);
const selectRewardByType = (state, props) => const selectRewardByType = (state, props) => selectUnclaimedRewardsByType(state)[props.reward_type];
selectUnclaimedRewardsByType(state)[props.reward_type];
export const makeSelectRewardByType = () => export const makeSelectRewardByType = () => createSelector(selectRewardByType, reward => reward);
createSelector(selectRewardByType, reward => reward);
export const makeSelectRewardAmountByType = () => export const makeSelectRewardAmountByType = () =>
createSelector( createSelector(selectRewardByType, reward => (reward ? reward.reward_amount : 0));
selectRewardByType,
reward => (reward ? reward.reward_amount : 0)
);

View file

@ -1,40 +1,34 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
import { import {
selectPageTitle, selectPageTitle,
selectCurrentPage, selectCurrentPage,
selectCurrentParams, selectCurrentParams,
} from "redux/selectors/navigation"; } from 'redux/selectors/navigation';
export const _selectState = state => state.search || {}; export const selectState = state => state.search || {};
export const selectSearchQuery = createSelector( export const selectSearchQuery = createSelector(
selectCurrentPage, selectCurrentPage,
selectCurrentParams, selectCurrentParams,
(page, params) => (page === "search" ? params && params.query : null) (page, params) => (page === 'search' ? params && params.query : null)
); );
export const selectIsSearching = createSelector( export const selectIsSearching = createSelector(selectState, state => !!state.searching);
_selectState,
state => !!state.searching
);
export const selectSearchUrisByQuery = createSelector( export const selectSearchUrisByQuery = createSelector(selectState, state => state.urisByQuery);
_selectState,
state => state.urisByQuery
);
export const makeSelectSearchUris = query => export const makeSelectSearchUris = query =>
// replace statement below is kind of ugly, and repeated in doSearch action // replace statement below is kind of ugly, and repeated in doSearch action
createSelector( createSelector(
selectSearchUrisByQuery, selectSearchUrisByQuery,
byQuery => byQuery[query ? query.replace(/^lbry:\/\//i, "") : query] byQuery => byQuery[query ? query.replace(/^lbry:\/\//i, '') : query]
); );
export const selectWunderBarAddress = createSelector( export const selectWunderBarAddress = createSelector(
selectCurrentPage, selectCurrentPage,
selectPageTitle, selectPageTitle,
selectSearchQuery, selectSearchQuery,
(page, title, query) => (page != "search" ? title : query || title) (page, title, query) => (page !== 'search' ? title : query || title)
); );
export const selectWunderBarIcon = createSelector( export const selectWunderBarIcon = createSelector(
@ -42,44 +36,44 @@ export const selectWunderBarIcon = createSelector(
selectCurrentParams, selectCurrentParams,
(page, params) => { (page, params) => {
switch (page) { switch (page) {
case "auth": case 'auth':
return "icon-user"; return 'icon-user';
case "settings": case 'settings':
return "icon-gear"; return 'icon-gear';
case "help": case 'help':
return "icon-question"; return 'icon-question';
case "report": case 'report':
return "icon-file"; return 'icon-file';
case "downloaded": case 'downloaded':
return "icon-folder"; return 'icon-folder';
case "published": case 'published':
return "icon-folder"; return 'icon-folder';
case "history": case 'history':
return "icon-history"; return 'icon-history';
case "send": case 'send':
return "icon-send"; return 'icon-send';
case "rewards": case 'rewards':
return "icon-rocket"; return 'icon-rocket';
case "invite": case 'invite':
return "icon-envelope-open"; return 'icon-envelope-open';
case "getcredits": case 'getcredits':
return "icon-shopping-cart"; return 'icon-shopping-cart';
case "wallet": case 'wallet':
case "backup": case 'backup':
return "icon-bank"; return 'icon-bank';
case "show": case 'show':
return "icon-file"; return 'icon-file';
case "publish": case 'publish':
return params.id ? __("icon-pencil") : __("icon-upload"); return params.id ? __('icon-pencil') : __('icon-upload');
case "developer": case 'developer':
return "icon-code"; return 'icon-code';
case "discover": case 'discover':
case "search": case 'search':
return "icon-search"; return 'icon-search';
case "subscriptions": case 'subscriptions':
return "icon-th-list"; return 'icon-th-list';
default: default:
return "icon-file"; return 'icon-file';
} }
} }
); );

View file

@ -1,24 +1,17 @@
import * as settings from "constants/settings"; import * as SETTINGS from 'constants/settings';
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
import path from "path";
const _selectState = state => state.settings || {}; const selectState = state => state.settings || {};
export const selectDaemonSettings = createSelector( export const selectDaemonSettings = createSelector(selectState, state => state.daemonSettings);
_selectState,
state => state.daemonSettings
);
export const selectClientSettings = createSelector( export const selectClientSettings = createSelector(
_selectState, selectState,
state => state.clientSettings || {} state => state.clientSettings || {}
); );
export const makeSelectClientSetting = setting => export const makeSelectClientSetting = setting =>
createSelector( createSelector(selectClientSettings, settings => (settings ? settings[setting] : undefined));
selectClientSettings,
settings => (settings ? settings[setting] : undefined)
);
export const selectSettingsIsGenerous = createSelector( export const selectSettingsIsGenerous = createSelector(
selectDaemonSettings, selectDaemonSettings,
@ -26,14 +19,11 @@ export const selectSettingsIsGenerous = createSelector(
); );
// refactor me // refactor me
export const selectShowNsfw = makeSelectClientSetting(settings.SHOW_NSFW); export const selectShowNsfw = makeSelectClientSetting(SETTINGS.SHOW_NSFW);
export const selectLanguages = createSelector( export const selectLanguages = createSelector(selectState, state => state.languages || {});
_selectState,
state => state.languages || {}
);
export const selectThemePath = createSelector( export const selectThemePath = createSelector(
makeSelectClientSetting(settings.THEME), makeSelectClientSetting(SETTINGS.THEME),
theme => `${staticResourcesPath}/themes/${theme || "light"}.css` theme => `${staticResourcesPath}/themes/${theme || 'light'}.css`
); );

View file

@ -1,7 +1,9 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
const _selectState = state => state.shapeShift; const selectState = state => state.shapeShift;
export const selectShapeShift = createSelector(_selectState, state => ({ export const selectShapeShift = createSelector(selectState, state => ({
...state, ...state,
})); }));
export { selectShapeShift as default };

View file

@ -1,15 +1,11 @@
import * as settings from "constants/settings"; import { createSelector } from 'reselect';
import { createSelector } from "reselect"; import { selectAllClaimsByChannel, selectClaimsById } from './claims';
import { selectAllClaimsByChannel, selectClaimsById } from "./claims";
// get the entire subscriptions state // get the entire subscriptions state
const _selectState = state => state.subscriptions || {}; const selectState = state => state.subscriptions || {};
// list of saved channel names and uris // list of saved channel names and uris
export const selectSubscriptions = createSelector( export const selectSubscriptions = createSelector(selectState, state => state.subscriptions);
_selectState,
state => state.subscriptions
);
export const selectSubscriptionsFromClaims = createSelector( export const selectSubscriptionsFromClaims = createSelector(
selectAllClaimsByChannel, selectAllClaimsByChannel,
@ -29,7 +25,7 @@ export const selectSubscriptionsFromClaims = createSelector(
// if subscribed channel has content // if subscribed channel has content
if (channelIds[subscription.uri]) { if (channelIds[subscription.uri]) {
// This will need to be more robust, we will want to be able to load more than the first page // This will need to be more robust, we will want to be able to load more than the first page
const pageOneChannelIds = channelIds[subscription.uri]["1"]; const pageOneChannelIds = channelIds[subscription.uri]['1'];
// we have the channel ids and the corresponding claims // we have the channel ids and the corresponding claims
// loop over the list of ids and grab the claim // loop over the list of ids and grab the claim
@ -40,9 +36,7 @@ export const selectSubscriptionsFromClaims = createSelector(
} }
// all we really need is a uri for each claim // all we really need is a uri for each claim
channelClaims = channelClaims.map( channelClaims = channelClaims.map(claim => `${claim.name}#${claim.claim_id}`);
claim => `${claim.name}#${claim.claim_id}`
);
fetchedSubscriptions.push({ fetchedSubscriptions.push({
claims: channelClaims, claims: channelClaims,

View file

@ -1,18 +1,15 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
export const _selectState = state => state.user || {}; export const selectState = state => state.user || {};
export const selectAuthenticationIsPending = createSelector( export const selectAuthenticationIsPending = createSelector(
_selectState, selectState,
state => state.authenticationIsPending state => state.authenticationIsPending
); );
export const selectUserIsPending = createSelector( export const selectUserIsPending = createSelector(selectState, state => state.userIsPending);
_selectState,
state => state.userIsPending
);
export const selectUser = createSelector(_selectState, state => state.user); export const selectUser = createSelector(selectState, state => state.user);
export const selectUserEmail = createSelector( export const selectUserEmail = createSelector(
selectUser, selectUser,
@ -20,7 +17,7 @@ export const selectUserEmail = createSelector(
); );
export const selectEmailToVerify = createSelector( export const selectEmailToVerify = createSelector(
_selectState, selectState,
selectUserEmail, selectUserEmail,
(state, userEmail) => state.emailToVerify || userEmail (state, userEmail) => state.emailToVerify || userEmail
); );
@ -31,32 +28,32 @@ export const selectUserIsRewardApproved = createSelector(
); );
export const selectEmailNewIsPending = createSelector( export const selectEmailNewIsPending = createSelector(
_selectState, selectState,
state => state.emailNewIsPending state => state.emailNewIsPending
); );
export const selectEmailNewErrorMessage = createSelector( export const selectEmailNewErrorMessage = createSelector(
_selectState, selectState,
state => state.emailNewErrorMessage state => state.emailNewErrorMessage
); );
export const selectEmailVerifyIsPending = createSelector( export const selectEmailVerifyIsPending = createSelector(
_selectState, selectState,
state => state.emailVerifyIsPending state => state.emailVerifyIsPending
); );
export const selectEmailVerifyErrorMessage = createSelector( export const selectEmailVerifyErrorMessage = createSelector(
_selectState, selectState,
state => state.emailVerifyErrorMessage state => state.emailVerifyErrorMessage
); );
export const selectIdentityVerifyIsPending = createSelector( export const selectIdentityVerifyIsPending = createSelector(
_selectState, selectState,
state => state.identityVerifyIsPending state => state.identityVerifyIsPending
); );
export const selectIdentityVerifyErrorMessage = createSelector( export const selectIdentityVerifyErrorMessage = createSelector(
_selectState, selectState,
state => state.identityVerifyErrorMessage state => state.identityVerifyErrorMessage
); );
@ -65,37 +62,31 @@ export const selectUserIsVerificationCandidate = createSelector(
user => user && (!user.has_verified_email || !user.is_identity_verified) user => user && (!user.has_verified_email || !user.is_identity_verified)
); );
export const selectAccessToken = createSelector( export const selectAccessToken = createSelector(selectState, state => state.accessToken);
_selectState,
state => state.accessToken
);
export const selectUserInviteStatusIsPending = createSelector( export const selectUserInviteStatusIsPending = createSelector(
_selectState, selectState,
state => state.inviteStatusIsPending state => state.inviteStatusIsPending
); );
export const selectUserInvitesRemaining = createSelector( export const selectUserInvitesRemaining = createSelector(
_selectState, selectState,
state => state.invitesRemaining state => state.invitesRemaining
); );
export const selectUserInvitees = createSelector( export const selectUserInvitees = createSelector(selectState, state => state.invitees);
_selectState,
state => state.invitees
);
export const selectUserInviteStatusFailed = createSelector( export const selectUserInviteStatusFailed = createSelector(
selectUserInvitesRemaining, selectUserInvitesRemaining,
inviteStatus => selectUserInvitesRemaining === null () => selectUserInvitesRemaining === null
); );
export const selectUserInviteNewIsPending = createSelector( export const selectUserInviteNewIsPending = createSelector(
_selectState, selectState,
state => state.inviteNewIsPending state => state.inviteNewIsPending
); );
export const selectUserInviteNewErrorMessage = createSelector( export const selectUserInviteNewErrorMessage = createSelector(
_selectState, selectState,
state => state.inviteNewErrorMessage state => state.inviteNewErrorMessage
); );

View file

@ -1,99 +1,81 @@
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
export const _selectState = state => state.wallet || {}; export const selectState = state => state.wallet || {};
export const selectBalance = createSelector( export const selectBalance = createSelector(selectState, state => state.balance);
_selectState,
state => state.balance
);
export const selectTransactionsById = createSelector( export const selectTransactionsById = createSelector(selectState, state => state.transactions);
_selectState,
state => state.transactions
);
export const selectTransactionItems = createSelector( export const selectTransactionItems = createSelector(selectTransactionsById, byId => {
selectTransactionsById, const items = [];
byId => {
const items = [];
Object.keys(byId).forEach(txid => { Object.keys(byId).forEach(txid => {
const tx = byId[txid]; const tx = byId[txid];
// ignore dust/fees // ignore dust/fees
// it is fee only txn if all infos are also empty // it is fee only txn if all infos are also empty
if ( if (
Math.abs(tx.value) === Math.abs(tx.fee) && Math.abs(tx.value) === Math.abs(tx.fee) &&
tx.claim_info.length == 0 && tx.claim_info.length === 0 &&
tx.support_info.length == 0 && tx.support_info.length === 0 &&
tx.update_info.length == 0 tx.update_info.length === 0
) { ) {
return; return;
} }
const append = []; const append = [];
append.push(
...tx.claim_info.map(item =>
Object.assign({}, tx, item, {
type: item.claim_name[0] === '@' ? 'channel' : 'publish',
})
)
);
append.push(
...tx.support_info.map(item =>
Object.assign({}, tx, item, {
type: !item.is_tip ? 'support' : 'tip',
})
)
);
append.push(...tx.update_info.map(item => Object.assign({}, tx, item, { type: 'update' })));
if (!append.length) {
append.push( append.push(
...tx.claim_info.map(item => Object.assign({}, tx, {
Object.assign({}, tx, item, { type: tx.value < 0 ? 'spend' : 'receive',
type: item.claim_name[0] === "@" ? "channel" : "publish",
})
)
);
append.push(
...tx.support_info.map(item =>
Object.assign({}, tx, item, {
type: !item.is_tip ? "support" : "tip",
})
)
);
append.push(
...tx.update_info.map(item =>
Object.assign({}, tx, item, { type: "update" })
)
);
if (!append.length) {
append.push(
Object.assign({}, tx, {
type: tx.value < 0 ? "spend" : "receive",
})
);
}
items.push(
...append.map(item => {
// value on transaction, amount on outpoint
// amount is always positive, but should match sign of value
const amount = parseFloat(
item.balance_delta ? item.balance_delta : item.value
);
return {
txid,
date: tx.timestamp ? new Date(parseInt(tx.timestamp) * 1000) : null,
amount,
fee: amount < 0 ? -1 * tx.fee / append.length : 0,
claim_id: item.claim_id,
claim_name: item.claim_name,
type: item.type || "send",
nout: item.nout,
};
}) })
); );
}); }
return items.reverse();
}
);
export const selectRecentTransactions = createSelector( items.push(
selectTransactionItems, ...append.map(item => {
transactions => { // value on transaction, amount on outpoint
const threshold = new Date(); // amount is always positive, but should match sign of value
threshold.setDate(threshold.getDate() - 7); const amount = parseFloat(item.balance_delta ? item.balance_delta : item.value);
return transactions.filter(transaction => transaction.date > threshold);
} return {
); txid,
date: tx.timestamp ? new Date(Number(tx.timestamp) * 1000) : null,
amount,
fee: amount < 0 ? -1 * tx.fee / append.length : 0,
claim_id: item.claim_id,
claim_name: item.claim_name,
type: item.type || 'send',
nout: item.nout,
};
})
);
});
return items.reverse();
});
export const selectRecentTransactions = createSelector(selectTransactionItems, transactions => {
const threshold = new Date();
threshold.setDate(threshold.getDate() - 7);
return transactions.filter(transaction => transaction.date > threshold);
});
export const selectHasTransactions = createSelector( export const selectHasTransactions = createSelector(
selectTransactionItems, selectTransactionItems,
@ -101,27 +83,21 @@ export const selectHasTransactions = createSelector(
); );
export const selectIsFetchingTransactions = createSelector( export const selectIsFetchingTransactions = createSelector(
_selectState, selectState,
state => state.fetchingTransactions state => state.fetchingTransactions
); );
export const selectIsSendingSupport = createSelector( export const selectIsSendingSupport = createSelector(selectState, state => state.sendingSupport);
_selectState,
state => state.sendingSupport
);
export const selectReceiveAddress = createSelector( export const selectReceiveAddress = createSelector(selectState, state => state.receiveAddress);
_selectState,
state => state.receiveAddress
);
export const selectGettingNewAddress = createSelector( export const selectGettingNewAddress = createSelector(
_selectState, selectState,
state => state.gettingNewAddress state => state.gettingNewAddress
); );
export const selectDraftTransaction = createSelector( export const selectDraftTransaction = createSelector(
_selectState, selectState,
state => state.draftTransaction || {} state => state.draftTransaction || {}
); );
@ -140,11 +116,10 @@ export const selectDraftTransactionError = createSelector(
draft => draft.error draft => draft.error
); );
export const selectBlocks = createSelector(_selectState, state => state.blocks); export const selectBlocks = createSelector(selectState, state => state.blocks);
export const makeSelectBlockDate = block => export const makeSelectBlockDate = block =>
createSelector( createSelector(
selectBlocks, selectBlocks,
blocks => blocks => (blocks && blocks[block] ? new Date(blocks[block].time * 1000) : undefined)
blocks && blocks[block] ? new Date(blocks[block].time * 1000) : undefined
); );

View file

@ -1,53 +1,32 @@
import lbry from "lbry"; import Lbry from 'lbry';
import lbryio from "lbryio"; import Lbryio from 'lbryio';
import { doShowSnackBar } from "redux/actions/app"; import { doShowSnackBar } from 'redux/actions/app';
function rewardMessage(type, amount) { function rewardMessage(type, amount) {
return { return {
new_developer: __( new_developer: __('You earned %s for registering as a new developer.', amount),
"You earned %s for registering as a new developer.", new_user: __('You earned %s LBC new user reward.', amount),
amount confirm_email: __('You earned %s LBC for verifying your email address.', amount),
), new_channel: __('You earned %s LBC for creating a publisher identity.', amount),
new_user: __("You earned %s LBC new user reward.", amount), first_stream: __('You earned %s LBC for streaming your first video.', amount),
confirm_email: __( many_downloads: __('You earned %s LBC for downloading a bunch of things.', amount),
"You earned %s LBC for verifying your email address.", first_publish: __('You earned %s LBC for making your first publication.', amount),
amount featured_download: __('You earned %s LBC for watching a featured download.', amount),
), referral: __('You earned %s LBC for referring someone.', amount),
new_channel: __(
"You earned %s LBC for creating a publisher identity.",
amount
),
first_stream: __(
"You earned %s LBC for streaming your first video.",
amount
),
many_downloads: __(
"You earned %s LBC for downloading a bunch of things.",
amount
),
first_publish: __(
"You earned %s LBC for making your first publication.",
amount
),
featured_download: __(
"You earned %s LBC for watching a featured download.",
amount
),
referral: __("You earned %s LBC for referring someone.", amount),
}[type]; }[type];
} }
const rewards = {}; const rewards = {};
rewards.TYPE_NEW_DEVELOPER = "new_developer"; rewards.TYPE_NEW_DEVELOPER = 'new_developer';
rewards.TYPE_NEW_USER = "new_user"; rewards.TYPE_NEW_USER = 'new_user';
rewards.TYPE_CONFIRM_EMAIL = "verified_email"; rewards.TYPE_CONFIRM_EMAIL = 'verified_email';
rewards.TYPE_FIRST_CHANNEL = "new_channel"; rewards.TYPE_FIRST_CHANNEL = 'new_channel';
rewards.TYPE_FIRST_STREAM = "first_stream"; rewards.TYPE_FIRST_STREAM = 'first_stream';
rewards.TYPE_MANY_DOWNLOADS = "many_downloads"; rewards.TYPE_MANY_DOWNLOADS = 'many_downloads';
rewards.TYPE_FIRST_PUBLISH = "first_publish"; rewards.TYPE_FIRST_PUBLISH = 'first_publish';
rewards.TYPE_FEATURED_DOWNLOAD = "featured_download"; rewards.TYPE_FEATURED_DOWNLOAD = 'featured_download';
rewards.TYPE_REFERRAL = "referral"; rewards.TYPE_REFERRAL = 'referral';
rewards.SORT_ORDER = [ rewards.SORT_ORDER = [
rewards.TYPE_NEW_USER, rewards.TYPE_NEW_USER,
rewards.TYPE_CONFIRM_EMAIL, rewards.TYPE_CONFIRM_EMAIL,
@ -62,18 +41,18 @@ rewards.SORT_ORDER = [
rewards.claimReward = function(type) { rewards.claimReward = function(type) {
function requestReward(resolve, reject, params) { function requestReward(resolve, reject, params) {
if (!lbryio.enabled) { if (!Lbryio.enabled) {
reject(new Error(__("Rewards are not enabled."))); reject(new Error(__('Rewards are not enabled.')));
return; return;
} }
lbryio.call("reward", "new", params, "post").then(reward => { Lbryio.call('reward', 'new', params, 'post').then(reward => {
const message = rewardMessage(type, reward.reward_amount); const message = rewardMessage(type, reward.reward_amount);
// Display global notice // Display global notice
const action = doShowSnackBar({ const action = doShowSnackBar({
message, message,
linkText: __("Show All"), linkText: __('Show All'),
linkTarget: "/rewards", linkTarget: '/rewards',
isError: false, isError: false,
}); });
window.app.store.dispatch(action); window.app.store.dispatch(action);
@ -85,7 +64,7 @@ rewards.claimReward = function(type) {
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
lbry.wallet_unused_address().then(address => { Lbry.wallet_unused_address().then(address => {
const params = { const params = {
reward_type: type, reward_type: type,
wallet_address: address, wallet_address: address,
@ -93,42 +72,38 @@ rewards.claimReward = function(type) {
switch (type) { switch (type) {
case rewards.TYPE_FIRST_CHANNEL: case rewards.TYPE_FIRST_CHANNEL:
lbry Lbry.claim_list_mine()
.claim_list_mine()
.then(claims => { .then(claims => {
const claim = claims const claim = claims
.reverse() .reverse()
.find( .find(
claim => foundClaim =>
claim.name.length && foundClaim.name.length &&
claim.name[0] == "@" && foundClaim.name[0] === '@' &&
claim.txid.length && foundClaim.txid.length &&
claim.category == "claim" foundClaim.category === 'claim'
); );
if (claim) { if (claim) {
params.transaction_id = claim.txid; params.transaction_id = claim.txid;
requestReward(resolve, reject, params); requestReward(resolve, reject, params);
} else { } else {
reject( reject(new Error(__('Please create a channel identity first.')));
new Error(__("Please create a channel identity first."))
);
} }
}) })
.catch(reject); .catch(reject);
break; break;
case rewards.TYPE_FIRST_PUBLISH: case rewards.TYPE_FIRST_PUBLISH:
lbry Lbry.claim_list_mine()
.claim_list_mine()
.then(claims => { .then(claims => {
const claim = claims const claim = claims
.reverse() .reverse()
.find( .find(
claim => foundClaim =>
claim.name.length && foundClaim.name.length &&
claim.name[0] != "@" && foundClaim.name[0] !== '@' &&
claim.txid.length && foundClaim.txid.length &&
claim.category == "claim" foundClaim.category === 'claim'
); );
if (claim) { if (claim) {
params.transaction_id = claim.txid; params.transaction_id = claim.txid;
@ -138,12 +113,10 @@ rewards.claimReward = function(type) {
claims.length claims.length
? new Error( ? new Error(
__( __(
"Please publish something and wait for confirmation by the network to claim this reward." 'Please publish something and wait for confirmation by the network to claim this reward.'
) )
) )
: new Error( : new Error(__('Please publish something to claim this reward.'))
__("Please publish something to claim this reward.")
)
); );
} }
}) })

View file

@ -1,13 +1,11 @@
@import url(https://fonts.googleapis.com/css?family=Roboto:400,400i,500,500i,700); @import url(https://fonts.googleapis.com/css?family=Roboto:400,400i,500,500i,700);
html html {
{
height: 100%; height: 100%;
font-size: var(--font-size); font-size: var(--font-size);
} }
body body {
{
color: var(--text-color); color: var(--text-color);
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
line-height: var(--font-line-height); line-height: var(--font-line-height);
@ -19,30 +17,25 @@ body
color: var(--text-selection-color); color: var(--text-selection-color);
} }
#window #window {
{
min-height: 100vh; min-height: 100vh;
background: var(--window-bg); background: var(--window-bg);
} }
.credit-amount--indicator .credit-amount--indicator {
{
font-weight: 500; font-weight: 500;
color: var(--color-money); color: var(--color-money);
} }
.credit-amount--fee .credit-amount--fee {
{
font-size: 0.9em; font-size: 0.9em;
color: var(--color-meta-light); color: var(--color-meta-light);
} }
.credit-amount--bold .credit-amount--bold {
{
font-weight: 700; font-weight: 700;
} }
#main-content #main-content {
{
margin: auto; margin: auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -58,8 +51,7 @@ body
margin-right: auto; margin-right: auto;
max-width: 100%; max-width: 100%;
} }
main.main--single-column main.main--single-column {
{
width: $width-page-constrained; width: $width-page-constrained;
} }
@ -76,7 +68,7 @@ body
background: url('../../../static/img/busy.gif') no-repeat center center; background: url('../../../static/img/busy.gif') no-repeat center center;
width: $width; width: $width;
height: $spacing-vertical; height: $spacing-vertical;
content: ""; content: '';
left: 50%; left: 50%;
margin-left: -1 / 2 * $width; margin-left: -1 / 2 * $width;
display: inline-block; display: inline-block;
@ -93,30 +85,37 @@ h2 {
font-size: 1.75em; font-size: 1.75em;
} }
h3 { font-size: 1.4em; } h3 {
font-size: 1.4em;
}
h4 { h4 {
font-size: 1.2em; font-size: 1.2em;
} }
h5 { font-size: 1.1em; } h5 {
sup, sub { font-size: 1.1em;
vertical-align: baseline; }
position: relative; sup,
sub {
vertical-align: baseline;
position: relative;
}
sup {
top: -0.4em;
}
sub {
top: 0.4em;
} }
sup { top: -0.4em; }
sub { top: 0.4em; }
code { code {
font: 0.8em Consolas, 'Lucida Console', 'Source Sans', monospace; font: 0.8em Consolas, 'Lucida Console', 'Source Sans', monospace;
background-color: var(--color-bg-alt); background-color: var(--color-bg-alt);
} }
p p {
{
margin-bottom: 0.8em; margin-bottom: 0.8em;
&:last-child &:last-child {
{
margin-bottom: 0; margin-bottom: 0;
} }
} }
@ -137,8 +136,7 @@ p
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
.busy-indicator .busy-indicator {
{
background: url('../../../static/img/busy.gif') no-repeat center center; background: url('../../../static/img/busy.gif') no-repeat center center;
display: inline-block; display: inline-block;
margin: -1em 0; margin: -1em 0;
@ -146,30 +144,26 @@ p
min-height: 8px; min-height: 8px;
vertical-align: middle; vertical-align: middle;
padding: 0 30px; padding: 0 30px;
&:last-child &:last-child {
{
padding-right: 2px; padding-right: 2px;
} }
&:first-child &:first-child {
{
padding-left: 2px; padding-left: 2px;
} }
} }
.help { .help {
font-size: .85em; font-size: 0.85em;
color: var(--color-help); color: var(--color-help);
} }
.meta .meta {
{
font-size: 0.9em; font-size: 0.9em;
color: var(--color-meta-light); color: var(--color-meta-light);
} }
.empty .empty {
{ color: var(--color-meta-light);
color:var(--color-meta-light);
font-style: italic; font-style: italic;
} }
@ -185,7 +179,6 @@ p
display: block; display: block;
margin-bottom: $spacing-vertical * 2/3; margin-bottom: $spacing-vertical * 2/3;
text-align: right; text-align: right;
line-height: 1; line-height: 1;
font-size: 0.85em; font-size: 0.85em;
@ -196,7 +189,6 @@ section.section-spaced {
margin-bottom: $spacing-vertical; margin-bottom: $spacing-vertical;
} }
.text-center .text-center {
{
text-align: center; text-align: center;
} }

File diff suppressed because it is too large Load diff

View file

@ -1,39 +1,70 @@
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, select, textarea, p, blockquote, th, td, iframe body,
{ div,
margin:0; dl,
padding:0; dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6,
pre,
code,
form,
fieldset,
legend,
input,
select,
textarea,
p,
blockquote,
th,
td,
iframe {
margin: 0;
padding: 0;
} }
:focus :focus {
{
outline: 0; outline: 0;
} }
input::-webkit-search-cancel-button { input::-webkit-search-cancel-button {
/* Remove default */ /* Remove default */
-webkit-appearance: none; -webkit-appearance: none;
} }
table table {
{
border-collapse: collapse; border-collapse: collapse;
border-spacing:0; border-spacing: 0;
} }
fieldset, img, iframe fieldset,
{ img,
border: 0; iframe {
border: 0;
} }
h1, h2, h3, h4, h5, h6 h1,
{ h2,
font-weight:normal; h3,
h4,
h5,
h6 {
font-weight: normal;
} }
ol, ul ol,
{ ul {
list-style-position: inside;
> li {
list-style-position: inside; list-style-position: inside;
> li { list-style-position: inside; } }
} }
input, textarea, select input,
{ textarea,
font-family:inherit; select {
font-size:inherit; font-family: inherit;
font-weight:inherit; font-size: inherit;
font-weight: inherit;
border: 0 none; border: 0 none;
} }
img { img {
@ -42,8 +73,7 @@ img {
vertical-align: middle; vertical-align: middle;
-ms-interpolation-mode: bicubic; -ms-interpolation-mode: bicubic;
} }
a a {
{ color: inherit;
color: inherit; text-decoration: none;
text-decoration: none;
} }

View file

@ -6,28 +6,27 @@ $width-page-constrained: 800px;
$text-color: #000; $text-color: #000;
:root { :root {
/* Colors */ /* Colors */
--color-brand: #155B4A; --color-brand: #155b4a;
--color-primary: #155B4A; --color-primary: #155b4a;
--color-primary-light: saturate(lighten(#155B4A, 50%), 20%); --color-primary-light: saturate(lighten(#155b4a, 50%), 20%);
--color-light-alt: hsl(hue(#155B4A), 15, 85); --color-light-alt: hsl(hue(#155b4a), 15, 85);
--color-dark-overlay: rgba(32,32,32,0.9); --color-dark-overlay: rgba(32, 32, 32, 0.9);
--color-help: rgba(0, 0, 0, 0.54); --color-help: rgba(0, 0, 0, 0.54);
--color-notice: #8a6d3b; --color-notice: #8a6d3b;
--color-error: #a94442; --color-error: #a94442;
--color-load-screen-text: #c3c3c3; --color-load-screen-text: #c3c3c3;
--color-meta-light: #505050; --color-meta-light: #505050;
--color-money: #216C2A; --color-money: #216c2a;
--color-download: rgba(0, 0, 0, 0.75); --color-download: rgba(0, 0, 0, 0.75);
--color-canvas: #f5f5f5; --color-canvas: #f5f5f5;
--color-bg: #ffffff; --color-bg: #ffffff;
--color-bg-alt: #D9D9D9; --color-bg-alt: #d9d9d9;
/* Misc */ /* Misc */
--content-max-width: 1000px; --content-max-width: 1000px;
--nsfw-blur-intensity: 20px; --nsfw-blur-intensity: 20px;
--height-video-embedded: $width-page-constrained * 9 / 16 ; --height-video-embedded: $width-page-constrained * 9 / 16;
/* Font */ /* Font */
--font-size: 16px; --font-size: 16px;
@ -35,20 +34,21 @@ $text-color: #000;
--font-size-subtext-multiple: 0.82; --font-size-subtext-multiple: 0.82;
/* Shadows */ /* Shadows */
--box-shadow-layer: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); --box-shadow-layer: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
--box-shadow-focus: 2px 4px 4px 0 rgba(0,0,0,.14),2px 5px 3px -2px rgba(0,0,0,.2),2px 3px 7px 0 rgba(0,0,0,.12); --box-shadow-focus: 2px 4px 4px 0 rgba(0, 0, 0, 0.14), 2px 5px 3px -2px rgba(0, 0, 0, 0.2),
2px 3px 7px 0 rgba(0, 0, 0, 0.12);
/* Transition */ /* Transition */
--transition-duration: .225s; --transition-duration: 0.225s;
--transition-type: ease; --transition-type: ease;
/* Text */ /* Text */
--text-color: $text-color; --text-color: $text-color;
--text-help-color: #EEE; --text-help-color: #eee;
--text-max-width: 660px; --text-max-width: 660px;
--text-link-padding: 4px; --text-link-padding: 4px;
--text-selection-bg: rgba(saturate(lighten(#155B4A, 20%), 20%), 1); // temp color --text-selection-bg: rgba(saturate(lighten(#155b4a, 20%), 20%), 1); // temp color
--text-selection-color: #FFF; --text-selection-color: #fff;
/* Window */ /* Window */
--window-bg: var(--color-canvas); --window-bg: var(--color-canvas);
@ -72,7 +72,7 @@ $text-color: #000;
/* input:hover */ /* input:hover */
--input-hover-border-color: rgba(0, 0, 0, 0.87); --input-hover-border-color: rgba(0, 0, 0, 0.87);
/* input:placeholder */ /* input:placeholder */
--input-placeholder-color: rgba(0, 0, 0, 0.42); --input-placeholder-color: rgba(0, 0, 0, 0.42);
--input-placeholder-opacity: 1; --input-placeholder-opacity: 1;
@ -82,9 +82,9 @@ $text-color: #000;
/* Button */ /* Button */
--button-bg: var(--color-bg-alt); --button-bg: var(--color-bg-alt);
--button-color: #FFF; --button-color: #fff;
--button-primary-bg: var(--color-primary); --button-primary-bg: var(--color-primary);
--button-primary-color: #FFF; --button-primary-color: #fff;
--button-padding: $spacing-vertical * 2/3; --button-padding: $spacing-vertical * 2/3;
--button-height: $spacing-vertical * 1.5; --button-height: $spacing-vertical * 1.5;
--button-intra-margin: $spacing-vertical; --button-intra-margin: $spacing-vertical;
@ -93,14 +93,14 @@ $text-color: #000;
/* Header */ /* Header */
--header-bg: var(--color-bg); --header-bg: var(--color-bg);
--header-color: #666; --header-color: #666;
--header-active-color: rgba(0,0,0, 0.85); --header-active-color: rgba(0, 0, 0, 0.85);
--header-height: $spacing-vertical * 2.5; --header-height: $spacing-vertical * 2.5;
--header-button-bg: transparent; //var(--button-bg); --header-button-bg: transparent; //var(--button-bg);
--header-button-hover-bg: rgba(100, 100, 100, 0.15); --header-button-hover-bg: rgba(100, 100, 100, 0.15);
/* Header -> search */ /* Header -> search */
--search-bg: rgba(255, 255, 255, 0.7); --search-bg: rgba(255, 255, 255, 0.7);
--search-border:1px solid #ccc; --search-border: 1px solid #ccc;
--search-color: #666; --search-color: #666;
--search-active-color: var(--header-active-color); --search-active-color: var(--header-active-color);
--search-active-shadow: 0 0 3px 0px var(--text-selection-bg); --search-active-shadow: 0 0 3px 0px var(--text-selection-bg);
@ -130,7 +130,7 @@ $text-color: #000;
/* Modal */ /* Modal */
--modal-width: 440px; --modal-width: 440px;
--modal-bg: var(--color-bg); --modal-bg: var(--color-bg);
--modal-overlay-bg: rgba(#F5F5F5, 0.75); // --color-canvas: #F5F5F5 --modal-overlay-bg: rgba(#f5f5f5, 0.75); // --color-canvas: #F5F5F5
--modal-border: 1px solid rgb(204, 204, 204); --modal-border: 1px solid rgb(204, 204, 204);
/* Menu */ /* Menu */
@ -146,7 +146,7 @@ $text-color: #000;
/* Scrollbar */ /* Scrollbar */
--scrollbar-radius: 10px; --scrollbar-radius: 10px;
--scrollbar-thumb-bg: rgba(0, 0, 0, 0.20); --scrollbar-thumb-bg: rgba(0, 0, 0, 0.2);
--scrollbar-thumb-hover-bg: rgba(0, 0, 0, 0.35); --scrollbar-thumb-hover-bg: rgba(0, 0, 0, 0.35);
--scrollbar-thumb-active-bg: var(--color-primary); --scrollbar-thumb-active-bg: var(--color-primary);
--scrollbar-track-bg: transparent; --scrollbar-track-bg: transparent;
@ -156,6 +156,5 @@ $text-color: #000;
/* Animation :) */ /* Animation :) */
--animation-duration: 0.3s; --animation-duration: 0.3s;
--animation-style: cubic-bezier(.55,0,.1,1); --animation-style: cubic-bezier(0.55, 0, 0.1, 1);
} }

View file

@ -1,31 +1,31 @@
@charset "UTF-8"; @charset "UTF-8";
@import "_reset"; @import '_reset';
@import "_vars"; @import '_vars';
@import "_icons"; @import '_icons';
@import "_gui"; @import '_gui';
@import "component/_table"; @import 'component/_table';
@import "component/_button.scss"; @import 'component/_button.scss';
@import "component/_card.scss"; @import 'component/_card.scss';
@import "component/_file-download.scss"; @import 'component/_file-download.scss';
@import "component/_file-selector.scss"; @import 'component/_file-selector.scss';
@import "component/_file-tile.scss"; @import 'component/_file-tile.scss';
@import "component/_form-field.scss"; @import 'component/_form-field.scss';
@import "component/_header.scss"; @import 'component/_header.scss';
@import "component/_menu.scss"; @import 'component/_menu.scss';
@import "component/_tooltip.scss"; @import 'component/_tooltip.scss';
@import "component/_load-screen.scss"; @import 'component/_load-screen.scss';
@import "component/_channel-indicator.scss"; @import 'component/_channel-indicator.scss';
@import "component/_notice.scss"; @import 'component/_notice.scss';
@import "component/_modal.scss"; @import 'component/_modal.scss';
@import "component/_snack-bar.scss"; @import 'component/_snack-bar.scss';
@import "component/_video.scss"; @import 'component/_video.scss';
@import "component/_pagination.scss"; @import 'component/_pagination.scss';
@import "component/_markdown-editor.scss"; @import 'component/_markdown-editor.scss';
@import "component/_scrollbar.scss"; @import 'component/_scrollbar.scss';
@import "component/_tabs.scss"; @import 'component/_tabs.scss';
@import "component/_divider.scss"; @import 'component/_divider.scss';
@import "component/_checkbox.scss"; @import 'component/_checkbox.scss';
@import "component/_radio.scss"; @import 'component/_radio.scss';
@import "component/_shapeshift.scss"; @import 'component/_shapeshift.scss';
@import "component/_spinner.scss"; @import 'component/_spinner.scss';
@import "page/_show.scss"; @import 'page/_show.scss';

View file

@ -1,4 +1,4 @@
@import "../mixin/link.scss"; @import '../mixin/link.scss';
$button-focus-shift: 12%; $button-focus-shift: 12%;
@ -6,14 +6,13 @@ $button-focus-shift: 12%;
position: relative; position: relative;
display: inline-block; display: inline-block;
+ .button-set-item + .button-set-item {
{
margin-left: var(--button-intra-margin); margin-left: var(--button-intra-margin);
} }
} }
.button-block, .faux-button-block .button-block,
{ .faux-button-block {
display: inline-block; display: inline-block;
height: var(--button-height); height: var(--button-height);
line-height: var(--button-height); line-height: var(--button-height);
@ -22,26 +21,21 @@ $button-focus-shift: 12%;
text-align: center; text-align: center;
border-radius: var(--button-radius); border-radius: var(--button-radius);
text-transform: uppercase; text-transform: uppercase;
.icon .icon {
{
top: 0em; top: 0em;
} }
.icon:first-child .icon:first-child {
{
padding-right: 5px; padding-right: 5px;
} }
.icon:last-child .icon:last-child {
{
padding-left: 5px; padding-left: 5px;
} }
.icon:only-child .icon:only-child {
{
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
} }
} }
.button-block .button-block {
{
cursor: pointer; cursor: pointer;
font-weight: 500; font-weight: 500;
font-size: 14px; font-size: 14px;
@ -57,9 +51,7 @@ $button-focus-shift: 12%;
} }
} }
.button-primary .button-primary {
{
color: var(--button-primary-color); color: var(--button-primary-color);
background-color: var(--button-primary-bg); background-color: var(--button-primary-bg);
box-shadow: var(--box-shadow-layer); box-shadow: var(--box-shadow-layer);
@ -70,14 +62,12 @@ $button-focus-shift: 12%;
//box-shadow: $box-shadow-focus; //box-shadow: $box-shadow-focus;
} }
} }
.button-alt .button-alt {
{
background-color: var(--button-bg); background-color: var(--button-bg);
box-shadow: var(--box-shadow-layer); box-shadow: var(--box-shadow-layer);
} }
.button-text .button-text {
{
@include text-link(); @include text-link();
display: inline-block; display: inline-block;
@ -85,17 +75,15 @@ $button-focus-shift: 12%;
margin: 0 var(--text-link-padding); margin: 0 var(--text-link-padding);
} }
} }
.button-text-help .button-text-help {
{
@include text-link(var(--text-help-color)); @include text-link(var(--text-help-color));
font-size: 0.8em; font-size: 0.8em;
} }
.button--flat .button--flat {
{
box-shadow: none !important; box-shadow: none !important;
} }
.button--submit { .button--submit {
font-family: inherit; font-family: inherit;
line-height: 0; line-height: 0;
} }

View file

@ -12,12 +12,11 @@
//below added to prevent scrollbar on long titles when show page loads, would prefer a cleaner CSS solution //below added to prevent scrollbar on long titles when show page loads, would prefer a cleaner CSS solution
overflow-x: hidden; overflow-x: hidden;
} }
.card--obscured .card--obscured {
{
position: relative; position: relative;
} }
.card--obscured .card__inner { .card--obscured .card__inner {
filter: blur( var(--nsfw-blur-intensity) ); filter: blur(var(--nsfw-blur-intensity));
} }
.card__title-primary, .card__title-primary,
.card__title-identity, .card__title-identity,
@ -52,7 +51,6 @@
margin-top: var(--card-margin); margin-top: var(--card-margin);
margin-bottom: var(--card-margin); margin-bottom: var(--card-margin);
user-select: none; user-select: none;
} }
.card__actions--bottom { .card__actions--bottom {
margin-top: $spacing-vertical * 1/3; margin-top: $spacing-vertical * 1/3;
@ -88,7 +86,7 @@
$font-size-subtext-multiple: 0.82; $font-size-subtext-multiple: 0.82;
.card__subtext { .card__subtext {
color: var(--color-meta-light); color: var(--color-meta-light);
font-size: calc( var(--font-size-subtext-multiple) * 1.0em ); font-size: calc(var(--font-size-subtext-multiple) * 1em);
margin-top: $spacing-vertical * 1/3; margin-top: $spacing-vertical * 1/3;
margin-bottom: $spacing-vertical * 1/3; margin-bottom: $spacing-vertical * 1/3;
} }
@ -96,7 +94,9 @@ $font-size-subtext-multiple: 0.82;
white-space: pre-wrap; white-space: pre-wrap;
} }
.card__subtext--two-lines { .card__subtext--two-lines {
height: calc( var(--font-size) * var(--font-size-subtext-multiple) * var(--font-line-height) * 2); /*this is so one line text still has the proper height*/ height: calc(
var(--font-size) * var(--font-size-subtext-multiple) * var(--font-line-height) * 2
); /*this is so one line text still has the proper height*/
} }
.card-overlay { .card-overlay {
position: absolute; position: absolute;
@ -117,7 +117,7 @@ $font-size-subtext-multiple: 0.82;
cursor: pointer; cursor: pointer;
} }
.card--link { .card--link {
transition: transform 0.2s var(--animation-style); transition: transform 0.2s var(--animation-style);
} }
.card--link:hover { .card--link:hover {
position: relative; position: relative;
@ -126,7 +126,7 @@ $font-size-subtext-multiple: 0.82;
transform: scale(var(--card-link-scaling)) translateX(var(--card-hover-translate)); transform: scale(var(--card-link-scaling)) translateX(var(--card-hover-translate));
transform-origin: 50% 50%; transform-origin: 50% 50%;
overflow-x: visible; overflow-x: visible;
overflow-y: visible overflow-y: visible;
} }
.card--link:hover ~ .card--link { .card--link:hover ~ .card--link {
transform: translateX(calc(var(--card-hover-translate) * 2)); transform: translateX(calc(var(--card-hover-translate) * 2));
@ -139,49 +139,49 @@ $font-size-subtext-multiple: 0.82;
} }
.card__media--autothumb { .card__media--autothumb {
position: relative position: relative;
} }
.card__media--autothumb.purple { .card__media--autothumb.purple {
background-color: #9c27b0 background-color: #9c27b0;
} }
.card__media--autothumb.red { .card__media--autothumb.red {
background-color: #e53935 background-color: #e53935;
} }
.card__media--autothumb.pink { .card__media--autothumb.pink {
background-color: #e91e63 background-color: #e91e63;
} }
.card__media--autothumb.indigo { .card__media--autothumb.indigo {
background-color: #3f51b5 background-color: #3f51b5;
} }
.card__media--autothumb.blue { .card__media--autothumb.blue {
background-color: #2196f3 background-color: #2196f3;
} }
.card__media--autothumb.light-blue { .card__media--autothumb.light-blue {
background-color: #039be5 background-color: #039be5;
} }
.card__media--autothumb.cyan { .card__media--autothumb.cyan {
background-color: #00acc1 background-color: #00acc1;
} }
.card__media--autothumb.teal { .card__media--autothumb.teal {
background-color: #009688 background-color: #009688;
} }
.card__media--autothumb.green { .card__media--autothumb.green {
background-color: #43a047 background-color: #43a047;
} }
.card__media--autothumb.yellow { .card__media--autothumb.yellow {
background-color: #ffeb3b background-color: #ffeb3b;
} }
.card__media--autothumb.orange { .card__media--autothumb.orange {
background-color: #ffa726 background-color: #ffa726;
} }
.card__media--autothumb .card__autothumb__text { .card__media--autothumb .card__autothumb__text {
font-size: 2.0em; font-size: 2em;
width: 100%; width: 100%;
color: #ffffff; color: #ffffff;
text-align: center; text-align: center;
position: absolute; position: absolute;
top: 36% top: 36%;
} }
.card__indicators { .card__indicators {
@ -194,17 +194,17 @@ $font-size-subtext-multiple: 0.82;
white-space: normal; white-space: normal;
} }
.card--small .card__media { .card--small .card__media {
height: calc( var(--card-small-width) * 9 / 16); height: calc(var(--card-small-width) * 9 / 16);
} }
.card--form { .card--form {
width: calc( var(--input-width) + var(--card-padding) * 2); width: calc(var(--input-width) + var(--card-padding) * 2);
} }
.card__subtitle { .card__subtitle {
color: var(--color-help); color: var(--color-help);
font-size: 0.85em; font-size: 0.85em;
line-height: calc( var(--font-line-height) * 1 / 0.85); line-height: calc(var(--font-line-height) * 1 / 0.85);
} }
.card--file-subtitle { .card--file-subtitle {
@ -219,8 +219,7 @@ $font-size-subtext-multiple: 0.82;
padding-left: 20px; padding-left: 20px;
} }
.card-series-submit .card-series-submit {
{
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
max-width: var(--card-max-width); max-width: var(--card-max-width);
@ -279,7 +278,7 @@ $padding-right-card-hover-hack: 30px;
position: absolute; position: absolute;
padding: 0 var(--card-margin); padding: 0 var(--card-margin);
height: 100%; height: 100%;
top: calc( $padding-top-card-hover-hack - var(--card-margin) ); top: calc($padding-top-card-hover-hack - var(--card-margin));
} }
.card-row__nav .card-row__scroll-button { .card-row__nav .card-row__scroll-button {
background: var(--card-bg); background: var(--card-bg);
@ -295,8 +294,8 @@ $padding-right-card-hover-hack: 30px;
transition: transform 0.2s var(--animation-style); transition: transform 0.2s var(--animation-style);
&:hover { &:hover {
opacity: 1.0; opacity: 1;
transform: scale(calc( var(--card-link-scaling) * 1.1)); transform: scale(calc(var(--card-link-scaling) * 1.1));
} }
} }
.card-row__nav--left { .card-row__nav--left {
@ -311,11 +310,11 @@ if we keep doing things like this, we should add a real grid system, but I'm goi
*/ */
.card-grid { .card-grid {
$margin-card-grid: $spacing-vertical * 2/3; $margin-card-grid: $spacing-vertical * 2/3;
display:flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
> .card { > .card {
width: $width-page-constrained / 2 - $margin-card-grid / 2; width: $width-page-constrained / 2 - $margin-card-grid / 2;
flex-grow:1; flex-grow: 1;
} }
> .card:nth-of-type(2n - 1):not(:last-child) { > .card:nth-of-type(2n - 1):not(:last-child) {
margin-right: $margin-card-grid; margin-right: $margin-card-grid;

View file

@ -1,4 +1,3 @@
.channel-name { .channel-name {
display: inline-flex; display: inline-flex;
overflow: hidden; overflow: hidden;

View file

@ -1,4 +1,6 @@
*, *:before, *:after { *,
*:before,
*:after {
box-sizing: border-box; box-sizing: border-box;
} }
@ -7,17 +9,18 @@ $md-checkbox-border-color: var(--input-border-color);
$md-checkbox-size: 20px; $md-checkbox-size: 20px;
$md-checkbox-padding: 4px; $md-checkbox-padding: 4px;
$md-checkmark-width: 2px; $md-checkmark-width: 2px;
$md-checkmark-color: #FFF; $md-checkmark-color: #fff;
.form-field--checkbox { .form-field--checkbox {
position: relative; position: relative;
label { label {
cursor: pointer; cursor: pointer;
&:before, &:after { &:before,
content: ""; &:after {
content: '';
position: absolute; position: absolute;
left:0; left: 0;
top: 0; top: 0;
} }
@ -29,7 +32,7 @@ $md-checkmark-color: #FFF;
border: 2px solid $md-checkbox-border-color; border: 2px solid $md-checkbox-border-color;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
transition: background .3s; transition: background 0.3s;
} }
&:after { &:after {
@ -37,19 +40,17 @@ $md-checkmark-color: #FFF;
} }
} }
input[type="checkbox"] { input[type='checkbox'] {
outline: 0; outline: 0;
visibility: hidden; visibility: hidden;
margin-right: 16px; margin-right: 16px;
&:checked { &:checked {
+ label:before {
+ label:before{
background: $md-checkbox-checked-color; background: $md-checkbox-checked-color;
border:none; border: none;
} }
+ label:after { + label:after {
$md-checkmark-size: $md-checkbox-size - 2*$md-checkbox-padding; $md-checkmark-size: $md-checkbox-size - 2*$md-checkbox-padding;
transform: rotate(-45deg); transform: rotate(-45deg);
@ -65,5 +66,4 @@ $md-checkmark-color: #FFF;
} }
} }
} }
} }

View file

@ -1,16 +1,15 @@
.file-download, .file-download__overlay { .file-download,
.button__content { .file-download__overlay {
margin: 0 var(--text-link-padding); .button__content {
} margin: 0 var(--text-link-padding);
}
} }
.file-download .file-download {
{
position: relative; position: relative;
color: var(--color-download); color: var(--color-download);
} }
.file-download__overlay .file-download__overlay {
{
background: var(--color-download); background: var(--color-download);
color: var(--color-bg); color: var(--color-bg);
position: absolute; position: absolute;

View file

@ -19,5 +19,4 @@ $height-file-tile: $spacing-vertical * 6;
.card__title-primary { .card__title-primary {
margin-top: 0; margin-top: 0;
} }
} }

View file

@ -1,9 +1,7 @@
.form-row-submit .form-row-submit {
{
margin-top: $spacing-vertical; margin-top: $spacing-vertical;
} }
.form-row-submit--with-footer .form-row-submit--with-footer {
{
margin-bottom: $spacing-vertical; margin-bottom: $spacing-vertical;
} }
@ -11,7 +9,7 @@
margin-top: $spacing-vertical * 5/6; margin-top: $spacing-vertical * 5/6;
margin-bottom: 0px; margin-bottom: 0px;
line-height: 1; line-height: 1;
font-size:calc( 0.9 * var(--font-size)); font-size: calc(0.9 * var(--font-size));
} }
.form-row__label-row--prefix { .form-row__label-row--prefix {
float: left; float: left;
@ -19,7 +17,8 @@
} }
.form-row--focus { .form-row--focus {
.form-field__label, .form-field__prefix { .form-field__label,
.form-field__prefix {
color: var(--color-primary) !important; color: var(--color-primary) !important;
} }
} }
@ -37,26 +36,27 @@
background: var(--select-bg); background: var(--select-bg);
color: var(--select-color); color: var(--select-color);
&:focus { &:focus {
outline: var(--input-border-size) solid var(--color-primary); outline: var(--input-border-size) solid var(--color-primary);
} }
} }
input[type="radio"], input[type="checkbox"], { input[type='radio'],
&:checked + .form-field__label, { input[type='checkbox'] {
&:checked + .form-field__label {
color: var(--text-color); color: var(--text-color);
} }
} }
input[type="text"].input-copyable { input[type='text'].input-copyable {
background: var(--input-bg); background: var(--input-bg);
color: var(--input-disabled-color); color: var(--input-disabled-color);
line-height: 1; line-height: 1;
padding-top: $spacing-vertical * 1/3; padding-top: $spacing-vertical * 1/3;
padding-bottom: $spacing-vertical * 1/3; padding-bottom: $spacing-vertical * 1/3;
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
width: 100%; width: 100%;
font-family: "Consolas", "Lucida Console", "Adobe Source Code Pro", monospace; font-family: 'Consolas', 'Lucida Console', 'Adobe Source Code Pro', monospace;
&.input-copyable--with-copy-btn { &.input-copyable--with-copy-btn {
width: 85%; width: 85%;
@ -64,44 +64,44 @@
} }
input[readonly] { input[readonly] {
color: var(--input-disabled-color) !important; color: var(--input-disabled-color) !important;
border-bottom: 1px dashed var(--input-disabled-border-color) !important; border-bottom: 1px dashed var(--input-disabled-border-color) !important;
} }
input[readonly]:focus { input[readonly]:focus {
background: var(--input-bg) !important; background: var(--input-bg) !important;
border-bottom: 1px dashed var(--input-disabled-border-color) !important; border-bottom: 1px dashed var(--input-disabled-border-color) !important;
} }
textarea, textarea,
input[type="text"], input[type='text'],
input[type="password"], input[type='password'],
input[type="email"], input[type='email'],
input[type="number"], input[type='number'],
input[type="search"], input[type='search'],
input[type="date"]{ input[type='date'] {
background: var(--input-bg); background: var(--input-bg);
border-bottom: var(--input-border-size) solid var(--input-border-color); border-bottom: var(--input-border-size) solid var(--input-border-color);
caret-color: var(--color-primary); caret-color: var(--color-primary);
color: var(--input-color); color: var(--input-color);
cursor: pointer; cursor: pointer;
line-height: 1; line-height: 1;
padding:0 1px 8px 1px; padding: 0 1px 8px 1px;
box-sizing: border-box; box-sizing: border-box;
-webkit-appearance: none; -webkit-appearance: none;
transition: all var(--transition-duration) var(--transition-type); transition: all var(--transition-duration) var(--transition-type);
&::-webkit-input-placeholder { &::-webkit-input-placeholder {
color: var(--input-placeholder-color); color: var(--input-placeholder-color);
opacity: var(--input-placeholder-opacity) !important; opacity: var(--input-placeholder-opacity) !important;
} }
&:focus { &:focus {
border-color: var(--color-primary); border-color: var(--color-primary);
background: var(--input-active-bg); background: var(--input-active-bg);
} }
&:hover:not(:focus){ &:hover:not(:focus) {
border-color: var(--input-hover-border-color); border-color: var(--input-hover-border-color);
} }
@ -112,11 +112,10 @@
&.form-field__input--inline { &.form-field__input--inline {
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
border-bottom-width: var(--input-border-size); border-bottom-width: var(--input-border-size);
margin-left: 8px; margin-left: 8px;
margin-right: 8px; margin-right: 8px;
} }
} }
textarea { textarea {
@ -132,7 +131,8 @@
display: block; display: block;
} }
.form-field__label, .form-row__label { .form-field__label,
.form-row__label {
color: var(--form-label-color); color: var(--form-label-color);
&[for] { &[for] {
cursor: pointer; cursor: pointer;
@ -167,17 +167,18 @@
width: 100%; width: 100%;
} }
.form-field__error, .form-field__helper { .form-field__error,
.form-field__helper {
margin-top: $spacing-vertical * 1/3; margin-top: $spacing-vertical * 1/3;
font-size: 0.8em; font-size: 0.8em;
transition: opacity var(--transition-duration) var(--transition-type); transition: opacity var(--transition-duration) var(--transition-type);
} }
.form-field__error { .form-field__error {
color: var(--color-error); color: var(--color-error);
} }
.form-field__helper { .form-field__helper {
color:var(--color-help); color: var(--color-help);
} }
.form-field__input.form-field__input-SimpleMDE .CodeMirror-scroll { .form-field__input.form-field__input-SimpleMDE .CodeMirror-scroll {

View file

@ -1,6 +1,4 @@
#header {
#header
{
color: var(--header-color); color: var(--header-color);
background: var(--header-bg); background: var(--header-bg);
display: flex; display: flex;
@ -19,11 +17,11 @@
padding-left: $spacing-vertical / 4; padding-left: $spacing-vertical / 4;
padding-right: $spacing-vertical / 4; padding-right: $spacing-vertical / 4;
.button-alt { .button-alt {
background: var(--header-button-bg) !important; background: var(--header-button-bg) !important;
font-size: 1em; font-size: 1em;
} }
.button-alt:hover { .button-alt:hover {
background: var(--header-button-hover-bg) !important; background: var(--header-button-hover-bg) !important;
} }
} }
@ -31,17 +29,18 @@
flex-grow: 1; flex-grow: 1;
} }
.wunderbar .wunderbar {
{
position: relative; position: relative;
.icon { .icon {
position: absolute; position: absolute;
left: 10px; left: 10px;
top: $spacing-vertical / 2 - 4px; //hacked top: $spacing-vertical / 2 - 4px; //hacked
} }
} }
.wunderbar--active .icon-search { color: var(--color-primary); } .wunderbar--active .icon-search {
color: var(--color-primary);
}
// below styles should be inside the common input styling // below styles should be inside the common input styling
// will come back to this with the redesign - sean // will come back to this with the redesign - sean

View file

@ -1,4 +1,3 @@
.load-screen { .load-screen {
color: white; color: white;
background: var(--color-brand); background: var(--color-brand);

View file

@ -1,17 +1,17 @@
.CodeMirror { .CodeMirror {
background: var(--color-canvas) !important; background: var(--color-canvas) !important;
border: 0px !important; border: 0px !important;
border-radius: 0px !important; border-radius: 0px !important;
color: var(--text-color) !important; color: var(--text-color) !important;
box-shadow: var(--box-shadow-layer); box-shadow: var(--box-shadow-layer);
} }
.editor-toolbar { .editor-toolbar {
opacity: 1 !important; opacity: 1 !important;
border: 0 !important; border: 0 !important;
background: var(--color-bg-alt); background: var(--color-bg-alt);
border-radius: 0 !important; border-radius: 0 !important;
box-shadow: var(--box-shadow-layer); box-shadow: var(--box-shadow-layer);
} }
.editor-toolbar i.separator { .editor-toolbar i.separator {
@ -20,85 +20,85 @@
} }
.editor-toolbar.fullscreen { .editor-toolbar.fullscreen {
background: var(--color-bg) !important; background: var(--color-bg) !important;
} }
div.editor-toolbar a { div.editor-toolbar a {
opacity: 0.64; opacity: 0.64;
color: var(--text-color) !important; color: var(--text-color) !important;
} }
.editor-toolbar a.active, .editor-toolbar a.active,
.editor-toolbar a:hover { .editor-toolbar a:hover {
opacity: 1; opacity: 1;
background: var(--button-bg) !important; background: var(--button-bg) !important;
border-color: transparent !important; border-color: transparent !important;
} }
.editor-toolbar.disabled-for-preview a:not(.no-disable) { .editor-toolbar.disabled-for-preview a:not(.no-disable) {
background: var(--color-bg-alt) !important; background: var(--color-bg-alt) !important;
border-color: transparent !important; border-color: transparent !important;
} }
.editor-statusbar { .editor-statusbar {
color: var(--form-label-color) !important; color: var(--form-label-color) !important;
} }
.editor-preview { .editor-preview {
background: var(--card-bg) !important; background: var(--card-bg) !important;
border: 0 !important; border: 0 !important;
} }
.editor-preview-side { .editor-preview-side {
background: var(--color-bg-alt) !important; background: var(--color-bg-alt) !important;
border: 1px solid var(--input-border-color) !important; border: 1px solid var(--input-border-color) !important;
} }
.editor-preview pre, .editor-preview pre,
.editor-preview-side pre { .editor-preview-side pre {
background: #eee; background: #eee;
} }
.editor-preview table td, .editor-preview table td,
.editor-preview table th, .editor-preview table th,
.editor-preview-side table td, .editor-preview-side table td,
.editor-preview-side table th { .editor-preview-side table th {
border: 1px solid var(--input-border-color) !important; border: 1px solid var(--input-border-color) !important;
} }
.CodeMirror .CodeMirror-code .cm-tag { .CodeMirror .CodeMirror-code .cm-tag {
color: #63a35c; color: #63a35c;
} }
.CodeMirror .CodeMirror-code .cm-attribute { .CodeMirror .CodeMirror-code .cm-attribute {
color: #795da3; color: #795da3;
} }
.CodeMirror .CodeMirror-code .cm-string { .CodeMirror .CodeMirror-code .cm-string {
color: #183691; color: #183691;
} }
.CodeMirror .CodeMirror-selected { .CodeMirror .CodeMirror-selected {
background: var(--text-selection-bg) !important; background: var(--text-selection-bg) !important;
color: var(--text-selection-color) !important; color: var(--text-selection-color) !important;
} }
.CodeMirror .CodeMirror-cursor{ .CodeMirror .CodeMirror-cursor {
border-color: var(--color-primary) !important; border-color: var(--color-primary) !important;
} }
.CodeMirror .CodeMirror-code .cm-comment { .CodeMirror .CodeMirror-code .cm-comment {
background: rgba(0, 0, 0, .05); background: rgba(0, 0, 0, 0.05);
} }
.CodeMirror .CodeMirror-code .cm-link { .CodeMirror .CodeMirror-code .cm-link {
color: #7f8c8d; color: #7f8c8d;
} }
.CodeMirror .CodeMirror-code .cm-url { .CodeMirror .CodeMirror-code .cm-url {
color: #aab2b3; color: #aab2b3;
} }
.CodeMirror .CodeMirror-placeholder { .CodeMirror .CodeMirror-placeholder {
opacity: .5; opacity: 0.5;
} }

View file

@ -1,4 +1,3 @@
$border-radius-menu: 2px; $border-radius-menu: 2px;
.menu-container { .menu-container {

View file

@ -1,5 +1,5 @@
.modal-overlay,
.modal-overlay, .error-modal-overlay { .error-modal-overlay {
position: fixed; position: fixed;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -72,11 +72,12 @@
max-width: none; max-width: none;
width: var(--modal-width); width: var(--modal-width);
} }
.error-modal__error-list { /*shitty hack/temp fix for long errors making modal unusable*/ .error-modal__error-list {
/*shitty hack/temp fix for long errors making modal unusable*/
border: 1px solid var(--input-border-color); border: 1px solid var(--input-border-color);
padding: 8px; padding: 8px;
list-style: none; list-style: none;
max-height: 400px; max-height: 400px;
max-width: var(--modal-width); max-width: var(--modal-width);
overflow-y: hidden; overflow-y: hidden;
} }

View file

@ -1,17 +1,16 @@
.notice { .notice {
padding: 10px 20px; padding: 10px 20px;
border: 1px solid #000; border: 1px solid #000;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
border-radius: 5px; border-radius: 5px;
color: #468847; color: #468847;
background-color: #dff0d8; background-color: #dff0d8;
border-color: #d6e9c6; border-color: #d6e9c6;
} }
.notice--error { .notice--error {
color: #b94a48; color: #b94a48;
background-color: #f2dede; background-color: #f2dede;
border-color: #eed3d7; border-color: #eed3d7;
} }

View file

@ -1,4 +1,3 @@
.pagination { .pagination {
display: block; display: block;
padding: 0; padding: 0;
@ -13,7 +12,9 @@
border-radius: 2px; border-radius: 2px;
&:not(.pagination__item--selected):hover { &:not(.pagination__item--selected):hover {
background: rgba(0, 0, 0, 0.2); background: rgba(0, 0, 0, 0.2);
> a { cursor: hand } > a {
cursor: hand;
}
} }
> a { > a {
display: inline-block; display: inline-block;
@ -21,12 +22,13 @@
} }
} }
.pagination__item--previous, .pagination__item--next { .pagination__item--previous,
.pagination__item--next {
font-size: 1.2em; font-size: 1.2em;
} }
.pagination__item--break { .pagination__item--break {
padding: 0 $spacing-vertical * 2 / 3; padding: 0 $spacing-vertical * 2 / 3;
} }
.pagination__item--selected { .pagination__item--selected {

View file

@ -5,50 +5,50 @@ $md-radio-checked-size: 10px;
$md-radio-ripple-size: 15px; $md-radio-ripple-size: 15px;
.form-field--radio { .form-field--radio {
position: relative; position: relative;
label { label {
cursor: pointer;
&:before,
&:after {
content: '';
position: absolute;
left: 0;
top: 0;
border-radius: 50%;
transition: all 0.3s ease;
transition-property: transform, border-color;
}
&:before {
width: $md-radio-size;
height: $md-radio-size;
background: transparent;
border: 2px solid $md-radio-border-color;
cursor: pointer; cursor: pointer;
&:before, &:after {
content: "";
position: absolute;
left:0;
top: 0;
border-radius: 50%;
transition: all .3s ease;
transition-property: transform, border-color;
}
&:before {
width: $md-radio-size;
height: $md-radio-size;
background: transparent;
border: 2px solid $md-radio-border-color;
cursor: pointer;
}
&:after {
top: $md-radio-size / 2 - $md-radio-checked-size / 2;
left: $md-radio-size / 2 - $md-radio-checked-size / 2;
width:$md-radio-checked-size;
height:$md-radio-checked-size;
transform: scale(0);
background:$md-radio-checked-color;
}
} }
input[type="radio"] { &:after {
visibility: hidden; top: $md-radio-size / 2 - $md-radio-checked-size / 2;
margin-right: 16px; left: $md-radio-size / 2 - $md-radio-checked-size / 2;
width: $md-radio-checked-size;
&:checked + label:before { height: $md-radio-checked-size;
border-color: $md-radio-checked-color; transform: scale(0);
} background: $md-radio-checked-color;
&:checked + label:after {
transform: scale(1);
}
} }
}
input[type='radio'] {
visibility: hidden;
margin-right: 16px;
&:checked + label:before {
border-color: $md-radio-checked-color;
}
&:checked + label:after {
transform: scale(1);
}
}
} }

View file

@ -1,24 +1,24 @@
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 8px; width: 8px;
overflow: auto; overflow: auto;
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
background: var(--scrollbar-track-bg); background: var(--scrollbar-track-bg);
border-radius: var(--scrollbar-radius); border-radius: var(--scrollbar-radius);
margin: 4px; margin: 4px;
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
border-radius: var(--scrollbar-radius); border-radius: var(--scrollbar-radius);
background-color: var(--scrollbar-thumb-bg); background-color: var(--scrollbar-thumb-bg);
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
} }
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {
background-color: var(--scrollbar-thumb-hover-bg); background-color: var(--scrollbar-thumb-hover-bg);
} }
::-webkit-scrollbar-thumb:active { ::-webkit-scrollbar-thumb:active {
background-color: var(--scrollbar-thumb-active-bg); background-color: var(--scrollbar-thumb-active-bg);
} }

View file

@ -34,7 +34,6 @@
margin-left: 40px; margin-left: 40px;
} }
.shapeshift__link { .shapeshift__link {
padding-left: 10px; padding-left: 10px;
} }

View file

@ -1,4 +1,3 @@
$padding-snack-horizontal: $spacing-vertical; $padding-snack-horizontal: $spacing-vertical;
.snack-bar { .snack-bar {
@ -24,7 +23,7 @@ $padding-snack-horizontal: $spacing-vertical;
border-radius: 2px; border-radius: 2px;
transition: all var(--transition-duration) var(--transition-type); transition: all var(--transition-duration) var(--transition-type);
z-index: 10000; /*hack to get it over react modal */ z-index: 10000; /*hack to get it over react modal */
} }

View file

@ -1,9 +1,9 @@
table.table-standard { table.table-standard {
word-wrap: break-word; word-wrap: break-word;
max-width: 100%; max-width: 100%;
th, td { th,
td {
padding: $spacing-vertical/2 8px; padding: $spacing-vertical/2 8px;
} }
th { th {
@ -13,7 +13,8 @@ table.table-standard {
td { td {
vertical-align: top; vertical-align: top;
} }
thead th, > tr:first-child th { thead th,
> tr:first-child th {
vertical-align: bottom; vertical-align: bottom;
font-weight: 500; font-weight: 500;
font-size: 0.9em; font-size: 0.9em;
@ -32,7 +33,7 @@ table.table-standard {
} }
tfoot td { tfoot td {
padding: $spacing-vertical / 2 8px; padding: $spacing-vertical / 2 8px;
font-size: .85em; font-size: 0.85em;
} }
tbody { tbody {
tr { tr {
@ -62,9 +63,19 @@ table.table-stretch {
} }
table.table-transactions { table.table-transactions {
td:nth-of-type(1) { width: 15%; } td:nth-of-type(1) {
td:nth-of-type(2) { width: 15%; } width: 15%;
td:nth-of-type(3) { width: 15%; } }
td:nth-of-type(4) { width: 40%; } td:nth-of-type(2) {
td:nth-of-type(5) { width: 15%; } width: 15%;
}
td:nth-of-type(3) {
width: 15%;
}
td:nth-of-type(4) {
width: 40%;
}
td:nth-of-type(5) {
width: 15%;
}
} }

View file

@ -1,14 +1,12 @@
/* Tabs */ /* Tabs */
nav.sub-header nav.sub-header {
{
text-transform: uppercase; text-transform: uppercase;
max-width: $width-page-constrained; max-width: $width-page-constrained;
margin-bottom: 40px; margin-bottom: 40px;
border-bottom: var(--divider); border-bottom: var(--divider);
user-select: none; user-select: none;
> a > a {
{
height: 38px; height: 38px;
line-height: 38px; line-height: 38px;
text-align: center; text-align: center;
@ -21,16 +19,13 @@ nav.sub-header
color: var(--tab-color); color: var(--tab-color);
position: relative; position: relative;
&:first-child &:first-child {
{
margin-left: 0; margin-left: 0;
} }
&:last-child &:last-child {
{
margin-right: 0; margin-right: 0;
} }
&.sub-header-selected &.sub-header-selected {
{
color: var(--tab-active-color); color: var(--tab-active-color);
&:before { &:before {
width: 100%; width: 100%;
@ -45,8 +40,7 @@ nav.sub-header
animation-timing-function: var(--animation-style); animation-timing-function: var(--animation-style);
} }
} }
&:hover &:hover {
{
color: var(--tab-active-color); color: var(--tab-active-color);
} }
} }
@ -60,8 +54,11 @@ nav.sub-header
} }
} }
@keyframes activeTab { @keyframes activeTab {
from {width: 0;} from {
to {width: 100%;} width: 0;
}
to {
width: 100%;
}
} }

View file

@ -1,4 +1,4 @@
@import "../mixin/link.scss"; @import '../mixin/link.scss';
.tooltip { .tooltip {
position: relative; position: relative;
@ -27,7 +27,7 @@
.tooltip--header .tooltip__link { .tooltip--header .tooltip__link {
@include text-link(#aaa); @include text-link(#aaa);
font-size: calc( var(--font-size) * 3/4 ); font-size: calc(var(--font-size) * 3/4);
margin-left: var(--button-padding); margin-left: var(--button-padding);
vertical-align: middle; vertical-align: middle;
} }

View file

@ -1,4 +1,3 @@
$height-video-embedded: $width-page-constrained * 9 / 16; $height-video-embedded: $width-page-constrained * 9 / 16;
video { video {
@ -32,13 +31,11 @@ video {
height: $height-video-embedded; height: $height-video-embedded;
} }
} }
.video--obscured .video__cover .video--obscured .video__cover {
{
position: relative; position: relative;
filter: blur(var(--nsfw-blur-intensity)); filter: blur(var(--nsfw-blur-intensity));
} }
.video__loading-screen { .video__loading-screen {
height: 100%; height: 100%;
display: flex; display: flex;

View file

@ -1,6 +1,5 @@
@mixin text-link($color: var(--color-primary), $hover-opacity: 0.70) { @mixin text-link($color: var(--color-primary), $hover-opacity: 0.7) {
.icon .icon {
{
&:first-child { &:first-child {
padding-right: 5px; padding-right: 5px;
} }
@ -15,10 +14,9 @@
text-decoration: none; text-decoration: none;
} }
} }
&:hover &:hover {
{
opacity: $hover-opacity; opacity: $hover-opacity;
transition: opacity var(--transition-duration) var(--transition-type); transition: opacity var(--transition-duration) var(--transition-type);
text-decoration: underline; text-decoration: underline;
.icon { .icon {
text-decoration: none; text-decoration: none;

View file

@ -1,29 +1,27 @@
import { createLogger } from "redux-logger"; import { createLogger } from 'redux-logger';
import appReducer from "redux/reducers/app"; import appReducer from 'redux/reducers/app';
import availabilityReducer from "redux/reducers/availability"; import availabilityReducer from 'redux/reducers/availability';
import claimsReducer from "redux/reducers/claims"; import claimsReducer from 'redux/reducers/claims';
import contentReducer from "redux/reducers/content"; import contentReducer from 'redux/reducers/content';
import costInfoReducer from "redux/reducers/cost_info"; import costInfoReducer from 'redux/reducers/cost_info';
import fileInfoReducer from "redux/reducers/file_info"; import fileInfoReducer from 'redux/reducers/file_info';
import navigationReducer from "redux/reducers/navigation"; import navigationReducer from 'redux/reducers/navigation';
import rewardsReducer from "redux/reducers/rewards"; import rewardsReducer from 'redux/reducers/rewards';
import searchReducer from "redux/reducers/search"; import searchReducer from 'redux/reducers/search';
import settingsReducer from "redux/reducers/settings"; import settingsReducer from 'redux/reducers/settings';
import userReducer from "redux/reducers/user"; import userReducer from 'redux/reducers/user';
import walletReducer from "redux/reducers/wallet"; import walletReducer from 'redux/reducers/wallet';
import shapeShiftReducer from "redux/reducers/shape_shift"; import shapeShiftReducer from 'redux/reducers/shape_shift';
import subscriptionsReducer from "redux/reducers/subscriptions"; import subscriptionsReducer from 'redux/reducers/subscriptions';
import { persistStore, autoRehydrate } from "redux-persist"; import { persistStore, autoRehydrate } from 'redux-persist';
import createCompressor from "redux-persist-transform-compress"; import createCompressor from 'redux-persist-transform-compress';
import createFilter from "redux-persist-transform-filter"; import createFilter from 'redux-persist-transform-filter';
import localForage from "localforage"; import localForage from 'localforage';
import { createStore, applyMiddleware, compose, combineReducers } from "redux"; import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import thunk from "redux-thunk"; import thunk from 'redux-thunk';
const env = process.env.NODE_ENV || "production"
function isFunction(object) { function isFunction(object) {
return typeof object === "function"; return typeof object === 'function';
} }
function isNotFunction(object) { function isNotFunction(object) {
@ -32,10 +30,8 @@ function isNotFunction(object) {
function createBulkThunkMiddleware() { function createBulkThunkMiddleware() {
return ({ dispatch, getState }) => next => action => { return ({ dispatch, getState }) => next => action => {
if (action.type === "BATCH_ACTIONS") { if (action.type === 'BATCH_ACTIONS') {
action.actions action.actions.filter(isFunction).map(actionFn => actionFn(dispatch, getState));
.filter(isFunction)
.map(actionFn => actionFn(dispatch, getState));
} }
return next(action); return next(action);
}; };
@ -44,10 +40,8 @@ function createBulkThunkMiddleware() {
function enableBatching(reducer) { function enableBatching(reducer) {
return function batchingReducer(state, action) { return function batchingReducer(state, action) {
switch (action.type) { switch (action.type) {
case "BATCH_ACTIONS": case 'BATCH_ACTIONS':
return action.actions return action.actions.filter(isNotFunction).reduce(batchingReducer, state);
.filter(isNotFunction)
.reduce(batchingReducer, state);
default: default:
return reducer(state, action); return reducer(state, action);
} }
@ -74,13 +68,14 @@ const reducers = combineReducers({
const bulkThunk = createBulkThunkMiddleware(); const bulkThunk = createBulkThunkMiddleware();
const middleware = [thunk, bulkThunk]; const middleware = [thunk, bulkThunk];
if (env === "development") { if (app.env === 'development') {
const logger = createLogger({ const logger = createLogger({
collapsed: true, collapsed: true,
}); });
middleware.push(logger); middleware.push(logger);
} }
// eslint-disable-next-line no-underscore-dangle
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore( const store = createStore(
@ -88,18 +83,18 @@ const store = createStore(
{}, // initial state {}, // initial state
composeEnhancers( composeEnhancers(
autoRehydrate({ autoRehydrate({
log: env === "development", log: app.env === 'development',
}), }),
applyMiddleware(...middleware) applyMiddleware(...middleware)
) )
); );
const compressor = createCompressor(); const compressor = createCompressor();
const saveClaimsFilter = createFilter("claims", ["byId", "claimsByUri"]); const saveClaimsFilter = createFilter('claims', ['byId', 'claimsByUri']);
const subscriptionsFilter = createFilter("subscriptions", ["subscriptions"]); const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']);
const persistOptions = { const persistOptions = {
whitelist: ["claims", "subscriptions"], whitelist: ['claims', 'subscriptions'],
// Order is important. Needs to be compressed last or other transforms can't // Order is important. Needs to be compressed last or other transforms can't
// read the data // read the data
transforms: [saveClaimsFilter, subscriptionsFilter, compressor], transforms: [saveClaimsFilter, subscriptionsFilter, compressor],
@ -109,7 +104,7 @@ const persistOptions = {
window.cacheStore = persistStore(store, persistOptions, err => { window.cacheStore = persistStore(store, persistOptions, err => {
if (err) { if (err) {
console.error("Unable to load saved settings"); console.error('Unable to load saved SETTINGS');
} }
}); });

View file

@ -1,3 +1,5 @@
// @flow
export type FormikActions = { export type FormikActions = {
setSubmitting: boolean => mixed, setSubmitting: boolean => mixed,
}; };

View file

@ -1,7 +1,7 @@
// https://github.com/reactjs/redux/issues/911 // https://github.com/reactjs/redux/issues/911
function batchActions(...actions) { function batchActions(...actions) {
return { return {
type: "BATCH_ACTIONS", type: 'BATCH_ACTIONS',
actions, actions,
}; };
} }

View file

@ -1,19 +1,16 @@
export function formatCredits(amount, precision) { export function formatCredits(amount, precision) {
return amount.toFixed(precision || 1).replace(/\.?0+$/, ""); return amount.toFixed(precision || 1).replace(/\.?0+$/, '');
} }
export function formatFullPrice(amount, precision) { export function formatFullPrice(amount, precision = 1) {
let formated = ""; let formated = '';
const quantity = amount.toString().split("."); const quantity = amount.toString().split('.');
const fraction = quantity[1]; const fraction = quantity[1];
if (fraction) { if (fraction) {
// Set precision const decimals = fraction.split('');
precision = precision || 1; const first = decimals.filter(number => number !== '0')[0];
const decimals = fraction.split("");
const first = decimals.filter(number => number != "0")[0];
const index = decimals.indexOf(first); const index = decimals.indexOf(first);
// Set format fraction // Set format fraction

View file

@ -1,26 +1,28 @@
export function parseQueryParams(queryString) { export function parseQueryParams(queryString) {
if (queryString === "") return {}; if (queryString === '') return {};
const parts = queryString const parts = queryString
.split("?") .split('?')
.pop() .pop()
.split("&") .split('&')
.map(p => p.split("=")); .map(p => p.split('='));
const params = {}; const params = {};
parts.forEach(arr => { parts.forEach(array => {
params[arr[0]] = arr[1]; const [first, second] = array;
params[first] = second;
}); });
return params; return params;
} }
export function toQueryString(params) { export function toQueryString(params) {
if (!params) return ""; if (!params) return '';
const parts = []; const parts = [];
for (const key in params) { Object.keys(params).forEach(key => {
if (params.hasOwnProperty(key) && params[key]) { if (Object.prototype.hasOwnProperty.call(params, key) && params[key]) {
parts.push(`${key}=${params[key]}`); parts.push(`${key}=${params[key]}`);
} }
} });
return parts.join("&");
return parts.join('&');
} }

View file

@ -1,10 +1,7 @@
// util for creating reducers // util for creating reducers
// based off of redux-actions // based off of redux-actions
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions // https://redux-actions.js.org/docs/api/handleAction.html#handleactions
export const handleActions = (actionMap, defaultState) => ( const handleActions = (actionMap, defaultState) => (state = defaultState, action) => {
state = defaultState,
action
) => {
const handler = actionMap[action.type]; const handler = actionMap[action.type];
if (handler) { if (handler) {
@ -16,3 +13,5 @@ export const handleActions = (actionMap, defaultState) => (
// returning a copy here breaks redux-persist // returning a copy here breaks redux-persist
return state; return state;
}; };
export { handleActions as default };

View file

@ -1,7 +1,7 @@
const { remote } = require("electron"); const { remote } = require('electron');
const application = remote.app; const application = remote.app;
const dock = application.dock; const { dock } = application;
const win = remote.BrowserWindow.getFocusedWindow(); const win = remote.BrowserWindow.getFocusedWindow();
const setBadge = text => { const setBadge = text => {
if (!dock) return; if (!dock) return;

View file

@ -1,6 +1,5 @@
const { remote } = require("electron"); import { remote } from 'electron';
const application = remote.app;
const win = remote.getCurrentWindow(); const win = remote.getCurrentWindow();
const setProgressBar = progress => { const setProgressBar = progress => {

View file

@ -19,7 +19,7 @@ const validateAddress = (coinType, address) => {
return coinRegex.test(address); return coinRegex.test(address);
}; };
export const validateShapeShiftForm = (vals, props) => { export const validateShapeShiftForm = vals => {
const errors = {}; const errors = {};
if (!vals.returnAddress) { if (!vals.returnAddress) {
@ -36,13 +36,13 @@ export const validateShapeShiftForm = (vals, props) => {
}; };
const exampleCoinAddresses = { const exampleCoinAddresses = {
BTC: "1745oPaHeW7Fmpb1fUKTtasYfxr4zu9bwq", BTC: '1745oPaHeW7Fmpb1fUKTtasYfxr4zu9bwq',
BCH: "1745oPaHeW7Fmpb1fUKTtasYfxr4zu9bwq", BCH: '1745oPaHeW7Fmpb1fUKTtasYfxr4zu9bwq',
ETH: "0x8507cA6a274123fC8f80d929AF9D83602bC4e8cC", ETH: '0x8507cA6a274123fC8f80d929AF9D83602bC4e8cC',
DASH: "XedBP7vLPFXbS3URjrH2Z57Fg9SWftBmQ6", DASH: 'XedBP7vLPFXbS3URjrH2Z57Fg9SWftBmQ6',
LTC: "LgZivMvFMTDoqcA5weCQ2QrmRp7pa56bBk", LTC: 'LgZivMvFMTDoqcA5weCQ2QrmRp7pa56bBk',
XMR: XMR:
"466XMeJEcowYGx7RzUJj3VDWBZgRWErVQQX6tHYbsacS5QF6v3tidE6LZZnTJgzeEh6bKEEJ6GC9jHirrUKvJwVKVj9e7jm", '466XMeJEcowYGx7RzUJj3VDWBZgRWErVQQX6tHYbsacS5QF6v3tidE6LZZnTJgzeEh6bKEEJ6GC9jHirrUKvJwVKVj9e7jm',
}; };
export const getExampleAddress = coin => exampleCoinAddresses[coin]; export const getExampleAddress = coin => exampleCoinAddresses[coin];

View file

@ -6,21 +6,25 @@
// as much as it can, without ever going more than once per `wait` duration; // as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass // but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto. // `{leading: false}`. To disable execution on the trailing edge, ditto.
export default function throttle(func, wait, options) { export default function throttle(func, wait, options = {}) {
let timeout, context, args, result; let timeout;
let context;
let args;
let result;
let previous = 0; let previous = 0;
const getNow = () => new Date().getTime(); const getNow = () => new Date().getTime();
if (!options) options = {};
const later = function() { const later = function() {
previous = options.leading === false ? 0 : getNow(); previous = options.leading === false ? 0 : getNow();
timeout = null; timeout = null;
result = func.apply(context, args); result = func.apply(context, args);
if (!timeout) context = args = null; if (!timeout) {
context = null;
args = null;
}
}; };
const throttled = function() { const throttled = function(...funcArgs) {
const now = getNow(); const now = getNow();
if (!previous && options.leading === false) previous = now; if (!previous && options.leading === false) previous = now;
@ -28,7 +32,7 @@ export default function throttle(func, wait, options) {
const remaining = wait - (now - previous); const remaining = wait - (now - previous);
context = this; context = this;
args = arguments; args = funcArgs;
if (remaining <= 0 || remaining > wait) { if (remaining <= 0 || remaining > wait) {
if (timeout) { if (timeout) {
@ -39,7 +43,10 @@ export default function throttle(func, wait, options) {
previous = now; previous = now;
result = func.apply(context, args); result = func.apply(context, args);
if (!timeout) context = args = null; if (!timeout) {
context = null;
args = null;
}
} else if (!timeout && options.trailing !== false) { } else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining); timeout = setTimeout(later, remaining);
} }
@ -49,7 +56,9 @@ export default function throttle(func, wait, options) {
throttled.cancel = function() { throttled.cancel = function() {
clearTimeout(timeout); clearTimeout(timeout);
previous = 0; previous = 0;
timeout = context = args = null; timeout = null;
context = null;
args = null;
}; };
return throttled; return throttled;

Some files were not shown because too many files have changed in this diff Show more