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",
"postinstall": "electron-builder install-app-deps",
"precommit": "lint-staged",
"lint": "eslint src/**/*.{js,jsx} --fix",
"pretty": "prettier src/**/*.{js,jsx,scss,json} --write"
"lint": "eslint 'src/**/*.{js,jsx}' --fix",
"pretty-print": "prettier 'src/**/*.{js,jsx,scss,json}' --write"
},
"keywords": [
"lbry"

View file

@ -1,68 +1,39 @@
/* eslint-disable no-console */
// Module imports
const {
app,
BrowserWindow,
ipcMain,
Menu,
Tray,
globalShortcut,
} = require("electron");
const path = require("path");
const url = require("url");
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");
import Path from 'path';
import Url from 'url';
import Jayson from 'jayson';
import Semver from 'semver';
import Https from 'https';
import Keytar from 'keytar';
import ChildProcess from 'child_process';
import Assert from 'assert';
import { app, BrowserWindow, globalShortcut, ipcMain, Menu, Tray } from 'electron';
import mainMenu from './menu/mainMenu';
const localVersion = app.getVersion();
const setMenu = require("./menu/main-menu.js");
export const contextMenu = require("./menu/context-menu");
export { contextMenu as Default } from './menu/contextMenu';
// Debug configs
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);
}
}
const isDevelopment = process.env.NODE_ENV === 'development';
// Misc constants
const LATEST_RELEASE_API_URL =
"https://api.github.com/repos/lbryio/lbry-app/releases/latest";
const DAEMON_PATH =
process.env.LBRY_DAEMON || path.join(__static, "daemon/lbrynet-daemon");
const LATEST_RELEASE_API_URL = 'https://api.github.com/repos/lbryio/lbry-app/releases/latest';
const DAEMON_PATH = process.env.LBRY_DAEMON || Path.join(__static, 'daemon/lbrynet-daemon');
const rendererUrl = isDevelopment
? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
: `file://${__dirname}/index.html`;
const client = jayson.client.http({
host: "localhost",
const client = Jayson.client.http({
host: 'localhost',
port: 5279,
path: "/",
path: '/',
timeout: 1000,
});
// 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.
let win;
let rendererWindow;
// Also keep the daemon subprocess alive
let daemonSubprocess;
@ -95,8 +66,8 @@ function processRequestedUri(uri) {
// lbry://channel/#claimid. We remove the slash here as well.
// On Linux and Mac, we just return the URI as given.
if (process.platform === "win32") {
return uri.replace(/\/$/, "").replace("/#", "#");
if (process.platform === 'win32') {
return uri.replace(/\/$/, '').replace('/#', '#');
}
return uri;
}
@ -109,223 +80,22 @@ function processRequestedUri(uri) {
function openItem(fullPath) {
const subprocOptions = {
detached: true,
stdio: "ignore",
stdio: 'ignore',
};
let child;
if (process.platform === "darwin") {
child = child_process.spawn("open", [fullPath], subprocOptions);
} else if (process.platform === "linux") {
child = child_process.spawn("xdg-open", [fullPath], subprocOptions);
} else if (process.platform === "win32") {
child = child_process.spawn(
fullPath,
Object.assign({}, subprocOptions, { shell: true })
);
if (process.platform === 'darwin') {
child = ChildProcess.spawn('open', [fullPath], subprocOptions);
} else if (process.platform === 'linux') {
child = ChildProcess.spawn('xdg-open', [fullPath], subprocOptions);
} else if (process.platform === 'win32') {
child = ChildProcess.spawn(fullPath, Object.assign({}, subprocOptions, { shell: true }));
}
// Causes child process reference to be garbage collected, allowing main process to exit
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.
*/
@ -334,6 +104,155 @@ export function safeQuit() {
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
* interface or through app.quit()), we abort the quit, try to shut down the daemon,
@ -344,16 +263,52 @@ function quitNow() {
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 => {
if (argv.length >= 2) {
handleOpenUriRequested(argv[1]); // This will handle restoring and focusing the window
} else if (win) {
if (win.isMinimized()) {
win.restore();
} else if (!win.isVisible()) {
win.show();
} else if (rendererWindow) {
if (rendererWindow.isMinimized()) {
rendererWindow.restore();
} else if (!rendererWindow.isVisible()) {
rendererWindow.show();
}
win.focus();
rendererWindow.focus();
}
});
@ -365,126 +320,44 @@ if (isSecondaryInstance) {
function launchDaemonIfNotRunning() {
// Check if the daemon is already running. If we get
// an error its because its not running
console.log("Checking for lbrynet daemon");
client.request("status", [], (err, res) => {
console.log('Checking for lbrynet daemon');
client.request('status', [], err => {
if (err) {
console.log("lbrynet daemon needs to be launched");
console.log('lbrynet daemon needs to be launched');
launchDaemon();
} else {
console.log("lbrynet daemon is already running");
console.log('lbrynet daemon is already running');
}
});
}
/*
* Last resort for killing unresponsive daemon instances.
* Looks for any processes called "lbrynet-daemon" and
* tries to force kill them.
*/
function forceKillAllDaemonsAndQuit() {
console.log(
"Attempting to force kill any running lbrynet-daemon instances..."
);
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();
}
});
// Taken from webtorrent-desktop
function checkLinuxTraySupport(cb) {
// Check that we're on Ubuntu (or another debian system) and that we have
// libappindicator1.
ChildProcess.exec('dpkg --get-selections libappindicator1', (err, stdout) => {
if (err) return cb(err);
// Unfortunately there's no cleaner way, as far as I can tell, to check
// whether a debian package is installed:
if (stdout.endsWith('\tinstall\n')) {
return cb(null);
}
}
}
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);
return cb(new Error('debian package not installed'));
});
} else if (process.argv.length >= 2) {
handleOpenUriRequested(process.argv[1]);
}
// When a quit is attempted, this is called. It attempts to shutdown the daemon,
// then calls quitNow() to quit for real.
function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) {
function doShutdown() {
console.log("Shutting down daemon");
console.log('Shutting down daemon');
daemonStopRequested = true;
client.request("daemon_stop", [], (err, res) => {
client.request('daemon_stop', [], err => {
if (err) {
console.log(
`received error when stopping lbrynet-daemon. Error message: ${
err.message
}\n`
);
console.log("You will need to manually kill the daemon.");
console.log(`received error when stopping lbrynet-daemon. Error message: ${err.message}\n`);
console.log('You will need to manually kill the daemon.');
} else {
console.log("Successfully stopped daemon via RPC call.");
console.log('Successfully stopped daemon via RPC call.');
quitNow();
}
});
@ -493,7 +366,7 @@ function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) {
if (daemonSubprocess) {
doShutdown();
} 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();
} else {
doShutdown();
@ -503,111 +376,154 @@ function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) {
// If not, we should wait until the daemon is closed before we start the install.
}
// Taken from webtorrent-desktop
function checkLinuxTraySupport(cb) {
// Check that we're on Ubuntu (or another debian system) and that we have
// libappindicator1.
child_process.exec(
"dpkg --get-selections libappindicator1",
(err, stdout) => {
if (err) return cb(err);
// Unfortunately there's no cleaner way, as far as I can tell, to check
// whether a debian package is installed:
if (stdout.endsWith("\tinstall\n")) {
cb(null);
} else {
cb(new Error("debian package not installed"));
}
}
);
if (isDevelopment) {
import('devtron')
.then(({ install }) => {
install();
console.log('Added Extension: Devtron');
})
.catch(error => {
console.error(error);
});
import('electron-devtools-installer')
.then(({ default: installExtension, REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS }) => {
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(error => {
console.error(error);
});
}
ipcMain.on("upgrade", (event, installerPath) => {
app.on("quit", () => {
console.log("Launching upgrade installer at", installerPath);
app.setAsDefaultProtocolClient('lbry');
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
// we'll only get here if we're fully prepared and quitting for real.
openItem(installerPath);
});
if (win) {
win.loadURL(`file://${__static}/upgrade.html`);
if (rendererWindow) {
rendererWindow.loadURL(`file://${__static}/upgrade.html`);
}
shutdownDaemonAndQuit(true);
// wait for daemon to shut down before upgrading
// what to do if no shutdown in a long time?
console.log("Update downloaded to", installerPath);
console.log('Update downloaded to', installerPath);
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) {
// 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 = {
headers: {
"User-Agent": `LBRY/${localVersion}`,
'User-Agent': `LBRY/${localVersion}`,
},
};
const req = https.get(
Object.assign(opts, url.parse(LATEST_RELEASE_API_URL)),
res => {
res.on("data", data => {
result += data;
});
res.on("end", () => {
const tagName = JSON.parse(result).tag_name;
const [_, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
if (!remoteVersion) {
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,
});
}
const req = Https.get(Object.assign(opts, Url.parse(LATEST_RELEASE_API_URL)), res => {
res.on('data', data => {
result += data;
});
res.on('end', () => {
const tagName = JSON.parse(result).tag_name;
const [, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
if (!remoteVersion) {
if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', null);
}
});
}
);
} else {
const upgradeAvailable = Semver.gt(formatRc(remoteVersion), formatRc(localVersion));
if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', {
remoteVersion,
localVersion,
upgradeAvailable,
});
}
}
});
});
req.on("error", err => {
console.log("Failed to get current version from GitHub. Error:", err);
if (win) {
win.webContents.send("version-info-received", null);
req.on('error', err => {
console.log('Failed to get current version from GitHub. Error:', err);
if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', null);
}
});
});
ipcMain.on("get-auth-token", event => {
keytar.getPassword("LBRY", "auth_token").then(token => {
event.sender.send(
"auth-token-response",
token ? token.toString().trim() : null
);
ipcMain.on('get-auth-token', event => {
Keytar.getPassword('LBRY', 'auth_token').then(token => {
event.sender.send('auth-token-response', token ? token.toString().trim() : null);
});
});
ipcMain.on("set-auth-token", (event, token) => {
keytar.setPassword(
"LBRY",
"auth_token",
token ? token.toString().trim() : null
);
ipcMain.on('set-auth-token', (event, token) => {
Keytar.setPassword('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 { remote } from "electron";
import path from "path";
import store from 'store';
import { remote } from 'electron';
import path from 'path';
import y18n from 'y18n';
const env = process.env.NODE_ENV || "production";
const env = process.env.NODE_ENV || 'production';
const config = {
...require(`./config/${env}`),
...import(`./config/${env}`),
};
const i18n = require("y18n")({
directory: path
.join(remote.app.getAppPath(), "/../static/locales")
.replace(/\\/g, "\\\\"),
const i18n = y18n({
directory: path.join(remote.app.getAppPath(), '/../static/locales').replace(/\\/g, '\\\\'),
updateFiles: false,
locale: "en",
locale: 'en',
});
const logs = [];
@ -26,8 +25,17 @@ const app = {
},
};
window.__ = i18n.__;
window.__n = i18n.__n;
// Workaround for https://github.com/electron-userland/electron-webpack/issues/52
if (env !== 'development') {
window.staticResourcesPath = path
.join(remote.app.getAppPath(), '../static')
.replace(/\\/g, '\\\\');
} else {
window.staticResourcesPath = '';
}
global.app = app;
module.exports = app;
// eslint-disable-next-line no-underscore-dangle
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 CLOSE_MODAL = "CLOSE_MODAL";
export const SHOW_SNACKBAR = "SHOW_SNACKBAR";
export const REMOVE_SNACKBAR_SNACK = "REMOVE_SNACKBAR_SNACK";
export const WINDOW_FOCUSED = "WINDOW_FOCUSED";
export const DAEMON_READY = "DAEMON_READY";
export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH";
export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH";
export const VOLUME_CHANGED = "VOLUME_CHANGED";
export const OPEN_MODAL = 'OPEN_MODAL';
export const CLOSE_MODAL = 'CLOSE_MODAL';
export const SHOW_SNACKBAR = 'SHOW_SNACKBAR';
export const REMOVE_SNACKBAR_SNACK = 'REMOVE_SNACKBAR_SNACK';
export const WINDOW_FOCUSED = 'WINDOW_FOCUSED';
export const DAEMON_READY = 'DAEMON_READY';
export const DAEMON_VERSION_MATCH = 'DAEMON_VERSION_MATCH';
export const DAEMON_VERSION_MISMATCH = 'DAEMON_VERSION_MISMATCH';
export const VOLUME_CHANGED = 'VOLUME_CHANGED';
// Navigation
export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH";
export const WINDOW_SCROLLED = "WINDOW_SCROLLED";
export const HISTORY_NAVIGATE = "HISTORY_NAVIGATE";
export const CHANGE_AFTER_AUTH_PATH = 'CHANGE_AFTER_AUTH_PATH';
export const WINDOW_SCROLLED = 'WINDOW_SCROLLED';
export const HISTORY_NAVIGATE = 'HISTORY_NAVIGATE';
// Upgrades
export const UPGRADE_CANCELLED = "UPGRADE_CANCELLED";
export const DOWNLOAD_UPGRADE = "DOWNLOAD_UPGRADE";
export const UPGRADE_DOWNLOAD_STARTED = "UPGRADE_DOWNLOAD_STARTED";
export const UPGRADE_DOWNLOAD_COMPLETED = "UPGRADE_DOWNLOAD_COMPLETED";
export const UPGRADE_DOWNLOAD_PROGRESSED = "UPGRADE_DOWNLOAD_PROGRESSED";
export const CHECK_UPGRADE_AVAILABLE = "CHECK_UPGRADE_AVAILABLE";
export const CHECK_UPGRADE_START = "CHECK_UPGRADE_START";
export const CHECK_UPGRADE_SUCCESS = "CHECK_UPGRADE_SUCCESS";
export const CHECK_UPGRADE_FAIL = "CHECK_UPGRADE_FAIL";
export const CHECK_UPGRADE_SUBSCRIBE = "CHECK_UPGRADE_SUBSCRIBE";
export const UPDATE_VERSION = "UPDATE_VERSION";
export const UPDATE_REMOTE_VERSION = "UPDATE_REMOTE_VERSION";
export const SKIP_UPGRADE = "SKIP_UPGRADE";
export const START_UPGRADE = "START_UPGRADE";
export const UPGRADE_CANCELLED = 'UPGRADE_CANCELLED';
export const DOWNLOAD_UPGRADE = 'DOWNLOAD_UPGRADE';
export const UPGRADE_DOWNLOAD_STARTED = 'UPGRADE_DOWNLOAD_STARTED';
export const UPGRADE_DOWNLOAD_COMPLETED = 'UPGRADE_DOWNLOAD_COMPLETED';
export const UPGRADE_DOWNLOAD_PROGRESSED = 'UPGRADE_DOWNLOAD_PROGRESSED';
export const CHECK_UPGRADE_AVAILABLE = 'CHECK_UPGRADE_AVAILABLE';
export const CHECK_UPGRADE_START = 'CHECK_UPGRADE_START';
export const CHECK_UPGRADE_SUCCESS = 'CHECK_UPGRADE_SUCCESS';
export const CHECK_UPGRADE_FAIL = 'CHECK_UPGRADE_FAIL';
export const CHECK_UPGRADE_SUBSCRIBE = 'CHECK_UPGRADE_SUBSCRIBE';
export const UPDATE_VERSION = 'UPDATE_VERSION';
export const UPDATE_REMOTE_VERSION = 'UPDATE_REMOTE_VERSION';
export const SKIP_UPGRADE = 'SKIP_UPGRADE';
export const START_UPGRADE = 'START_UPGRADE';
// Wallet
export const GET_NEW_ADDRESS_STARTED = "GET_NEW_ADDRESS_STARTED";
export const GET_NEW_ADDRESS_COMPLETED = "GET_NEW_ADDRESS_COMPLETED";
export const FETCH_TRANSACTIONS_STARTED = "FETCH_TRANSACTIONS_STARTED";
export const FETCH_TRANSACTIONS_COMPLETED = "FETCH_TRANSACTIONS_COMPLETED";
export const UPDATE_BALANCE = "UPDATE_BALANCE";
export const CHECK_ADDRESS_IS_MINE_STARTED = "CHECK_ADDRESS_IS_MINE_STARTED";
export const 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_ADDRESS = "SET_DRAFT_TRANSACTION_ADDRESS";
export const SEND_TRANSACTION_STARTED = "SEND_TRANSACTION_STARTED";
export const SEND_TRANSACTION_COMPLETED = "SEND_TRANSACTION_COMPLETED";
export const SEND_TRANSACTION_FAILED = "SEND_TRANSACTION_FAILED";
export const FETCH_BLOCK_SUCCESS = "FETCH_BLOCK_SUCCESS";
export const SUPPORT_TRANSACTION_STARTED = "SUPPORT_TRANSACTION_STARTED";
export const SUPPORT_TRANSACTION_COMPLETED = "SUPPORT_TRANSACTION_COMPLETED";
export const SUPPORT_TRANSACTION_FAILED = "SUPPORT_TRANSACTION_FAILED";
export const GET_NEW_ADDRESS_STARTED = 'GET_NEW_ADDRESS_STARTED';
export const GET_NEW_ADDRESS_COMPLETED = 'GET_NEW_ADDRESS_COMPLETED';
export const FETCH_TRANSACTIONS_STARTED = 'FETCH_TRANSACTIONS_STARTED';
export const FETCH_TRANSACTIONS_COMPLETED = 'FETCH_TRANSACTIONS_COMPLETED';
export const UPDATE_BALANCE = 'UPDATE_BALANCE';
export const CHECK_ADDRESS_IS_MINE_STARTED = 'CHECK_ADDRESS_IS_MINE_STARTED';
export const 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_ADDRESS = 'SET_DRAFT_TRANSACTION_ADDRESS';
export const SEND_TRANSACTION_STARTED = 'SEND_TRANSACTION_STARTED';
export const SEND_TRANSACTION_COMPLETED = 'SEND_TRANSACTION_COMPLETED';
export const SEND_TRANSACTION_FAILED = 'SEND_TRANSACTION_FAILED';
export const FETCH_BLOCK_SUCCESS = 'FETCH_BLOCK_SUCCESS';
export const SUPPORT_TRANSACTION_STARTED = 'SUPPORT_TRANSACTION_STARTED';
export const SUPPORT_TRANSACTION_COMPLETED = 'SUPPORT_TRANSACTION_COMPLETED';
export const SUPPORT_TRANSACTION_FAILED = 'SUPPORT_TRANSACTION_FAILED';
// Claims
export const FETCH_FEATURED_CONTENT_STARTED = "FETCH_FEATURED_CONTENT_STARTED";
export const FETCH_FEATURED_CONTENT_COMPLETED =
"FETCH_FEATURED_CONTENT_COMPLETED";
export const RESOLVE_URIS_STARTED = "RESOLVE_URIS_STARTED";
export const RESOLVE_URIS_COMPLETED = "RESOLVE_URIS_COMPLETED";
export const FETCH_CHANNEL_CLAIMS_STARTED = "FETCH_CHANNEL_CLAIMS_STARTED";
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_COMPLETED =
"FETCH_CHANNEL_CLAIM_COUNT_COMPLETED";
export const FETCH_CLAIM_LIST_MINE_STARTED = "FETCH_CLAIM_LIST_MINE_STARTED";
export const FETCH_CLAIM_LIST_MINE_COMPLETED =
"FETCH_CLAIM_LIST_MINE_COMPLETED";
export const ABANDON_CLAIM_STARTED = "ABANDON_CLAIM_STARTED";
export const ABANDON_CLAIM_SUCCEEDED = "ABANDON_CLAIM_SUCCEEDED";
export const FETCH_CHANNEL_LIST_MINE_STARTED =
"FETCH_CHANNEL_LIST_MINE_STARTED";
export const FETCH_CHANNEL_LIST_MINE_COMPLETED =
"FETCH_CHANNEL_LIST_MINE_COMPLETED";
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";
export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';
export const FETCH_FEATURED_CONTENT_COMPLETED = 'FETCH_FEATURED_CONTENT_COMPLETED';
export const RESOLVE_URIS_STARTED = 'RESOLVE_URIS_STARTED';
export const RESOLVE_URIS_COMPLETED = 'RESOLVE_URIS_COMPLETED';
export const FETCH_CHANNEL_CLAIMS_STARTED = 'FETCH_CHANNEL_CLAIMS_STARTED';
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_COMPLETED = 'FETCH_CHANNEL_CLAIM_COUNT_COMPLETED';
export const FETCH_CLAIM_LIST_MINE_STARTED = 'FETCH_CLAIM_LIST_MINE_STARTED';
export const FETCH_CLAIM_LIST_MINE_COMPLETED = 'FETCH_CLAIM_LIST_MINE_COMPLETED';
export const ABANDON_CLAIM_STARTED = 'ABANDON_CLAIM_STARTED';
export const ABANDON_CLAIM_SUCCEEDED = 'ABANDON_CLAIM_SUCCEEDED';
export const FETCH_CHANNEL_LIST_MINE_STARTED = 'FETCH_CHANNEL_LIST_MINE_STARTED';
export const FETCH_CHANNEL_LIST_MINE_COMPLETED = 'FETCH_CHANNEL_LIST_MINE_COMPLETED';
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
export const FILE_LIST_STARTED = "FILE_LIST_STARTED";
export const FILE_LIST_SUCCEEDED = "FILE_LIST_SUCCEEDED";
export const FETCH_FILE_INFO_STARTED = "FETCH_FILE_INFO_STARTED";
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_COMPLETED = "FETCH_COST_INFO_COMPLETED";
export const LOADING_VIDEO_STARTED = "LOADING_VIDEO_STARTED";
export const LOADING_VIDEO_COMPLETED = "LOADING_VIDEO_COMPLETED";
export const LOADING_VIDEO_FAILED = "LOADING_VIDEO_FAILED";
export const DOWNLOADING_STARTED = "DOWNLOADING_STARTED";
export const DOWNLOADING_PROGRESSED = "DOWNLOADING_PROGRESSED";
export const DOWNLOADING_COMPLETED = "DOWNLOADING_COMPLETED";
export const PLAY_VIDEO_STARTED = "PLAY_VIDEO_STARTED";
export const FETCH_AVAILABILITY_STARTED = "FETCH_AVAILABILITY_STARTED";
export const FETCH_AVAILABILITY_COMPLETED = "FETCH_AVAILABILITY_COMPLETED";
export const FILE_DELETE = "FILE_DELETE";
export const FILE_LIST_STARTED = 'FILE_LIST_STARTED';
export const FILE_LIST_SUCCEEDED = 'FILE_LIST_SUCCEEDED';
export const FETCH_FILE_INFO_STARTED = 'FETCH_FILE_INFO_STARTED';
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_COMPLETED = 'FETCH_COST_INFO_COMPLETED';
export const LOADING_VIDEO_STARTED = 'LOADING_VIDEO_STARTED';
export const LOADING_VIDEO_COMPLETED = 'LOADING_VIDEO_COMPLETED';
export const LOADING_VIDEO_FAILED = 'LOADING_VIDEO_FAILED';
export const DOWNLOADING_STARTED = 'DOWNLOADING_STARTED';
export const DOWNLOADING_PROGRESSED = 'DOWNLOADING_PROGRESSED';
export const DOWNLOADING_COMPLETED = 'DOWNLOADING_COMPLETED';
export const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED';
export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED';
export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED';
export const FILE_DELETE = 'FILE_DELETE';
// Search
export const SEARCH_STARTED = "SEARCH_STARTED";
export const SEARCH_COMPLETED = "SEARCH_COMPLETED";
export const SEARCH_CANCELLED = "SEARCH_CANCELLED";
export const SEARCH_STARTED = 'SEARCH_STARTED';
export const SEARCH_COMPLETED = 'SEARCH_COMPLETED';
export const SEARCH_CANCELLED = 'SEARCH_CANCELLED';
// Settings
export const DAEMON_SETTINGS_RECEIVED = "DAEMON_SETTINGS_RECEIVED";
export const CLIENT_SETTING_CHANGED = "CLIENT_SETTING_CHANGED";
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
export const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED';
// User
export const AUTHENTICATION_STARTED = "AUTHENTICATION_STARTED";
export const AUTHENTICATION_SUCCESS = "AUTHENTICATION_SUCCESS";
export const AUTHENTICATION_FAILURE = "AUTHENTICATION_FAILURE";
export const USER_EMAIL_DECLINE = "USER_EMAIL_DECLINE";
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_EXISTS = "USER_EMAIL_NEW_EXISTS";
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_SUCCESS = "USER_EMAIL_VERIFY_SUCCESS";
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_SUCCESS = "USER_IDENTITY_VERIFY_SUCCESS";
export const USER_IDENTITY_VERIFY_FAILURE = "USER_IDENTITY_VERIFY_FAILURE";
export const USER_FETCH_STARTED = "USER_FETCH_STARTED";
export const USER_FETCH_SUCCESS = "USER_FETCH_SUCCESS";
export const USER_FETCH_FAILURE = "USER_FETCH_FAILURE";
export const 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_FAILURE =
"USER_INVITE_STATUS_FETCH_FAILURE";
export const USER_INVITE_NEW_STARTED = "USER_INVITE_NEW_STARTED";
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";
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
export const AUTHENTICATION_SUCCESS = 'AUTHENTICATION_SUCCESS';
export const AUTHENTICATION_FAILURE = 'AUTHENTICATION_FAILURE';
export const USER_EMAIL_DECLINE = 'USER_EMAIL_DECLINE';
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_EXISTS = 'USER_EMAIL_NEW_EXISTS';
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_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS';
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_SUCCESS = 'USER_IDENTITY_VERIFY_SUCCESS';
export const USER_IDENTITY_VERIFY_FAILURE = 'USER_IDENTITY_VERIFY_FAILURE';
export const USER_FETCH_STARTED = 'USER_FETCH_STARTED';
export const USER_FETCH_SUCCESS = 'USER_FETCH_SUCCESS';
export const USER_FETCH_FAILURE = 'USER_FETCH_FAILURE';
export const 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_FAILURE = 'USER_INVITE_STATUS_FETCH_FAILURE';
export const USER_INVITE_NEW_STARTED = 'USER_INVITE_NEW_STARTED';
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
export const FETCH_REWARDS_STARTED = "FETCH_REWARDS_STARTED";
export const FETCH_REWARDS_COMPLETED = "FETCH_REWARDS_COMPLETED";
export const CLAIM_REWARD_STARTED = "CLAIM_REWARD_STARTED";
export const CLAIM_REWARD_SUCCESS = "CLAIM_REWARD_SUCCESS";
export const CLAIM_REWARD_FAILURE = "CLAIM_REWARD_FAILURE";
export const CLAIM_REWARD_CLEAR_ERROR = "CLAIM_REWARD_CLEAR_ERROR";
export const FETCH_REWARD_CONTENT_COMPLETED = "FETCH_REWARD_CONTENT_COMPLETED";
export const FETCH_REWARDS_STARTED = 'FETCH_REWARDS_STARTED';
export const FETCH_REWARDS_COMPLETED = 'FETCH_REWARDS_COMPLETED';
export const CLAIM_REWARD_STARTED = 'CLAIM_REWARD_STARTED';
export const CLAIM_REWARD_SUCCESS = 'CLAIM_REWARD_SUCCESS';
export const CLAIM_REWARD_FAILURE = 'CLAIM_REWARD_FAILURE';
export const CLAIM_REWARD_CLEAR_ERROR = 'CLAIM_REWARD_CLEAR_ERROR';
export const FETCH_REWARD_CONTENT_COMPLETED = 'FETCH_REWARD_CONTENT_COMPLETED';
// Language
export const DOWNLOAD_LANGUAGE_SUCCEEDED = "DOWNLOAD_LANGUAGE_SUCCEEDED";
export const DOWNLOAD_LANGUAGE_FAILED = "DOWNLOAD_LANGUAGE_FAILED";
export const DOWNLOAD_LANGUAGE_SUCCEEDED = 'DOWNLOAD_LANGUAGE_SUCCEEDED';
export const DOWNLOAD_LANGUAGE_FAILED = 'DOWNLOAD_LANGUAGE_FAILED';
// ShapeShift
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_FAIL = "GET_SUPPORTED_COINS_FAIL";
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_FAIL = "GET_COIN_STATS_FAIL";
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_FAIL = "PREPARE_SHAPE_SHIFT_FAIL";
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_FAIL = "GET_ACTIVE_SHIFT_FAIL";
export const CLEAR_SHAPE_SHIFT = "CLEAR_SHAPE_SHIFT";
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_FAIL = 'GET_SUPPORTED_COINS_FAIL';
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_FAIL = 'GET_COIN_STATS_FAIL';
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_FAIL = 'PREPARE_SHAPE_SHIFT_FAIL';
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_FAIL = 'GET_ACTIVE_SHIFT_FAIL';
export const CLEAR_SHAPE_SHIFT = 'CLEAR_SHAPE_SHIFT';
// Subscriptions
export const CHANNEL_SUBSCRIBE = "CHANNEL_SUBSCRIBE";
export const CHANNEL_UNSUBSCRIBE = "CHANNEL_UNSUBSCRIBE";
export const HAS_FETCHED_SUBSCRIPTIONS = "HAS_FETCHED_SUBSCRIPTIONS";
export const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
export const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';

View file

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

View file

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

View file

@ -1,15 +1,15 @@
export const CONFIRM_FILE_REMOVE = "confirmFileRemove";
export const INCOMPATIBLE_DAEMON = "incompatibleDaemon";
export const FILE_TIMEOUT = "file_timeout";
export const DOWNLOADING = "downloading";
export const ERROR = "error";
export const INSUFFICIENT_CREDITS = "insufficient_credits";
export const UPGRADE = "upgrade";
export const WELCOME = "welcome";
export const EMAIL_COLLECTION = "email_collection";
export const FIRST_REWARD = "first_reward";
export const AUTHENTICATION_FAILURE = "auth_failure";
export const TRANSACTION_FAILED = "transaction_failed";
export const REWARD_APPROVAL_REQUIRED = "reward_approval_required";
export const AFFIRM_PURCHASE = "affirm_purchase";
export const CONFIRM_CLAIM_REVOKE = "confirmClaimRevoke";
export const CONFIRM_FILE_REMOVE = 'confirmFileRemove';
export const INCOMPATIBLE_DAEMON = 'incompatibleDaemon';
export const FILE_TIMEOUT = 'file_timeout';
export const DOWNLOADING = 'downloading';
export const ERROR = 'error';
export const INSUFFICIENT_CREDITS = 'insufficient_credits';
export const UPGRADE = 'upgrade';
export const WELCOME = 'welcome';
export const EMAIL_COLLECTION = 'email_collection';
export const FIRST_REWARD = 'first_reward';
export const AUTHENTICATION_FAILURE = 'auth_failure';
export const TRANSACTION_FAILED = 'transaction_failed';
export const REWARD_APPROVAL_REQUIRED = 'reward_approval_required';
export const AFFIRM_PURCHASE = 'affirm_purchase';
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 */
/* 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 */
export const CREDIT_REQUIRED_ACKNOWLEDGED = "credit_required_acknowledged";
export const NEW_USER_ACKNOWLEDGED = "welcome_acknowledged";
export const EMAIL_COLLECTION_ACKNOWLEDGED = "email_collection_acknowledged";
export const LANGUAGE = "language";
export const SHOW_NSFW = "showNsfw";
export const SHOW_UNAVAILABLE = "showUnavailable";
export const INSTANT_PURCHASE_ENABLED = "instantPurchaseEnabled";
export const INSTANT_PURCHASE_MAX = "instantPurchaseMax";
export const THEME = "theme";
export const THEMES = "themes";
/* 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 */
export const CREDIT_REQUIRED_ACKNOWLEDGED = 'credit_required_acknowledged';
export const NEW_USER_ACKNOWLEDGED = 'welcome_acknowledged';
export const EMAIL_COLLECTION_ACKNOWLEDGED = 'email_collection_acknowledged';
export const LANGUAGE = 'language';
export const SHOW_NSFW = 'showNsfw';
export const SHOW_UNAVAILABLE = 'showUnavailable';
export const INSTANT_PURCHASE_ENABLED = 'instantPurchaseEnabled';
export const INSTANT_PURCHASE_MAX = 'instantPurchaseMax';
export const THEME = 'theme';
export const THEMES = 'themes';

View file

@ -1,3 +1,3 @@
export const NO_DEPOSITS = "no_deposits";
export const RECEIVED = "received";
export const COMPLETE = "complete";
export const NO_DEPOSITS = 'no_deposits';
export const RECEIVED = 'received';
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.exports: any;
}

View file

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

View file

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

View file

@ -1,5 +1,31 @@
import jsonrpc from "./jsonrpc.js";
import lbryuri from "./lbryuri.js";
import jsonrpc from 'jsonrpc';
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) {
const itemRaw = localStorage.getItem(key);
@ -10,49 +36,24 @@ function setLocal(key, 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
* needed to make a dummy claim or file info object.
*/
let pendingId = 0;
function savePendingPublish({ name, channel_name }) {
let uri;
if (channel_name) {
uri = lbryuri.build({ name: channel_name, path: name }, false);
} else {
uri = lbryuri.build({ name }, false);
}
++pendingId;
const pendingPublishes = getLocal("pendingPublishes") || [];
function savePendingPublish({ name, channelName }) {
pendingId += 1;
const pendingPublishes = getLocal('pendingPublishes') || [];
const newPendingPublish = {
name,
channel_name,
channelName,
claim_id: `pending-${pendingId}`,
txid: `pending-${pendingId}`,
nout: 0,
outpoint: `pending-${pendingId}:0`,
time: Date.now(),
};
setLocal("pendingPublishes", [...pendingPublishes, newPendingPublish]);
setLocal('pendingPublishes', [...pendingPublishes, 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.
* A channel name may also be provided along with name.
*/
function removePendingPublishIfNeeded({ name, channel_name, outpoint }) {
function removePendingPublishIfNeeded({ name, channelName, outpoint }) {
function pubMatches(pub) {
return (
pub.outpoint === outpoint ||
(pub.name === name &&
(!channel_name || pub.channel_name === channel_name))
(pub.name === name && (!channelName || pub.channel_name === channelName))
);
}
setLocal(
"pendingPublishes",
lbry.getPendingPublishes().filter(pub => !pubMatches(pub))
);
setLocal('pendingPublishes', Lbry.getPendingPublishes().filter(pub => !pubMatches(pub)));
}
/**
* Gets the current list of pending publish attempts. Filters out any that have timed out and
* removes them from the list.
*/
lbry.getPendingPublishes = function() {
const pendingPublishes = getLocal("pendingPublishes") || [];
Lbry.getPendingPublishes = function() {
const pendingPublishes = getLocal('pendingPublishes') || [];
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;
};
@ -92,65 +89,52 @@ lbry.getPendingPublishes = function() {
* 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.
*/
function getPendingPublish({ name, channel_name, outpoint }) {
const pendingPublishes = lbry.getPendingPublishes();
function getPendingPublish({ name, channelName, outpoint }) {
const pendingPublishes = Lbry.getPendingPublishes();
return (
pendingPublishes.find(
pub =>
pub.outpoint === outpoint ||
(pub.name === name &&
(!channel_name || pub.channel_name === channel_name))
(pub.name === name && (!channelName || pub.channel_name === channelName))
) || null
);
}
function pendingPublishToDummyClaim({
channel_name,
name,
outpoint,
claim_id,
txid,
nout,
}) {
return { name, outpoint, claim_id, txid, nout, channel_name };
function pendingPublishToDummyClaim({ channelName, name, outpoint, claimId, txid, nout }) {
return { name, outpoint, claimId, txid, nout, channelName };
}
function pendingPublishToDummyFileInfo({ name, outpoint, claim_id }) {
return { name, outpoint, claim_id, metadata: null };
function pendingPublishToDummyFileInfo({ name, outpoint, claimId }) {
return { name, outpoint, claimId, metadata: null };
}
// core
lbry._connectPromise = null;
lbry.connect = function() {
if (lbry._connectPromise === null) {
lbry._connectPromise = new Promise((resolve, reject) => {
Lbry.connectPromise = null;
Lbry.connect = function() {
if (Lbry.connectPromise === null) {
Lbry.connectPromise = new Promise((resolve, reject) => {
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
function checkDaemonStarted() {
lbry
tryNum += 1;
lbryProxy
.status()
.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();
});
}
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
* publish can appear in the UI immediately.
*/
lbry.publishDeprecated = function(
params,
fileListedCallback,
publishedCallback,
errorCallback
) {
lbry.publish(params).then(
Lbry.publishDeprecated = function(params, fileListedCallback, publishedCallback, errorCallback) {
// Give a short grace period in case publish() returns right away or (more likely) gives an error
const returnPendingTimeout = setTimeout(
() => {
const { name, channel_name: channelName } = params;
if (publishedCallback || fileListedCallback) {
savePendingPublish({
name,
channelName,
});
publishedCallback(true);
}
},
2000,
{ once: true }
);
lbryProxy.publish(params).then(
result => {
if (returnPendingTimeout) clearTimeout(returnPendingTimeout);
publishedCallback(result);
@ -176,66 +171,40 @@ lbry.publishDeprecated = function(
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}`;
};
lbry.getMediaType = function(contentType, fileName) {
Lbry.getMediaType = function(contentType, fileName) {
if (contentType) {
return /^[^/]+/.exec(contentType)[0];
} else if (fileName) {
const dotIndex = fileName.lastIndexOf(".");
if (dotIndex == -1) {
return "unknown";
const dotIndex = fileName.lastIndexOf('.');
if (dotIndex === -1) {
return 'unknown';
}
const ext = fileName.substr(dotIndex + 1);
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)) {
return "audio";
} else if (
/^html|htm|xml|pdf|odf|doc|docx|md|markdown|txt|epub|org$/i.test(ext)
) {
return "document";
return 'audio';
} else if (/^html|htm|xml|pdf|odf|doc|docx|md|markdown|txt|epub|org$/i.test(ext)) {
return 'document';
}
return "unknown";
return 'unknown';
}
return "unknown";
return 'unknown';
};
lbry.getAppVersionInfo = function() {
return new Promise((resolve, reject) => {
ipcRenderer.once("version-info-received", (event, versionInfo) => {
Lbry.getAppVersionInfo = function() {
return new Promise(resolve => {
ipcRenderer.once('version-info-received', (event, 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.
* (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) => {
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.
@ -266,16 +235,14 @@ lbry.file_list = function(params = {}) {
}
apiCall(
"file_list",
'file_list',
params,
fileInfos => {
removePendingPublishIfNeeded({ name, channel_name, outpoint });
removePendingPublishIfNeeded({ name, channelName, outpoint });
// if a naked file_list call, append the pending file infos
if (!name && !channel_name && !outpoint) {
const dummyFileInfos = lbry
.getPendingPublishes()
.map(pendingPublishToDummyFileInfo);
if (!name && !channelName && !outpoint) {
const dummyFileInfos = Lbry.getPendingPublishes().map(pendingPublishToDummyFileInfo);
resolve([...fileInfos, ...dummyFileInfos]);
} 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) => {
apiCall(
"claim_list_mine",
'claim_list_mine',
params,
claims => {
for (const { name, channel_name, txid, nout } of claims) {
claims.forEach(({ name, channel_name: channelName, txid, nout }) => {
removePendingPublishIfNeeded({
name,
channel_name,
channelName,
outpoint: `${txid}:${nout}`,
});
}
});
const dummyClaims = lbry
.getPendingPublishes()
.map(pendingPublishToDummyClaim);
const dummyClaims = Lbry.getPendingPublishes().map(pendingPublishToDummyClaim);
resolve([...claims, ...dummyClaims]);
},
reject
@ -311,13 +276,13 @@ lbry.claim_list_mine = function(params = {}) {
});
};
lbry.resolve = function(params = {}) {
Lbry.resolve = function(params = {}) {
return new Promise((resolve, reject) => {
apiCall(
"resolve",
'resolve',
params,
data => {
if ("uri" in params) {
if ('uri' in params) {
// If only a single URI was requested, don't nest the results in an object
resolve(data && data[params.uri] ? data[params.uri] : {});
} else {
@ -329,18 +294,4 @@ lbry.resolve = function(params = {}) {
});
};
lbry = 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);
});
};
},
});
export default lbry;
export default lbryProxy;

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,31 +1,18 @@
import * as types from "constants/action_types";
import lbry from "lbry";
import lbryio from "lbryio";
import { doResolveUri } from "redux/actions/content";
import { selectResolvingUris } from "redux/selectors/content";
import { selectClaimsByUri } from "redux/selectors/claims";
import { selectSettingsIsGenerous } from "redux/selectors/settings";
import * as ACTIONS from 'constants/action_types';
import Lbryio from 'lbryio';
import { selectClaimsByUri } from 'redux/selectors/claims';
// eslint-disable-next-line import/prefer-default-export
export function doFetchCostInfoForUri(uri) {
return function(dispatch, getState) {
const state = getState(),
claim = selectClaimsByUri(state)[uri],
isGenerous = selectSettingsIsGenerous(state);
const state = getState();
const claim = selectClaimsByUri(state)[uri];
if (!claim) return null;
function begin() {
dispatch({
type: types.FETCH_COST_INFO_STARTED,
data: {
uri,
},
});
}
if (!claim) return;
function resolve(costInfo) {
dispatch({
type: types.FETCH_COST_INFO_COMPLETED,
type: ACTIONS.FETCH_COST_INFO_COMPLETED,
data: {
uri,
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 =
claim.value && claim.value.stream && claim.value.stream.metadata
? claim.value.stream.metadata.fee
@ -52,35 +27,12 @@ export function doFetchCostInfoForUri(uri) {
if (fee === undefined) {
resolve({ cost: 0, includesData: true });
} else if (fee.currency == "LBC") {
} 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 });
Lbryio.getExchangeRates().then(({ LBC_USD }) => {
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 lbry from "lbry";
import { doFetchClaimListMine, doAbandonClaim } from "redux/actions/content";
import * as ACTIONS from 'constants/action_types';
import Lbry from 'lbry';
import { doFetchClaimListMine, doAbandonClaim } from 'redux/actions/content';
import {
selectClaimsByUri,
selectIsFetchingClaimListMine,
selectMyClaimsOutpoints,
} from "redux/selectors/claims";
} from 'redux/selectors/claims';
import {
selectIsFetchingFileList,
selectFileInfosByOutpoint,
selectUrisLoading,
selectTotalDownloadProgress,
} from "redux/selectors/file_info";
import { doCloseModal } from "redux/actions/app";
import { doNavigate, doHistoryBack } from "redux/actions/navigation";
import setProgressBar from "util/setProgressBar";
import batchActions from "util/batchActions";
const { shell } = require("electron");
} from 'redux/selectors/file_info';
import { doCloseModal } from 'redux/actions/app';
import { doHistoryBack } from 'redux/actions/navigation';
import setProgressBar from 'util/setProgressBar';
import batchActions from 'util/batchActions';
import { shell } from 'electron';
export function doFetchFileInfo(uri) {
return function(dispatch, getState) {
@ -28,15 +27,15 @@ export function doFetchFileInfo(uri) {
if (!alreadyFetching) {
dispatch({
type: types.FETCH_FILE_INFO_STARTED,
type: ACTIONS.FETCH_FILE_INFO_STARTED,
data: {
outpoint,
},
});
lbry.file_list({ outpoint, full_status: true }).then(fileInfos => {
Lbry.file_list({ outpoint, full_status: true }).then(fileInfos => {
dispatch({
type: types.FETCH_FILE_INFO_COMPLETED,
type: ACTIONS.FETCH_FILE_INFO_COMPLETED,
data: {
outpoint,
fileInfo: fileInfos && fileInfos.length ? fileInfos[0] : null,
@ -54,12 +53,12 @@ export function doFileList() {
if (!isFetching) {
dispatch({
type: types.FILE_LIST_STARTED,
type: ACTIONS.FILE_LIST_STARTED,
});
lbry.file_list().then(fileInfos => {
Lbry.file_list().then(fileInfos => {
dispatch({
type: types.FILE_LIST_SUCCEEDED,
type: ACTIONS.FILE_LIST_SUCCEEDED,
data: {
fileInfos,
},
@ -69,8 +68,14 @@ export function doFileList() {
};
}
export function doOpenFileInFolder(path) {
return function() {
shell.showItemInFolder(path);
};
}
export function doOpenFileInShell(path) {
return function(dispatch, getState) {
return function(dispatch) {
const success = shell.openItem(path);
if (!success) {
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) {
return function(dispatch, getState) {
const state = getState();
lbry.file_delete({
Lbry.file_delete({
outpoint,
delete_from_download_dir: deleteFromComputer,
});
@ -108,7 +107,7 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
}
dispatch({
type: types.FILE_DELETE,
type: ACTIONS.FILE_DELETE,
data: {
outpoint,
},
@ -119,12 +118,8 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
};
}
export function doDeleteFileAndGoBack(
fileInfo,
deleteFromComputer,
abandonClaim
) {
return function(dispatch, getState) {
export function doDeleteFileAndGoBack(fileInfo, deleteFromComputer, abandonClaim) {
return function(dispatch) {
const actions = [];
actions.push(doCloseModal());
actions.push(doHistoryBack());
@ -135,9 +130,9 @@ export function doDeleteFileAndGoBack(
export function doFetchFileInfosAndPublishedClaims() {
return function(dispatch, getState) {
const state = getState(),
isFetchingClaimListMine = selectIsFetchingClaimListMine(state),
isFetchingFileInfo = selectIsFetchingFileList(state);
const state = getState();
const isFetchingClaimListMine = selectIsFetchingClaimListMine(state);
const isFetchingFileInfo = selectIsFetchingFileList(state);
if (!isFetchingClaimListMine) dispatch(doFetchClaimListMine());
if (!isFetchingFileInfo) dispatch(doFileList());

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
// @flow
import Promise from "bluebird";
import * as types from "constants/action_types";
import { coinRegexPatterns } from "util/shape_shift";
import Promise from 'bluebird';
import * as ACTIONS from 'constants/action_types';
import { coinRegexPatterns } from 'util/shape_shift';
import type {
GetSupportedCoinsSuccess,
GetCoinStatsStart,
@ -11,26 +11,26 @@ import type {
PrepareShapeShiftFail,
GetActiveShiftSuccess,
GetActiveShiftFail,
} from "redux/reducers/shape_shift";
import type { FormikActions } from "types/common";
} from 'redux/reducers/shape_shift';
import type { FormikActions } from 'types/common';
// 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
// Action types defined in the reducer will contain some payload
export type Action =
| { type: types.GET_SUPPORTED_COINS_START }
| { type: types.GET_SUPPORTED_COINS_FAIL }
| { type: ACTIONS.GET_SUPPORTED_COINS_START }
| { type: ACTIONS.GET_SUPPORTED_COINS_FAIL }
| GetSupportedCoinsSuccess
| GetCoinStatsStart
| { type: types.GET_COIN_STATS_START }
| { type: ACTIONS.GET_COIN_STATS_START }
| GetCoinStatsFail
| GetCoinStatsSuccess
| { type: types.PREPARE_SHAPE_SHIFT_START }
| { type: ACTIONS.PREPARE_SHAPE_SHIFT_START }
| PrepareShapeShiftFail
| PrepareShapeShiftSuccess
| { type: types.GET_ACTIVE_SHIFT_START }
| { type: ACTIONS.GET_ACTIVE_SHIFT_START }
| GetActiveShiftFail
| GetActiveShiftSuccess;
@ -38,10 +38,8 @@ export type Action =
// It would be nice to import these from types/common
// Not sure how that would work since they rely on the Action type
type PromiseAction = Promise<Action>;
export type Dispatch = (action: Action | PromiseAction | Array<Action>) => any;
type ThunkAction = (dispatch: Dispatch) => any;
export type Dispatch = (
action: Action | ThunkAction | PromiseAction | Array<Action>
) => any;
// ShapeShift form values
export type ShapeShiftFormValues = {
@ -50,15 +48,26 @@ export type ShapeShiftFormValues = {
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 => {
dispatch({ type: types.GET_SUPPORTED_COINS_START });
dispatch({ type: ACTIONS.GET_SUPPORTED_COINS_START });
return shapeShift
.coinsAsync()
.then(coinData => {
let supportedCoins = [];
Object.keys(coinData).forEach(symbol => {
if (coinData[symbol].status === "available") {
if (coinData[symbol].status === 'available') {
supportedCoins.push(coinData[symbol]);
}
});
@ -69,69 +78,43 @@ export const shapeShiftInit = () => (dispatch: Dispatch): ThunkAction => {
.map(coin => coin.symbol);
dispatch({
type: types.GET_SUPPORTED_COINS_SUCCESS,
type: ACTIONS.GET_SUPPORTED_COINS_SUCCESS,
data: supportedCoins,
});
dispatch(getCoinStats(supportedCoins[0]));
})
.catch(err =>
dispatch({ type: types.GET_SUPPORTED_COINS_FAIL, data: err })
);
.catch(err => dispatch({ type: ACTIONS.GET_SUPPORTED_COINS_FAIL, data: err }));
};
export const getCoinStats = (coin: string) => (
export const createShapeShift = (values: ShapeShiftFormValues, actions: FormikActions) => (
dispatch: Dispatch
): ThunkAction => {
const pair = `${coin.toLowerCase()}_lbc`;
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 { originCoin, returnAddress, receiveAddress: withdrawalAddress } = values;
const pair = `${originCoin.toLowerCase()}_lbc`;
const options = {
returnAddress,
};
dispatch({ type: types.PREPARE_SHAPE_SHIFT_START });
dispatch({ type: ACTIONS.PREPARE_SHAPE_SHIFT_START });
return shapeShift
.shiftAsync(withdrawalAddress, pair, options)
.then(res =>
dispatch({ type: types.PREPARE_SHAPE_SHIFT_SUCCESS, data: res })
)
.then(res => dispatch({ type: ACTIONS.PREPARE_SHAPE_SHIFT_SUCCESS, data: res }))
.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
actions.setSubmitting(false);
});
};
export const getActiveShift = (depositAddress: string) => (
dispatch: Dispatch
): ThunkAction => {
dispatch({ type: types.GET_ACTIVE_SHIFT_START });
export const getActiveShift = (depositAddress: string) => (dispatch: Dispatch): ThunkAction => {
dispatch({ type: ACTIONS.GET_ACTIVE_SHIFT_START });
return shapeShift
.statusAsync(depositAddress)
.then(res => dispatch({ type: types.GET_ACTIVE_SHIFT_SUCCESS, data: res }))
.catch(err => dispatch({ type: types.GET_ACTIVE_SHIFT_FAIL, data: err }));
.then(res => dispatch({ type: ACTIONS.GET_ACTIVE_SHIFT_SUCCESS, data: res }))
.catch(err => dispatch({ type: ACTIONS.GET_ACTIVE_SHIFT_FAIL, data: err }));
};
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
import * as actions from "constants/action_types";
import type {
Subscription,
Action,
Dispatch,
} from "redux/reducers/subscriptions";
import lbry from "lbry";
import * as ACTIONS from 'constants/action_types';
import type { Subscription, Dispatch } from 'redux/reducers/subscriptions';
export const doChannelSubscribe = (subscription: Subscription) => (
dispatch: Dispatch
) =>
export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dispatch) =>
dispatch({
type: actions.CHANNEL_SUBSCRIBE,
type: ACTIONS.CHANNEL_SUBSCRIBE,
data: subscription,
});
export const doChannelUnsubscribe = (subscription: Subscription) => (
dispatch: Dispatch
) =>
export const doChannelUnsubscribe = (subscription: Subscription) => (dispatch: Dispatch) =>
dispatch({
type: actions.CHANNEL_UNSUBSCRIBE,
type: ACTIONS.CHANNEL_UNSUBSCRIBE,
data: subscription,
});
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 modals from "constants/modal_types";
import lbryio from "lbryio";
import { doOpenModal, doShowSnackBar } from "redux/actions/app";
import { doRewardList, doClaimRewardType } from "redux/actions/rewards";
import { selectEmailToVerify, selectUser } from "redux/selectors/user";
import rewards from "rewards";
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import Lbryio from 'lbryio';
import { doOpenModal, doShowSnackBar } from 'redux/actions/app';
import { doRewardList, doClaimRewardType } from 'redux/actions/rewards';
import { selectEmailToVerify } from 'redux/selectors/user';
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() {
return function(dispatch, getState) {
return function(dispatch) {
dispatch({
type: types.AUTHENTICATION_STARTED,
type: ACTIONS.AUTHENTICATION_STARTED,
});
lbryio
.authenticate()
Lbryio.authenticate()
.then(user => {
dispatch({
type: types.AUTHENTICATION_SUCCESS,
type: ACTIONS.AUTHENTICATION_SUCCESS,
data: { user },
});
dispatch(doRewardList());
dispatch(doFetchInviteStatus());
})
.catch(error => {
dispatch(doOpenModal(modals.AUTHENTICATION_FAILURE));
dispatch(doOpenModal(MODALS.AUTHENTICATION_FAILURE));
dispatch({
type: types.AUTHENTICATION_FAILURE,
type: ACTIONS.AUTHENTICATION_FAILURE,
data: { error },
});
});
@ -32,23 +56,22 @@ export function doAuthenticate() {
}
export function doUserFetch() {
return function(dispatch, getState) {
return function(dispatch) {
dispatch({
type: types.USER_FETCH_STARTED,
type: ACTIONS.USER_FETCH_STARTED,
});
lbryio
.getCurrentUser()
Lbryio.getCurrentUser()
.then(user => {
dispatch(doRewardList());
dispatch({
type: types.USER_FETCH_SUCCESS,
type: ACTIONS.USER_FETCH_SUCCESS,
data: { user },
});
})
.catch(error => {
dispatch({
type: types.USER_FETCH_FAILURE,
type: ACTIONS.USER_FETCH_FAILURE,
data: { error },
});
});
@ -56,15 +79,15 @@ export function doUserFetch() {
}
export function doUserEmailNew(email) {
return function(dispatch, getState) {
return function(dispatch) {
dispatch({
type: types.USER_EMAIL_NEW_STARTED,
type: ACTIONS.USER_EMAIL_NEW_STARTED,
email,
});
const success = () => {
dispatch({
type: types.USER_EMAIL_NEW_SUCCESS,
type: ACTIONS.USER_EMAIL_NEW_SUCCESS,
data: { email },
});
dispatch(doUserFetch());
@ -72,28 +95,20 @@ export function doUserEmailNew(email) {
const failure = error => {
dispatch({
type: types.USER_EMAIL_NEW_FAILURE,
type: ACTIONS.USER_EMAIL_NEW_FAILURE,
data: { error },
});
};
lbryio
.call(
"user_email",
"new",
{ email, send_verification_email: true },
"post"
)
Lbryio.call('user_email', 'new', { email, send_verification_email: true }, 'post')
.catch(error => {
if (error.response && error.response.status == 409) {
return lbryio
.call(
"user_email",
"resend_token",
{ email, only_if_expired: true },
"post"
)
.then(success, failure);
if (error.response && error.response.status === 409) {
return Lbryio.call(
'user_email',
'resend_token',
{ email, only_if_expired: true },
'post'
).then(success, failure);
}
throw error;
})
@ -104,35 +119,34 @@ export function doUserEmailNew(email) {
export function doUserEmailVerify(verificationToken) {
return function(dispatch, getState) {
const email = selectEmailToVerify(getState());
verificationToken = verificationToken.toString().trim();
const trimmedVerificationToken = verificationToken.toString().trim();
dispatch({
type: types.USER_EMAIL_VERIFY_STARTED,
code: verificationToken,
type: ACTIONS.USER_EMAIL_VERIFY_STARTED,
code: trimmedVerificationToken,
});
lbryio
.call(
"user_email",
"confirm",
{ verification_token: verificationToken, email },
"post"
)
Lbryio.call(
'user_email',
'confirm',
{ verification_token: trimmedVerificationToken, email },
'post'
)
.then(userEmail => {
if (userEmail.is_verified) {
dispatch({
type: types.USER_EMAIL_VERIFY_SUCCESS,
type: ACTIONS.USER_EMAIL_VERIFY_SUCCESS,
data: { email },
});
dispatch(doClaimRewardType(rewards.TYPE_CONFIRM_EMAIL)),
dispatch(doUserFetch());
dispatch(doClaimRewardType(rewards.TYPE_CONFIRM_EMAIL));
dispatch(doUserFetch());
} 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 => {
dispatch({
type: types.USER_EMAIL_VERIFY_FAILURE,
type: ACTIONS.USER_EMAIL_VERIFY_FAILURE,
data: { error },
});
});
@ -140,30 +154,27 @@ export function doUserEmailVerify(verificationToken) {
}
export function doUserIdentityVerify(stripeToken) {
return function(dispatch, getState) {
return function(dispatch) {
dispatch({
type: types.USER_IDENTITY_VERIFY_STARTED,
type: ACTIONS.USER_IDENTITY_VERIFY_STARTED,
token: stripeToken,
});
lbryio
.call("user", "verify_identity", { stripe_token: stripeToken }, "post")
Lbryio.call('user', 'verify_identity', { stripe_token: stripeToken }, 'post')
.then(user => {
if (user.is_identity_verified) {
dispatch({
type: types.USER_IDENTITY_VERIFY_SUCCESS,
type: ACTIONS.USER_IDENTITY_VERIFY_SUCCESS,
data: { user },
});
dispatch(doClaimRewardType(rewards.TYPE_NEW_USER));
} else {
throw new Error(
"Your identity is still not verified. This should not happen."
); // shouldn't happen
throw new Error('Your identity is still not verified. This should not happen.'); // shouldn't happen
}
})
.catch(error => {
dispatch({
type: types.USER_IDENTITY_VERIFY_FAILURE,
type: ACTIONS.USER_IDENTITY_VERIFY_FAILURE,
data: { error: error.toString() },
});
});
@ -171,61 +182,32 @@ export function doUserIdentityVerify(stripeToken) {
}
export function doFetchAccessToken() {
return function(dispatch, getState) {
return function(dispatch) {
const success = token =>
dispatch({
type: types.FETCH_ACCESS_TOKEN_SUCCESS,
type: ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS,
data: { token },
});
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 },
});
});
Lbryio.getAuthToken().then(success);
};
}
export function doUserInviteNew(email) {
return function(dispatch, getState) {
return function(dispatch) {
dispatch({
type: types.USER_INVITE_NEW_STARTED,
type: ACTIONS.USER_INVITE_NEW_STARTED,
});
lbryio
.call("user", "invite", { email }, "post")
.then(invite => {
Lbryio.call('user', 'invite', { email }, 'post')
.then(() => {
dispatch({
type: types.USER_INVITE_NEW_SUCCESS,
type: ACTIONS.USER_INVITE_NEW_SUCCESS,
data: { email },
});
dispatch(
doShowSnackBar({
message: __("Invite sent to %s", email),
message: __('Invite sent to %s', email),
})
);
@ -233,7 +215,7 @@ export function doUserInviteNew(email) {
})
.catch(error => {
dispatch({
type: types.USER_INVITE_NEW_FAILURE,
type: ACTIONS.USER_INVITE_NEW_FAILURE,
data: { error },
});
});

View file

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

View file

@ -1,11 +1,10 @@
// @flow
import * as types from "constants/action_types";
import * as modalTypes from "constants/modal_types";
import * as ACTIONS from 'constants/action_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 reducers = {};
@ -41,12 +40,12 @@ const defaultState: AppState = {
modal: null,
modalProps: {},
platform: process.platform,
upgradeSkipped: sessionStorage.getItem("upgradeSkipped") === "true",
upgradeSkipped: sessionStorage.getItem('upgradeSkipped') === 'true',
daemonVersionMatched: null,
daemonReady: false,
hasSignature: false,
badgeNumber: 0,
volume: Number(sessionStorage.getItem("volume")) || 1,
volume: Number(sessionStorage.getItem('volume')) || 1,
downloadProgress: undefined,
upgradeDownloading: undefined,
@ -57,26 +56,26 @@ const defaultState: AppState = {
snackBar: undefined,
};
reducers[types.DAEMON_READY] = function(state, action) {
reducers[ACTIONS.DAEMON_READY] = function(state) {
return Object.assign({}, state, {
daemonReady: true,
});
};
reducers[types.DAEMON_VERSION_MATCH] = function(state, action) {
reducers[ACTIONS.DAEMON_VERSION_MATCH] = function(state) {
return Object.assign({}, state, {
daemonVersionMatched: true,
});
};
reducers[types.DAEMON_VERSION_MISMATCH] = function(state, action) {
reducers[ACTIONS.DAEMON_VERSION_MISMATCH] = function(state) {
return Object.assign({}, state, {
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, {
downloadProgress: null,
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, {
downloadPath: action.data.path,
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, {
upgradeDownloading: true,
});
};
reducers[types.SKIP_UPGRADE] = function(state, action) {
sessionStorage.setItem("upgradeSkipped", "true");
reducers[ACTIONS.SKIP_UPGRADE] = function(state) {
sessionStorage.setItem('upgradeSkipped', 'true');
return Object.assign({}, state, {
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, {
version: action.data.version,
});
};
reducers[types.CHECK_UPGRADE_SUCCESS] = function(state, action) {
reducers[ACTIONS.CHECK_UPGRADE_SUCCESS] = function(state, action) {
return Object.assign({}, state, {
isUpgradeAvailable: action.data.upgradeAvailable,
remoteVersion: action.data.remoteVersion,
});
};
reducers[types.CHECK_UPGRADE_SUBSCRIBE] = function(state, action) {
reducers[ACTIONS.CHECK_UPGRADE_SUBSCRIBE] = function(state, action) {
return Object.assign({}, state, {
checkUpgradeTimer: action.data.checkUpgradeTimer,
});
};
reducers[types.OPEN_MODAL] = function(state, action) {
reducers[ACTIONS.OPEN_MODAL] = function(state, action) {
return Object.assign({}, state, {
modal: action.data.modal,
modalProps: action.data.modalProps || {},
});
};
reducers[types.CLOSE_MODAL] = function(state, action) {
reducers[ACTIONS.CLOSE_MODAL] = function(state) {
return Object.assign({}, state, {
modal: undefined,
modalProps: {},
});
};
reducers[types.UPGRADE_DOWNLOAD_PROGRESSED] = function(state, action) {
reducers[ACTIONS.UPGRADE_DOWNLOAD_PROGRESSED] = function(state, action) {
return Object.assign({}, state, {
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 snackBar = Object.assign({}, state.snackBar);
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 snacks = Object.assign([], snackBar.snacks);
snacks.shift();
@ -179,8 +178,8 @@ reducers[types.REMOVE_SNACKBAR_SNACK] = function(state, action) {
});
};
reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
const badgeNumber = state.badgeNumber;
reducers[ACTIONS.DOWNLOADING_COMPLETED] = function(state) {
const { badgeNumber } = state;
// Don't update the badge number if the window is focused
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, {
badgeNumber: 0,
});
};
reducers[types.VOLUME_CHANGED] = function(state, action) {
reducers[ACTIONS.VOLUME_CHANGED] = function(state, action) {
return Object.assign({}, state, {
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 defaultState = {};
reducers[types.FETCH_AVAILABILITY_STARTED] = function(state, action) {
reducers[ACTIONS.FETCH_AVAILABILITY_STARTED] = function(state, action) {
const { uri } = action.data;
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 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 defaultState = {};
reducers[types.RESOLVE_URIS_COMPLETED] = function(state, action) {
reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = function(state, action) {
const { resolveInfo } = action.data;
const byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId);
for (const [uri, { certificate, claim }] of Object.entries(resolveInfo)) {
Object.entries(resolveInfo).forEach(([uri, { certificate, claim }]) => {
if (claim) {
byId[claim.claim_id] = claim;
byUri[uri] = claim.claim_id;
@ -25,7 +25,7 @@ reducers[types.RESOLVE_URIS_COMPLETED] = function(state, action) {
} else {
byUri[uri] = null;
}
}
});
return Object.assign({}, state, {
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, {
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 byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId);
const pendingById = Object.assign({}, state.pendingById);
const abandoningById = Object.assign({}, state.abandoningById);
claims
.filter(claim => claim.category && claim.category.match(/claim/))
.forEach(claim => {
byId[claim.claim_id] = claim;
claims.filter(claim => claim.category && claim.category.match(/claim/)).forEach(claim => {
byId[claim.claim_id] = claim;
const pending = Object.values(pendingById).find(
pendingClaim =>
pendingClaim.name == claim.name &&
pendingClaim.channel_name == claim.channel_name
);
const pending = Object.values(pendingById).find(
pendingClaim =>
pendingClaim.name === claim.name && pendingClaim.channel_name === claim.channel_name
);
if (pending) {
delete pendingById[pending.claim_id];
}
});
if (pending) {
delete pendingById[pending.claim_id];
}
});
// Remove old timed out pending publishes
const old = Object.values(pendingById)
Object.values(pendingById)
.filter(pendingClaim => Date.now() - pendingClaim.time >= 20 * 60 * 1000)
.forEach(pendingClaim => {
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 });
};
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 myChannelClaims = new Set(state.myChannelClaims);
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 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 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 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 byId = Object.assign({}, state.byId);
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 byId = Object.assign({}, state.byId);
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 defaultState = {
@ -7,13 +7,13 @@ const defaultState = {
channelClaimCounts: {},
};
reducers[types.FETCH_FEATURED_CONTENT_STARTED] = function(state, action) {
reducers[ACTIONS.FETCH_FEATURED_CONTENT_STARTED] = function(state) {
return Object.assign({}, state, {
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;
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) {
const { claimIds, success } = action.data;
reducers[ACTIONS.FETCH_REWARD_CONTENT_COMPLETED] = function(state, action) {
const { claimIds } = action.data;
return Object.assign({}, state, {
rewardedContentClaimIds: claimIds,
});
};
reducers[types.RESOLVE_URIS_STARTED] = function(state, action) {
reducers[ACTIONS.RESOLVE_URIS_STARTED] = function(state, action) {
const { uris } = action.data;
const oldResolving = state.resolvingUris || [];
const newResolving = Object.assign([], oldResolving);
for (const uri of uris) {
uris.forEach(uri => {
if (!newResolving.includes(uri)) {
newResolving.push(uri);
}
}
});
return Object.assign({}, state, {
resolvingUris: newResolving,
});
};
reducers[types.RESOLVE_URIS_COMPLETED] = function(state, action) {
reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = function(state, action) {
const { resolveInfo } = action.data;
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
for (const [uri, { certificate, claims_in_channel }] of Object.entries(
resolveInfo
)) {
if (certificate && !isNaN(claims_in_channel)) {
channelClaimCounts[uri] = claims_in_channel;
Object.entries(resolveInfo).forEach(([uri, { certificate, claimsInChannel }]) => {
if (certificate && !Number.isNaN(claimsInChannel)) {
channelClaimCounts[uri] = claimsInChannel;
}
}
});
return Object.assign({}, state, {
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, {
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 { 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 defaultState = {};
reducers[types.FETCH_COST_INFO_STARTED] = function(state, action) {
reducers[ACTIONS.FETCH_COST_INFO_STARTED] = function(state, action) {
const { uri } = action.data;
const newFetching = Object.assign({}, state.fetching);
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 newByUri = Object.assign({}, state.byUri);
const newFetching = Object.assign({}, state.fetching);

View file

@ -1,16 +1,15 @@
import * as types from "constants/action_types";
import lbryuri from "lbryuri";
import * as ACTIONS from 'constants/action_types';
const reducers = {};
const defaultState = {};
reducers[types.FILE_LIST_STARTED] = function(state, action) {
reducers[ACTIONS.FILE_LIST_STARTED] = function(state) {
return Object.assign({}, state, {
isFetchingFileList: true,
});
};
reducers[types.FILE_LIST_SUCCEEDED] = function(state, action) {
reducers[ACTIONS.FILE_LIST_SUCCEEDED] = function(state, action) {
const { fileInfos } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint);
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 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 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 newByOutpoint = Object.assign({}, state.byOutpoint);
@ -72,8 +71,8 @@ reducers[types.DOWNLOADING_STARTED] = function(state, action) {
});
};
reducers[types.DOWNLOADING_PROGRESSED] = function(state, action) {
const { uri, outpoint, fileInfo } = action.data;
reducers[ACTIONS.DOWNLOADING_PROGRESSED] = function(state, action) {
const { outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint);
const newDownloading = Object.assign({}, state.downloadingByOutpoint);
@ -87,8 +86,8 @@ reducers[types.DOWNLOADING_PROGRESSED] = function(state, action) {
});
};
reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
const { uri, outpoint, fileInfo } = action.data;
reducers[ACTIONS.DOWNLOADING_COMPLETED] = function(state, action) {
const { outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint);
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 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 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 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;
if (time) {
return Object.assign({}, state, {
publishedDate: time,
});
}
return null;
};
export default function reducer(state = defaultState, action) {

View file

@ -1,38 +1,36 @@
import * as types from "constants/action_types";
import { parseQueryParams } from "util/query_params";
import * as ACTIONS from 'constants/action_types';
const currentPath = () => {
const hash = document.location.hash;
if (hash !== "") return hash.replace(/^#/, "");
return "/discover";
const getCurrentPath = () => {
const { hash } = document.location;
if (hash !== '') return hash.replace(/^#/, '');
return '/discover';
};
const reducers = {};
const defaultState = {
currentPath: currentPath(),
pathAfterAuth: "/discover",
currentPath: getCurrentPath(),
pathAfterAuth: '/discover',
index: 0,
stack: [],
};
reducers[types.DAEMON_READY] = function(state, action) {
reducers[ACTIONS.DAEMON_READY] = function(state) {
const { currentPath } = state;
const params = parseQueryParams(currentPath.split("?")[1] || "");
return Object.assign({}, state, {
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, {
pathAfterAuth: action.data.path,
});
};
reducers[types.HISTORY_NAVIGATE] = (state, action) => {
reducers[ACTIONS.HISTORY_NAVIGATE] = (state, action) => {
const { stack, index } = state;
const { url: path, index: newIndex, scrollY } = action.data;
const { url: path, index: newIndex } = action.data;
const newState = {
currentPath: path,
@ -46,12 +44,11 @@ reducers[types.HISTORY_NAVIGATE] = (state, action) => {
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);
};
reducers[types.WINDOW_SCROLLED] = (state, action) => {
const { stack, index } = state;
reducers[ACTIONS.WINDOW_SCROLLED] = (state, action) => {
const { index } = state;
const { scrollY } = action.data;
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 defaultState = {
@ -9,17 +9,17 @@ const defaultState = {
claimErrorsByType: {},
};
reducers[types.FETCH_REWARDS_STARTED] = function(state, action) {
reducers[ACTIONS.FETCH_REWARDS_STARTED] = function(state) {
return Object.assign({}, state, {
fetching: true,
});
};
reducers[types.FETCH_REWARDS_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = function(state, action) {
const { userRewards } = action.data;
let unclaimedRewards = {},
claimedRewards = {};
const unclaimedRewards = {};
const claimedRewards = {};
userRewards.forEach(reward => {
if (reward.transaction_id) {
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 newClaimErrorsByType = Object.assign({}, state.claimErrorsByType);
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;
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 unclaimedRewardsByType = Object.assign(
{},
state.unclaimedRewardsByType
);
const unclaimedRewardsByType = Object.assign({}, state.unclaimedRewardsByType);
const existingReward = unclaimedRewardsByType[reward.reward_type];
delete state.unclaimedRewardsByType[reward.reward_type];
const newReward = Object.assign({}, reward, {
reward_title: existingReward.reward_title,
@ -84,24 +80,19 @@ reducers[types.CLAIM_REWARD_SUCCESS] = function(state, action) {
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;
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;
return setClaimRewardState(
state,
reward,
state.claimPendingByType[reward.reward_type],
""
);
return setClaimRewardState(state, reward, state.claimPendingByType[reward.reward_type], '');
};
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 defaultState = {
@ -6,15 +6,13 @@ const defaultState = {
searching: false,
};
reducers[types.SEARCH_STARTED] = function(state, action) {
const { query } = action.data;
reducers[ACTIONS.SEARCH_STARTED] = function(state) {
return Object.assign({}, state, {
searching: true,
});
};
reducers[types.SEARCH_COMPLETED] = function(state, action) {
reducers[ACTIONS.SEARCH_COMPLETED] = function(state, action) {
const { query, uris } = action.data;
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, {
searching: false,
});

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
import * as types from "constants/action_types";
import * as ACTIONS from 'constants/action_types';
const reducers = {};
@ -6,9 +6,9 @@ const defaultState = {
authenticationIsPending: false,
userIsPending: false,
emailNewIsPending: false,
emailNewErrorMessage: "",
emailToVerify: "",
inviteNewErrorMessage: "",
emailNewErrorMessage: '',
emailToVerify: '',
inviteNewErrorMessage: '',
inviteNewIsPending: false,
inviteStatusIsPending: false,
invitesRemaining: undefined,
@ -16,7 +16,7 @@ const defaultState = {
user: undefined,
};
reducers[types.AUTHENTICATION_STARTED] = function(state, action) {
reducers[ACTIONS.AUTHENTICATION_STARTED] = function(state) {
return Object.assign({}, state, {
authenticationIsPending: 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, {
authenticationIsPending: 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, {
authenticationIsPending: 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, {
userIsPending: true,
user: defaultState.user,
});
};
reducers[types.USER_FETCH_SUCCESS] = function(state, action) {
reducers[ACTIONS.USER_FETCH_SUCCESS] = function(state, action) {
return Object.assign({}, state, {
userIsPending: false,
user: action.data.user,
});
};
reducers[types.USER_FETCH_FAILURE] = function(state, action) {
reducers[ACTIONS.USER_FETCH_FAILURE] = function(state) {
return Object.assign({}, state, {
userIsPending: true,
user: null,
});
};
reducers[types.USER_EMAIL_NEW_STARTED] = function(state, action) {
reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = function(state) {
return Object.assign({}, state, {
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);
user.primary_email = action.data.email;
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) {
const user = Object.assign({}, state.user);
reducers[ACTIONS.USER_EMAIL_NEW_EXISTS] = function(state, action) {
return Object.assign({}, state, {
emailToVerify: action.data.email,
emailNewIsPending: false,
});
};
reducers[types.USER_EMAIL_NEW_FAILURE] = function(state, action) {
reducers[ACTIONS.USER_EMAIL_NEW_FAILURE] = function(state, action) {
return Object.assign({}, state, {
emailNewIsPending: false,
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, {
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);
user.primary_email = action.data.email;
return Object.assign({}, state, {
emailToVerify: "",
emailToVerify: '',
emailVerifyIsPending: false,
user,
});
};
reducers[types.USER_EMAIL_VERIFY_FAILURE] = function(state, action) {
reducers[ACTIONS.USER_EMAIL_VERIFY_FAILURE] = function(state, action) {
return Object.assign({}, state, {
emailVerifyIsPending: false,
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, {
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, {
identityVerifyIsPending: false,
identityVerifyErrorMessage: "",
identityVerifyErrorMessage: '',
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, {
identityVerifyIsPending: false,
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;
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, {
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, {
inviteStatusIsPending: false,
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, {
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, {
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, {
inviteNewIsPending: false,
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, {
inviteStatusIsPending: false,
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 address = localStorage.getItem("receiveAddress");
const receiveAddress = localStorage.getItem('receiveAddress');
const buildDraftTransaction = () => ({
amount: undefined,
address: undefined,
@ -12,19 +12,19 @@ const defaultState = {
blocks: {},
transactions: {},
fetchingTransactions: false,
receiveAddress: address,
receiveAddress,
gettingNewAddress: false,
draftTransaction: buildDraftTransaction(),
sendingSupport: false,
};
reducers[types.FETCH_TRANSACTIONS_STARTED] = function(state, action) {
reducers[ACTIONS.FETCH_TRANSACTIONS_STARTED] = function(state) {
return Object.assign({}, state, {
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 { 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, {
gettingNewAddress: true,
});
};
reducers[types.GET_NEW_ADDRESS_COMPLETED] = function(state, action) {
reducers[ACTIONS.GET_NEW_ADDRESS_COMPLETED] = function(state, action) {
const { address } = action.data;
localStorage.setItem("receiveAddress", address);
localStorage.setItem('receiveAddress', address);
return Object.assign({}, state, {
gettingNewAddress: false,
receiveAddress: address,
});
};
reducers[types.UPDATE_BALANCE] = function(state, action) {
reducers[ACTIONS.UPDATE_BALANCE] = function(state, action) {
return Object.assign({}, state, {
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, {
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, {
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 newDraft = Object.assign({}, oldDraft, {
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 newDraft = Object.assign({}, oldDraft, {
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, {
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, {
draftTransaction: buildDraftTransaction(),
});
};
reducers[types.SEND_TRANSACTION_FAILED] = function(state, action) {
reducers[ACTIONS.SEND_TRANSACTION_FAILED] = function(state, action) {
const newDraftTransaction = Object.assign({}, state.draftTransaction, {
sending: false,
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, {
sendingSupport: true,
});
};
reducers[types.SUPPORT_TRANSACTION_COMPLETED] = function(state, action) {
reducers[ACTIONS.SUPPORT_TRANSACTION_COMPLETED] = function(state) {
return Object.assign({}, state, {
sendingSupport: false,
});
};
reducers[types.SUPPORT_TRANSACTION_FAILED] = function(state, action) {
reducers[ACTIONS.SUPPORT_TRANSACTION_FAILED] = function(state, action) {
return Object.assign({}, state, {
error: action.data.error,
sendingSupport: false,
});
};
reducers[types.FETCH_BLOCK_SUCCESS] = (state, action) => {
const { block, block: { height } } = action.data,
blocks = Object.assign({}, state.blocks);
reducers[ACTIONS.FETCH_BLOCK_SUCCESS] = (state, action) => {
const { block, block: { height } } = action.data;
const blocks = Object.assign({}, state.blocks);
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(
_selectState,
state => state.platform
);
export const selectPlatform = createSelector(selectState, state => state.platform);
export const selectUpdateUrl = createSelector(selectPlatform, platform => {
switch (platform) {
case "darwin":
return "https://lbry.io/get/lbry.dmg";
case "linux":
return "https://lbry.io/get/lbry.deb";
case "win32":
return "https://lbry.io/get/lbry.exe";
case 'darwin':
return 'https://lbry.io/get/lbry.dmg';
case 'linux':
return 'https://lbry.io/get/lbry.deb';
case 'win32':
return 'https://lbry.io/get/lbry.exe';
default:
throw "Unknown platform";
throw Error('Unknown platform');
}
});
export const selectRemoteVersion = createSelector(
_selectState,
state => state.remoteVersion
);
export const selectRemoteVersion = createSelector(selectState, state => state.remoteVersion);
export const selectIsUpgradeAvailable = createSelector(
_selectState,
selectState,
state => state.isUpgradeAvailable
);
@ -35,81 +29,54 @@ export const selectUpgradeFilename = createSelector(
selectRemoteVersion,
(platform, version) => {
switch (platform) {
case "darwin":
case 'darwin':
return `LBRY_${version}.dmg`;
case "linux":
case 'linux':
return `LBRY_${version}_amd64.deb`;
case "win32":
case 'win32':
return `LBRY_${version}.exe`;
default:
throw "Unknown platform";
throw Error('Unknown platform');
}
}
);
export const selectCurrentModal = createSelector(
_selectState,
state => state.modal
);
export const selectCurrentModal = createSelector(selectState, state => state.modal);
export const selectDownloadProgress = createSelector(
_selectState,
state => state.downloadProgress
);
export const selectDownloadProgress = createSelector(selectState, state => state.downloadProgress);
export const selectDownloadComplete = createSelector(
_selectState,
selectState,
state => state.upgradeDownloadCompleted
);
export const selectIsUpgradeSkipped = createSelector(
_selectState,
state => state.isUpgradeSkipped
);
export const selectIsUpgradeSkipped = createSelector(selectState, state => state.isUpgradeSkipped);
export const selectUpgradeDownloadPath = createSelector(
_selectState,
state => state.downloadPath
);
export const selectUpgradeDownloadPath = createSelector(selectState, state => state.downloadPath);
export const selectUpgradeDownloadItem = createSelector(
_selectState,
state => state.downloadItem
);
export const selectUpgradeDownloadItem = createSelector(selectState, state => state.downloadItem);
export const selectModalProps = createSelector(
_selectState,
state => state.modalProps
);
export const selectModalProps = createSelector(selectState, state => state.modalProps);
export const selectDaemonReady = createSelector(
_selectState,
state => state.daemonReady
);
export const selectDaemonReady = createSelector(selectState, state => state.daemonReady);
export const selectDaemonVersionMatched = createSelector(
_selectState,
selectState,
state => state.daemonVersionMatched
);
export const selectSnackBar = createSelector(
_selectState,
state => state.snackBar || {}
);
export const selectSnackBar = createSelector(selectState, state => state.snackBar || {});
export const selectSnackBarSnacks = createSelector(
selectSnackBar,
snackBar => snackBar.snacks || []
);
export const selectBadgeNumber = createSelector(
_selectState,
state => state.badgeNumber
);
export const selectBadgeNumber = createSelector(selectState, state => state.badgeNumber);
export const selectCurrentLanguage = createSelector(
_selectState,
() => app.i18n.getLocale() || "en"
selectState,
() => 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(
_selectState,
state => state.byUri || {}
);
const selectFetchingAvailability = createSelector(selectState, state => state.fetching || {});
export const makeSelectIsAvailableForUri = uri =>
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]);
export { selectFetchingAvailability as default };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,15 +1,11 @@
import * as settings from "constants/settings";
import { createSelector } from "reselect";
import { selectAllClaimsByChannel, selectClaimsById } from "./claims";
import { createSelector } from 'reselect';
import { selectAllClaimsByChannel, selectClaimsById } from './claims';
// get the entire subscriptions state
const _selectState = state => state.subscriptions || {};
const selectState = state => state.subscriptions || {};
// list of saved channel names and uris
export const selectSubscriptions = createSelector(
_selectState,
state => state.subscriptions
);
export const selectSubscriptions = createSelector(selectState, state => state.subscriptions);
export const selectSubscriptionsFromClaims = createSelector(
selectAllClaimsByChannel,
@ -29,7 +25,7 @@ export const selectSubscriptionsFromClaims = createSelector(
// if subscribed channel has content
if (channelIds[subscription.uri]) {
// 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
// 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
channelClaims = channelClaims.map(
claim => `${claim.name}#${claim.claim_id}`
);
channelClaims = channelClaims.map(claim => `${claim.name}#${claim.claim_id}`);
fetchedSubscriptions.push({
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(
_selectState,
selectState,
state => state.authenticationIsPending
);
export const selectUserIsPending = createSelector(
_selectState,
state => state.userIsPending
);
export const selectUserIsPending = createSelector(selectState, state => state.userIsPending);
export const selectUser = createSelector(_selectState, state => state.user);
export const selectUser = createSelector(selectState, state => state.user);
export const selectUserEmail = createSelector(
selectUser,
@ -20,7 +17,7 @@ export const selectUserEmail = createSelector(
);
export const selectEmailToVerify = createSelector(
_selectState,
selectState,
selectUserEmail,
(state, userEmail) => state.emailToVerify || userEmail
);
@ -31,32 +28,32 @@ export const selectUserIsRewardApproved = createSelector(
);
export const selectEmailNewIsPending = createSelector(
_selectState,
selectState,
state => state.emailNewIsPending
);
export const selectEmailNewErrorMessage = createSelector(
_selectState,
selectState,
state => state.emailNewErrorMessage
);
export const selectEmailVerifyIsPending = createSelector(
_selectState,
selectState,
state => state.emailVerifyIsPending
);
export const selectEmailVerifyErrorMessage = createSelector(
_selectState,
selectState,
state => state.emailVerifyErrorMessage
);
export const selectIdentityVerifyIsPending = createSelector(
_selectState,
selectState,
state => state.identityVerifyIsPending
);
export const selectIdentityVerifyErrorMessage = createSelector(
_selectState,
selectState,
state => state.identityVerifyErrorMessage
);
@ -65,37 +62,31 @@ export const selectUserIsVerificationCandidate = createSelector(
user => user && (!user.has_verified_email || !user.is_identity_verified)
);
export const selectAccessToken = createSelector(
_selectState,
state => state.accessToken
);
export const selectAccessToken = createSelector(selectState, state => state.accessToken);
export const selectUserInviteStatusIsPending = createSelector(
_selectState,
selectState,
state => state.inviteStatusIsPending
);
export const selectUserInvitesRemaining = createSelector(
_selectState,
selectState,
state => state.invitesRemaining
);
export const selectUserInvitees = createSelector(
_selectState,
state => state.invitees
);
export const selectUserInvitees = createSelector(selectState, state => state.invitees);
export const selectUserInviteStatusFailed = createSelector(
selectUserInvitesRemaining,
inviteStatus => selectUserInvitesRemaining === null
() => selectUserInvitesRemaining === null
);
export const selectUserInviteNewIsPending = createSelector(
_selectState,
selectState,
state => state.inviteNewIsPending
);
export const selectUserInviteNewErrorMessage = createSelector(
_selectState,
selectState,
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(
_selectState,
state => state.balance
);
export const selectBalance = createSelector(selectState, state => state.balance);
export const selectTransactionsById = createSelector(
_selectState,
state => state.transactions
);
export const selectTransactionsById = createSelector(selectState, state => state.transactions);
export const selectTransactionItems = createSelector(
selectTransactionsById,
byId => {
const items = [];
export const selectTransactionItems = createSelector(selectTransactionsById, byId => {
const items = [];
Object.keys(byId).forEach(txid => {
const tx = byId[txid];
Object.keys(byId).forEach(txid => {
const tx = byId[txid];
// ignore dust/fees
// it is fee only txn if all infos are also empty
if (
Math.abs(tx.value) === Math.abs(tx.fee) &&
tx.claim_info.length == 0 &&
tx.support_info.length == 0 &&
tx.update_info.length == 0
) {
return;
}
// ignore dust/fees
// it is fee only txn if all infos are also empty
if (
Math.abs(tx.value) === Math.abs(tx.fee) &&
tx.claim_info.length === 0 &&
tx.support_info.length === 0 &&
tx.update_info.length === 0
) {
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(
...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(
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,
};
Object.assign({}, tx, {
type: tx.value < 0 ? 'spend' : 'receive',
})
);
});
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);
}
);
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(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(
selectTransactionItems,
@ -101,27 +83,21 @@ export const selectHasTransactions = createSelector(
);
export const selectIsFetchingTransactions = createSelector(
_selectState,
selectState,
state => state.fetchingTransactions
);
export const selectIsSendingSupport = createSelector(
_selectState,
state => state.sendingSupport
);
export const selectIsSendingSupport = createSelector(selectState, state => state.sendingSupport);
export const selectReceiveAddress = createSelector(
_selectState,
state => state.receiveAddress
);
export const selectReceiveAddress = createSelector(selectState, state => state.receiveAddress);
export const selectGettingNewAddress = createSelector(
_selectState,
selectState,
state => state.gettingNewAddress
);
export const selectDraftTransaction = createSelector(
_selectState,
selectState,
state => state.draftTransaction || {}
);
@ -140,11 +116,10 @@ export const selectDraftTransactionError = createSelector(
draft => draft.error
);
export const selectBlocks = createSelector(_selectState, state => state.blocks);
export const selectBlocks = createSelector(selectState, state => state.blocks);
export const makeSelectBlockDate = block =>
createSelector(
selectBlocks,
blocks =>
blocks && blocks[block] ? new Date(blocks[block].time * 1000) : undefined
blocks => (blocks && blocks[block] ? new Date(blocks[block].time * 1000) : undefined)
);

View file

@ -1,53 +1,32 @@
import lbry from "lbry";
import lbryio from "lbryio";
import { doShowSnackBar } from "redux/actions/app";
import Lbry from 'lbry';
import Lbryio from 'lbryio';
import { doShowSnackBar } from 'redux/actions/app';
function rewardMessage(type, amount) {
return {
new_developer: __(
"You earned %s for registering as a new developer.",
amount
),
new_user: __("You earned %s LBC new user reward.", 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
),
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),
new_developer: __('You earned %s for registering as a new developer.', amount),
new_user: __('You earned %s LBC new user reward.', 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),
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];
}
const rewards = {};
rewards.TYPE_NEW_DEVELOPER = "new_developer";
rewards.TYPE_NEW_USER = "new_user";
rewards.TYPE_CONFIRM_EMAIL = "verified_email";
rewards.TYPE_FIRST_CHANNEL = "new_channel";
rewards.TYPE_FIRST_STREAM = "first_stream";
rewards.TYPE_MANY_DOWNLOADS = "many_downloads";
rewards.TYPE_FIRST_PUBLISH = "first_publish";
rewards.TYPE_FEATURED_DOWNLOAD = "featured_download";
rewards.TYPE_REFERRAL = "referral";
rewards.TYPE_NEW_DEVELOPER = 'new_developer';
rewards.TYPE_NEW_USER = 'new_user';
rewards.TYPE_CONFIRM_EMAIL = 'verified_email';
rewards.TYPE_FIRST_CHANNEL = 'new_channel';
rewards.TYPE_FIRST_STREAM = 'first_stream';
rewards.TYPE_MANY_DOWNLOADS = 'many_downloads';
rewards.TYPE_FIRST_PUBLISH = 'first_publish';
rewards.TYPE_FEATURED_DOWNLOAD = 'featured_download';
rewards.TYPE_REFERRAL = 'referral';
rewards.SORT_ORDER = [
rewards.TYPE_NEW_USER,
rewards.TYPE_CONFIRM_EMAIL,
@ -62,18 +41,18 @@ rewards.SORT_ORDER = [
rewards.claimReward = function(type) {
function requestReward(resolve, reject, params) {
if (!lbryio.enabled) {
reject(new Error(__("Rewards are not enabled.")));
if (!Lbryio.enabled) {
reject(new Error(__('Rewards are not enabled.')));
return;
}
lbryio.call("reward", "new", params, "post").then(reward => {
Lbryio.call('reward', 'new', params, 'post').then(reward => {
const message = rewardMessage(type, reward.reward_amount);
// Display global notice
const action = doShowSnackBar({
message,
linkText: __("Show All"),
linkTarget: "/rewards",
linkText: __('Show All'),
linkTarget: '/rewards',
isError: false,
});
window.app.store.dispatch(action);
@ -85,7 +64,7 @@ rewards.claimReward = function(type) {
}
return new Promise((resolve, reject) => {
lbry.wallet_unused_address().then(address => {
Lbry.wallet_unused_address().then(address => {
const params = {
reward_type: type,
wallet_address: address,
@ -93,42 +72,38 @@ rewards.claimReward = function(type) {
switch (type) {
case rewards.TYPE_FIRST_CHANNEL:
lbry
.claim_list_mine()
Lbry.claim_list_mine()
.then(claims => {
const claim = claims
.reverse()
.find(
claim =>
claim.name.length &&
claim.name[0] == "@" &&
claim.txid.length &&
claim.category == "claim"
foundClaim =>
foundClaim.name.length &&
foundClaim.name[0] === '@' &&
foundClaim.txid.length &&
foundClaim.category === 'claim'
);
if (claim) {
params.transaction_id = claim.txid;
requestReward(resolve, reject, params);
} else {
reject(
new Error(__("Please create a channel identity first."))
);
reject(new Error(__('Please create a channel identity first.')));
}
})
.catch(reject);
break;
case rewards.TYPE_FIRST_PUBLISH:
lbry
.claim_list_mine()
Lbry.claim_list_mine()
.then(claims => {
const claim = claims
.reverse()
.find(
claim =>
claim.name.length &&
claim.name[0] != "@" &&
claim.txid.length &&
claim.category == "claim"
foundClaim =>
foundClaim.name.length &&
foundClaim.name[0] !== '@' &&
foundClaim.txid.length &&
foundClaim.category === 'claim'
);
if (claim) {
params.transaction_id = claim.txid;
@ -138,12 +113,10 @@ rewards.claimReward = function(type) {
claims.length
? 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(
__("Please publish something to claim this reward.")
)
: new Error(__('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);
html
{
html {
height: 100%;
font-size: var(--font-size);
}
body
{
body {
color: var(--text-color);
font-family: 'Roboto', sans-serif;
line-height: var(--font-line-height);
@ -19,30 +17,25 @@ body
color: var(--text-selection-color);
}
#window
{
#window {
min-height: 100vh;
background: var(--window-bg);
}
.credit-amount--indicator
{
.credit-amount--indicator {
font-weight: 500;
color: var(--color-money);
}
.credit-amount--fee
{
.credit-amount--fee {
font-size: 0.9em;
color: var(--color-meta-light);
}
.credit-amount--bold
{
.credit-amount--bold {
font-weight: 700;
}
#main-content
{
#main-content {
margin: auto;
display: flex;
flex-direction: column;
@ -58,8 +51,7 @@ body
margin-right: auto;
max-width: 100%;
}
main.main--single-column
{
main.main--single-column {
width: $width-page-constrained;
}
@ -76,7 +68,7 @@ body
background: url('../../../static/img/busy.gif') no-repeat center center;
width: $width;
height: $spacing-vertical;
content: "";
content: '';
left: 50%;
margin-left: -1 / 2 * $width;
display: inline-block;
@ -93,30 +85,37 @@ h2 {
font-size: 1.75em;
}
h3 { font-size: 1.4em; }
h3 {
font-size: 1.4em;
}
h4 {
font-size: 1.2em;
}
h5 { font-size: 1.1em; }
sup, sub {
vertical-align: baseline;
position: relative;
h5 {
font-size: 1.1em;
}
sup,
sub {
vertical-align: baseline;
position: relative;
}
sup {
top: -0.4em;
}
sub {
top: 0.4em;
}
sup { top: -0.4em; }
sub { top: 0.4em; }
code {
font: 0.8em Consolas, 'Lucida Console', 'Source Sans', monospace;
background-color: var(--color-bg-alt);
}
p
{
p {
margin-bottom: 0.8em;
&:last-child
{
&:last-child {
margin-bottom: 0;
}
}
@ -137,8 +136,7 @@ p
-webkit-box-orient: vertical;
}
.busy-indicator
{
.busy-indicator {
background: url('../../../static/img/busy.gif') no-repeat center center;
display: inline-block;
margin: -1em 0;
@ -146,30 +144,26 @@ p
min-height: 8px;
vertical-align: middle;
padding: 0 30px;
&:last-child
{
&:last-child {
padding-right: 2px;
}
&:first-child
{
&:first-child {
padding-left: 2px;
}
}
.help {
font-size: .85em;
font-size: 0.85em;
color: var(--color-help);
}
.meta
{
.meta {
font-size: 0.9em;
color: var(--color-meta-light);
}
.empty
{
color:var(--color-meta-light);
.empty {
color: var(--color-meta-light);
font-style: italic;
}
@ -185,7 +179,6 @@ p
display: block;
margin-bottom: $spacing-vertical * 2/3;
text-align: right;
line-height: 1;
font-size: 0.85em;
@ -196,7 +189,6 @@ section.section-spaced {
margin-bottom: $spacing-vertical;
}
.text-center
{
.text-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
{
margin:0;
padding:0;
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 {
margin: 0;
padding: 0;
}
:focus
{
:focus {
outline: 0;
}
input::-webkit-search-cancel-button {
/* Remove default */
-webkit-appearance: none;
}
table
{
table {
border-collapse: collapse;
border-spacing:0;
border-spacing: 0;
}
fieldset, img, iframe
{
border: 0;
fieldset,
img,
iframe {
border: 0;
}
h1, h2, h3, h4, h5, h6
{
font-weight:normal;
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: normal;
}
ol, ul
{
ol,
ul {
list-style-position: inside;
> li {
list-style-position: inside;
> li { list-style-position: inside; }
}
}
input, textarea, select
{
font-family:inherit;
font-size:inherit;
font-weight:inherit;
input,
textarea,
select {
font-family: inherit;
font-size: inherit;
font-weight: inherit;
border: 0 none;
}
img {
@ -42,8 +73,7 @@ img {
vertical-align: middle;
-ms-interpolation-mode: bicubic;
}
a
{
color: inherit;
text-decoration: none;
}
a {
color: inherit;
text-decoration: none;
}

View file

@ -6,28 +6,27 @@ $width-page-constrained: 800px;
$text-color: #000;
:root {
/* Colors */
--color-brand: #155B4A;
--color-primary: #155B4A;
--color-primary-light: saturate(lighten(#155B4A, 50%), 20%);
--color-light-alt: hsl(hue(#155B4A), 15, 85);
--color-dark-overlay: rgba(32,32,32,0.9);
--color-brand: #155b4a;
--color-primary: #155b4a;
--color-primary-light: saturate(lighten(#155b4a, 50%), 20%);
--color-light-alt: hsl(hue(#155b4a), 15, 85);
--color-dark-overlay: rgba(32, 32, 32, 0.9);
--color-help: rgba(0, 0, 0, 0.54);
--color-notice: #8a6d3b;
--color-error: #a94442;
--color-load-screen-text: #c3c3c3;
--color-meta-light: #505050;
--color-money: #216C2A;
--color-money: #216c2a;
--color-download: rgba(0, 0, 0, 0.75);
--color-canvas: #f5f5f5;
--color-bg: #ffffff;
--color-bg-alt: #D9D9D9;
--color-bg-alt: #d9d9d9;
/* Misc */
--content-max-width: 1000px;
--nsfw-blur-intensity: 20px;
--height-video-embedded: $width-page-constrained * 9 / 16 ;
--height-video-embedded: $width-page-constrained * 9 / 16;
/* Font */
--font-size: 16px;
@ -35,20 +34,21 @@ $text-color: #000;
--font-size-subtext-multiple: 0.82;
/* Shadows */
--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-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, 0.14), 2px 5px 3px -2px rgba(0, 0, 0, 0.2),
2px 3px 7px 0 rgba(0, 0, 0, 0.12);
/* Transition */
--transition-duration: .225s;
--transition-duration: 0.225s;
--transition-type: ease;
/* Text */
--text-color: $text-color;
--text-help-color: #EEE;
--text-help-color: #eee;
--text-max-width: 660px;
--text-link-padding: 4px;
--text-selection-bg: rgba(saturate(lighten(#155B4A, 20%), 20%), 1); // temp color
--text-selection-color: #FFF;
--text-selection-bg: rgba(saturate(lighten(#155b4a, 20%), 20%), 1); // temp color
--text-selection-color: #fff;
/* Window */
--window-bg: var(--color-canvas);
@ -72,7 +72,7 @@ $text-color: #000;
/* input:hover */
--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-opacity: 1;
@ -82,9 +82,9 @@ $text-color: #000;
/* Button */
--button-bg: var(--color-bg-alt);
--button-color: #FFF;
--button-color: #fff;
--button-primary-bg: var(--color-primary);
--button-primary-color: #FFF;
--button-primary-color: #fff;
--button-padding: $spacing-vertical * 2/3;
--button-height: $spacing-vertical * 1.5;
--button-intra-margin: $spacing-vertical;
@ -93,14 +93,14 @@ $text-color: #000;
/* Header */
--header-bg: var(--color-bg);
--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-button-bg: transparent; //var(--button-bg);
--header-button-hover-bg: rgba(100, 100, 100, 0.15);
/* Header -> search */
--search-bg: rgba(255, 255, 255, 0.7);
--search-border:1px solid #ccc;
--search-border: 1px solid #ccc;
--search-color: #666;
--search-active-color: var(--header-active-color);
--search-active-shadow: 0 0 3px 0px var(--text-selection-bg);
@ -130,7 +130,7 @@ $text-color: #000;
/* Modal */
--modal-width: 440px;
--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);
/* Menu */
@ -146,7 +146,7 @@ $text-color: #000;
/* Scrollbar */
--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-active-bg: var(--color-primary);
--scrollbar-track-bg: transparent;
@ -156,6 +156,5 @@ $text-color: #000;
/* Animation :) */
--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";
@import "_reset";
@import "_vars";
@import "_icons";
@import "_gui";
@import "component/_table";
@import "component/_button.scss";
@import "component/_card.scss";
@import "component/_file-download.scss";
@import "component/_file-selector.scss";
@import "component/_file-tile.scss";
@import "component/_form-field.scss";
@import "component/_header.scss";
@import "component/_menu.scss";
@import "component/_tooltip.scss";
@import "component/_load-screen.scss";
@import "component/_channel-indicator.scss";
@import "component/_notice.scss";
@import "component/_modal.scss";
@import "component/_snack-bar.scss";
@import "component/_video.scss";
@import "component/_pagination.scss";
@import "component/_markdown-editor.scss";
@import "component/_scrollbar.scss";
@import "component/_tabs.scss";
@import "component/_divider.scss";
@import "component/_checkbox.scss";
@import "component/_radio.scss";
@import "component/_shapeshift.scss";
@import "component/_spinner.scss";
@import "page/_show.scss";
@import '_reset';
@import '_vars';
@import '_icons';
@import '_gui';
@import 'component/_table';
@import 'component/_button.scss';
@import 'component/_card.scss';
@import 'component/_file-download.scss';
@import 'component/_file-selector.scss';
@import 'component/_file-tile.scss';
@import 'component/_form-field.scss';
@import 'component/_header.scss';
@import 'component/_menu.scss';
@import 'component/_tooltip.scss';
@import 'component/_load-screen.scss';
@import 'component/_channel-indicator.scss';
@import 'component/_notice.scss';
@import 'component/_modal.scss';
@import 'component/_snack-bar.scss';
@import 'component/_video.scss';
@import 'component/_pagination.scss';
@import 'component/_markdown-editor.scss';
@import 'component/_scrollbar.scss';
@import 'component/_tabs.scss';
@import 'component/_divider.scss';
@import 'component/_checkbox.scss';
@import 'component/_radio.scss';
@import 'component/_shapeshift.scss';
@import 'component/_spinner.scss';
@import 'page/_show.scss';

View file

@ -1,4 +1,4 @@
@import "../mixin/link.scss";
@import '../mixin/link.scss';
$button-focus-shift: 12%;
@ -6,14 +6,13 @@ $button-focus-shift: 12%;
position: relative;
display: inline-block;
+ .button-set-item
{
+ .button-set-item {
margin-left: var(--button-intra-margin);
}
}
.button-block, .faux-button-block
{
.button-block,
.faux-button-block {
display: inline-block;
height: var(--button-height);
line-height: var(--button-height);
@ -22,26 +21,21 @@ $button-focus-shift: 12%;
text-align: center;
border-radius: var(--button-radius);
text-transform: uppercase;
.icon
{
.icon {
top: 0em;
}
.icon:first-child
{
.icon:first-child {
padding-right: 5px;
}
.icon:last-child
{
.icon:last-child {
padding-left: 5px;
}
.icon:only-child
{
.icon:only-child {
padding-left: 0;
padding-right: 0;
}
}
.button-block
{
.button-block {
cursor: pointer;
font-weight: 500;
font-size: 14px;
@ -57,9 +51,7 @@ $button-focus-shift: 12%;
}
}
.button-primary
{
.button-primary {
color: var(--button-primary-color);
background-color: var(--button-primary-bg);
box-shadow: var(--box-shadow-layer);
@ -70,14 +62,12 @@ $button-focus-shift: 12%;
//box-shadow: $box-shadow-focus;
}
}
.button-alt
{
.button-alt {
background-color: var(--button-bg);
box-shadow: var(--box-shadow-layer);
}
.button-text
{
.button-text {
@include text-link();
display: inline-block;
@ -85,17 +75,15 @@ $button-focus-shift: 12%;
margin: 0 var(--text-link-padding);
}
}
.button-text-help
{
.button-text-help {
@include text-link(var(--text-help-color));
font-size: 0.8em;
}
.button--flat
{
.button--flat {
box-shadow: none !important;
}
.button--submit {
font-family: inherit;
line-height: 0;
}
font-family: inherit;
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
overflow-x: hidden;
}
.card--obscured
{
.card--obscured {
position: relative;
}
.card--obscured .card__inner {
filter: blur( var(--nsfw-blur-intensity) );
filter: blur(var(--nsfw-blur-intensity));
}
.card__title-primary,
.card__title-identity,
@ -52,7 +51,6 @@
margin-top: var(--card-margin);
margin-bottom: var(--card-margin);
user-select: none;
}
.card__actions--bottom {
margin-top: $spacing-vertical * 1/3;
@ -88,7 +86,7 @@
$font-size-subtext-multiple: 0.82;
.card__subtext {
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-bottom: $spacing-vertical * 1/3;
}
@ -96,7 +94,9 @@ $font-size-subtext-multiple: 0.82;
white-space: pre-wrap;
}
.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 {
position: absolute;
@ -117,7 +117,7 @@ $font-size-subtext-multiple: 0.82;
cursor: pointer;
}
.card--link {
transition: transform 0.2s var(--animation-style);
transition: transform 0.2s var(--animation-style);
}
.card--link:hover {
position: relative;
@ -126,7 +126,7 @@ $font-size-subtext-multiple: 0.82;
transform: scale(var(--card-link-scaling)) translateX(var(--card-hover-translate));
transform-origin: 50% 50%;
overflow-x: visible;
overflow-y: visible
overflow-y: visible;
}
.card--link:hover ~ .card--link {
transform: translateX(calc(var(--card-hover-translate) * 2));
@ -139,49 +139,49 @@ $font-size-subtext-multiple: 0.82;
}
.card__media--autothumb {
position: relative
position: relative;
}
.card__media--autothumb.purple {
background-color: #9c27b0
background-color: #9c27b0;
}
.card__media--autothumb.red {
background-color: #e53935
background-color: #e53935;
}
.card__media--autothumb.pink {
background-color: #e91e63
background-color: #e91e63;
}
.card__media--autothumb.indigo {
background-color: #3f51b5
background-color: #3f51b5;
}
.card__media--autothumb.blue {
background-color: #2196f3
background-color: #2196f3;
}
.card__media--autothumb.light-blue {
background-color: #039be5
background-color: #039be5;
}
.card__media--autothumb.cyan {
background-color: #00acc1
background-color: #00acc1;
}
.card__media--autothumb.teal {
background-color: #009688
background-color: #009688;
}
.card__media--autothumb.green {
background-color: #43a047
background-color: #43a047;
}
.card__media--autothumb.yellow {
background-color: #ffeb3b
background-color: #ffeb3b;
}
.card__media--autothumb.orange {
background-color: #ffa726
background-color: #ffa726;
}
.card__media--autothumb .card__autothumb__text {
font-size: 2.0em;
font-size: 2em;
width: 100%;
color: #ffffff;
text-align: center;
position: absolute;
top: 36%
top: 36%;
}
.card__indicators {
@ -194,17 +194,17 @@ $font-size-subtext-multiple: 0.82;
white-space: normal;
}
.card--small .card__media {
height: calc( var(--card-small-width) * 9 / 16);
height: calc(var(--card-small-width) * 9 / 16);
}
.card--form {
width: calc( var(--input-width) + var(--card-padding) * 2);
width: calc(var(--input-width) + var(--card-padding) * 2);
}
.card__subtitle {
color: var(--color-help);
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 {
@ -219,8 +219,7 @@ $font-size-subtext-multiple: 0.82;
padding-left: 20px;
}
.card-series-submit
{
.card-series-submit {
margin-left: auto;
margin-right: auto;
max-width: var(--card-max-width);
@ -279,7 +278,7 @@ $padding-right-card-hover-hack: 30px;
position: absolute;
padding: 0 var(--card-margin);
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 {
background: var(--card-bg);
@ -295,8 +294,8 @@ $padding-right-card-hover-hack: 30px;
transition: transform 0.2s var(--animation-style);
&:hover {
opacity: 1.0;
transform: scale(calc( var(--card-link-scaling) * 1.1));
opacity: 1;
transform: scale(calc(var(--card-link-scaling) * 1.1));
}
}
.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 {
$margin-card-grid: $spacing-vertical * 2/3;
display:flex;
display: flex;
flex-wrap: wrap;
> .card {
width: $width-page-constrained / 2 - $margin-card-grid / 2;
flex-grow:1;
flex-grow: 1;
}
> .card:nth-of-type(2n - 1):not(:last-child) {
margin-right: $margin-card-grid;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,4 @@
#header
{
#header {
color: var(--header-color);
background: var(--header-bg);
display: flex;
@ -19,11 +17,11 @@
padding-left: $spacing-vertical / 4;
padding-right: $spacing-vertical / 4;
.button-alt {
background: var(--header-button-bg) !important;
font-size: 1em;
background: var(--header-button-bg) !important;
font-size: 1em;
}
.button-alt:hover {
background: var(--header-button-hover-bg) !important;
background: var(--header-button-hover-bg) !important;
}
}
@ -31,17 +29,18 @@
flex-grow: 1;
}
.wunderbar
{
.wunderbar {
position: relative;
.icon {
position: absolute;
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
// will come back to this with the redesign - sean

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
.modal-overlay, .error-modal-overlay {
.modal-overlay,
.error-modal-overlay {
position: fixed;
display: flex;
justify-content: center;
@ -72,11 +72,12 @@
max-width: none;
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);
padding: 8px;
list-style: none;
max-height: 400px;
max-width: var(--modal-width);
max-width: var(--modal-width);
overflow-y: hidden;
}

View file

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

View file

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

View file

@ -5,50 +5,50 @@ $md-radio-checked-size: 10px;
$md-radio-ripple-size: 15px;
.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;
&: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"] {
visibility: hidden;
margin-right: 16px;
&:checked + label:before {
border-color: $md-radio-checked-color;
}
&:checked + label:after {
transform: scale(1);
}
&: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'] {
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 {
width: 8px;
overflow: auto;
width: 8px;
overflow: auto;
}
::-webkit-scrollbar-track {
background: var(--scrollbar-track-bg);
border-radius: var(--scrollbar-radius);
margin: 4px;
background: var(--scrollbar-track-bg);
border-radius: var(--scrollbar-radius);
margin: 4px;
}
::-webkit-scrollbar-thumb {
border-radius: var(--scrollbar-radius);
background-color: var(--scrollbar-thumb-bg);
transition: background-color 0.3s ease;
border-radius: var(--scrollbar-radius);
background-color: var(--scrollbar-thumb-bg);
transition: background-color 0.3s ease;
}
::-webkit-scrollbar-thumb:hover {
background-color: var(--scrollbar-thumb-hover-bg);
background-color: var(--scrollbar-thumb-hover-bg);
}
::-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;
}
.shapeshift__link {
padding-left: 10px;
}

View file

@ -1,4 +1,3 @@
$padding-snack-horizontal: $spacing-vertical;
.snack-bar {
@ -24,7 +23,7 @@ $padding-snack-horizontal: $spacing-vertical;
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 */
}

View file

@ -55,4 +55,4 @@
&:after {
background: var(--color-bg);
}
}
}

View file

@ -1,9 +1,9 @@
table.table-standard {
word-wrap: break-word;
max-width: 100%;
th, td {
th,
td {
padding: $spacing-vertical/2 8px;
}
th {
@ -13,7 +13,8 @@ table.table-standard {
td {
vertical-align: top;
}
thead th, > tr:first-child th {
thead th,
> tr:first-child th {
vertical-align: bottom;
font-weight: 500;
font-size: 0.9em;
@ -32,7 +33,7 @@ table.table-standard {
}
tfoot td {
padding: $spacing-vertical / 2 8px;
font-size: .85em;
font-size: 0.85em;
}
tbody {
tr {
@ -62,9 +63,19 @@ table.table-stretch {
}
table.table-transactions {
td:nth-of-type(1) { width: 15%; }
td:nth-of-type(2) { width: 15%; }
td:nth-of-type(3) { width: 15%; }
td:nth-of-type(4) { width: 40%; }
td:nth-of-type(5) { width: 15%; }
td:nth-of-type(1) {
width: 15%;
}
td:nth-of-type(2) {
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 */
nav.sub-header
{
nav.sub-header {
text-transform: uppercase;
max-width: $width-page-constrained;
margin-bottom: 40px;
border-bottom: var(--divider);
user-select: none;
> a
{
> a {
height: 38px;
line-height: 38px;
text-align: center;
@ -21,16 +19,13 @@ nav.sub-header
color: var(--tab-color);
position: relative;
&:first-child
{
&:first-child {
margin-left: 0;
}
&:last-child
{
&:last-child {
margin-right: 0;
}
&.sub-header-selected
{
&.sub-header-selected {
color: var(--tab-active-color);
&:before {
width: 100%;
@ -45,8 +40,7 @@ nav.sub-header
animation-timing-function: var(--animation-style);
}
}
&:hover
{
&:hover {
color: var(--tab-active-color);
}
}
@ -60,8 +54,11 @@ nav.sub-header
}
}
@keyframes activeTab {
from {width: 0;}
to {width: 100%;}
from {
width: 0;
}
to {
width: 100%;
}
}

View file

@ -1,4 +1,4 @@
@import "../mixin/link.scss";
@import '../mixin/link.scss';
.tooltip {
position: relative;
@ -27,7 +27,7 @@
.tooltip--header .tooltip__link {
@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);
vertical-align: middle;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,10 +1,7 @@
// util for creating reducers
// based off of redux-actions
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions
export const handleActions = (actionMap, defaultState) => (
state = defaultState,
action
) => {
const handleActions = (actionMap, defaultState) => (state = defaultState, action) => {
const handler = actionMap[action.type];
if (handler) {
@ -16,3 +13,5 @@ export const handleActions = (actionMap, defaultState) => (
// returning a copy here breaks redux-persist
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 dock = application.dock;
const { dock } = application;
const win = remote.BrowserWindow.getFocusedWindow();
const setBadge = text => {
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 setProgressBar = progress => {

View file

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

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