diff --git a/CHANGELOG.md b/CHANGELOG.md
index b07652053..1619af53a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,15 +9,16 @@ Web UI version numbers should always match the corresponding version of LBRY App
## [Unreleased]
### Added
* Now you can revoke your claims from the txns list itself.(#581)
+ * Added new window menu options for reloading and help.
+ * Rewards are now marked in transaction history (#660)
*
### Changed
- *
+ * Replaced all instances of `XMLHttpRequest` with native `Fetch` API.
*
### Fixed
- *
- *
+ * Fixed console errors on settings page related to improper React input properties.
### Deprecated
*
diff --git a/app/main.js b/app/main.js
index e51928139..c11468b8f 100644
--- a/app/main.js
+++ b/app/main.js
@@ -1,6 +1,7 @@
const {app, BrowserWindow, ipcMain} = require('electron');
const url = require('url');
-const isDebug = process.env.NODE_ENV === 'development'
+const isDebug = process.env.NODE_ENV === 'development';
+const setMenu = require('./menu/main-menu.js');
if (isDebug) {
try
@@ -169,6 +170,12 @@ function createWindow () {
win.on('closed', () => {
win = null
})
+
+ // Menu bar
+ win.setAutoHideMenuBar(true);
+ win.setMenuBarVisibility(isDebug);
+ setMenu();
+
};
function handleOpenUriRequested(uri) {
diff --git a/app/menu/main-menu.js b/app/menu/main-menu.js
index 082b116ac..7b214bfca 100644
--- a/app/menu/main-menu.js
+++ b/app/menu/main-menu.js
@@ -1,6 +1,4 @@
-const {Menu} = require('electron');
-const electron = require('electron');
-const app = electron.app;
+const { app, shell, Menu } = require('electron');
const baseTemplate = [
{
@@ -30,16 +28,65 @@ const baseTemplate = [
]
},
{
- label: 'Help',
+ label: 'View',
submenu: [
{
- label: 'Help',
+ 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')
+ }
+ },
]
}
];
@@ -71,40 +118,8 @@ const macOSAppMenuTemplate = {
]
};
-const developerMenuTemplate = {
- label: 'Developer',
- submenu: [
- {
- label: 'Reload',
- accelerator: 'CmdOrCtrl+R',
- click(item, focusedWindow) {
- if (focusedWindow) {
- focusedWindow.reload();
- }
- }
- },
- {
- label: 'Toggle Developer Tools',
- accelerator: process.platform == 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
- click(item, focusedWindow) {
- if (focusedWindow) {
- focusedWindow.webContents.toggleDevTools();
- }
- }
- },
- ]
-};
-
-module.exports = {
- showMenubar(showDeveloperMenu) {
+module.exports = () => {
let template = baseTemplate.slice();
- if (process.platform === 'darwin') {
- template.unshift(macOSAppMenuTemplate);
- }
- if (showDeveloperMenu) {
- template.push(developerMenuTemplate);
- }
-
+ (process.platform === 'darwin') && template.unshift(macOSAppMenuTemplate);
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
- },
};
diff --git a/ui/js/actions/settings.js b/ui/js/actions/settings.js
index 79c098a95..6a2455bf9 100644
--- a/ui/js/actions/settings.js
+++ b/ui/js/actions/settings.js
@@ -133,30 +133,27 @@ export function doDownloadLanguages() {
fs.mkdirSync(app.i18n.directory);
}
- const xhr = new XMLHttpRequest();
- xhr.onreadystatechange = () => {
- if (xhr.readyState === XMLHttpRequest.DONE) {
- if (xhr.status === 200) {
- try {
- const files = JSON.parse(xhr.responseText);
- const actions = [];
- files.forEach(file => {
- actions.push(doDownloadLanguage(file));
- });
-
- dispatch(batchActions(...actions));
- } catch (err) {
- throw err;
- }
- } else {
- throw new Error(
- __("The list of available languages could not be retrieved.")
- );
- }
+ function checkStatus(response) {
+ if (response.status >= 200 && response.status < 300) {
+ return response;
+ } else {
+ throw new Error(
+ __("The list of available languages could not be retrieved.")
+ );
}
- };
- xhr.open("get", "http://i18n.lbry.io");
- xhr.send();
+ }
+
+ 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));
+ });
};
}
diff --git a/ui/js/component/transactionList/internal/TransactionListItem.jsx b/ui/js/component/transactionList/internal/TransactionListItem.jsx
index 673dc46bc..229dc8fd6 100644
--- a/ui/js/component/transactionList/internal/TransactionListItem.jsx
+++ b/ui/js/component/transactionList/internal/TransactionListItem.jsx
@@ -53,6 +53,10 @@ class TransactionListItem extends React.PureComponent {
}
}
+ capitalize(string) {
+ return string.charAt(0).toUpperCase() + string.slice(1);
+ }
+
render() {
const { reward, transaction, isRevokeable } = this.props;
const {
@@ -108,8 +112,9 @@ class TransactionListItem extends React.PureComponent {
/>}
- {type}{" "}
+ {this.capitalize(type)}{" "}
{isRevokeable && this.getLink(type)}
+
|
{reward &&
diff --git a/ui/js/component/transactionList/view.jsx b/ui/js/component/transactionList/view.jsx
index 975fa4df3..a24f9f205 100644
--- a/ui/js/component/transactionList/view.jsx
+++ b/ui/js/component/transactionList/view.jsx
@@ -1,7 +1,6 @@
import React from "react";
import TransactionListItem from "./internal/TransactionListItem";
import FormField from "component/formField";
-import lbryuri from "lbryuri";
import * as modals from "constants/modal_types";
class TransactionList extends React.PureComponent {
diff --git a/ui/js/jsonrpc.js b/ui/js/jsonrpc.js
index 4d1ed5bd3..f6c638083 100644
--- a/ui/js/jsonrpc.js
+++ b/ui/js/jsonrpc.js
@@ -9,80 +9,90 @@ jsonrpc.call = function(
connectFailedCallback,
timeout
) {
- var xhr = new XMLHttpRequest();
- if (typeof connectFailedCallback !== "undefined") {
- if (timeout) {
- xhr.timeout = timeout;
+ function checkStatus(response) {
+ if (response.status >= 200 && response.status < 300) {
+ return response;
+ } else {
+ var error = new Error(response.statusText);
+ error.response = response;
+ throw error;
}
+ }
- xhr.addEventListener("error", function(e) {
- connectFailedCallback(e);
- });
- xhr.addEventListener("timeout", function() {
- connectFailedCallback(
- new Error(__("XMLHttpRequest connection timed out"))
- );
+ function parseJSON(response) {
+ return response.json();
+ }
+
+ function makeRequest(url, options) {
+ return new Promise((resolve, reject) => {
+ fetch(url, options).then(resolve).catch(reject);
+
+ if (timeout) {
+ const e = new Error(__("XMLHttpRequest connection timed out"));
+ setTimeout(() => {
+ return reject(e);
+ }, timeout);
+ }
});
}
- xhr.addEventListener("load", function() {
- var response = JSON.parse(xhr.responseText);
- let error = response.error || (response.result && response.result.error);
- if (error) {
- if (errorCallback) {
- errorCallback(error);
- } else {
- var errorEvent = new CustomEvent("unhandledError", {
- detail: {
- connectionString: connectionString,
- method: method,
- params: params,
- code: error.code,
- message: error.message || error,
- data: error.data,
- },
- });
- document.dispatchEvent(errorEvent);
+ const counter = parseInt(sessionStorage.getItem("JSONRPCCounter") || 0);
+ const url = connectionString;
+ const options = {
+ method: "POST",
+ body: JSON.stringify({
+ jsonrpc: "2.0",
+ method: method,
+ params: params,
+ id: counter,
+ }),
+ };
+
+ sessionStorage.setItem("JSONRPCCounter", counter + 1);
+
+ return fetch(url, options)
+ .then(checkStatus)
+ .then(parseJSON)
+ .then(response => {
+ const error =
+ response.error || (response.result && response.result.error);
+
+ if (!error && typeof callback === "function") {
+ return callback(response.result);
+ }
+
+ if (error && typeof errorCallback === "function") {
+ return errorCallback(error);
}
- } else if (callback) {
- callback(response.result);
- }
- });
- if (connectFailedCallback) {
- xhr.addEventListener("error", function(event) {
- connectFailedCallback(event);
- });
- } else {
- xhr.addEventListener("error", function(event) {
var errorEvent = new CustomEvent("unhandledError", {
detail: {
connectionString: connectionString,
method: method,
params: params,
- code: xhr.status,
+ code: error.code,
+ message: error.message || error,
+ data: error.data,
+ },
+ });
+ document.dispatchEvent(errorEvent);
+ })
+ .catch(e => {
+ if (connectFailedCallback) {
+ return connectFailedCallback(e);
+ }
+
+ var errorEvent = new CustomEvent("unhandledError", {
+ detail: {
+ connectionString: connectionString,
+ method: method,
+ params: params,
+ code: e.response && e.response.status,
message: __("Connection to API server failed"),
},
});
document.dispatchEvent(errorEvent);
});
- }
-
- const counter = parseInt(sessionStorage.getItem("JSONRPCCounter") || 0);
-
- xhr.open("POST", connectionString, true);
- xhr.send(
- JSON.stringify({
- jsonrpc: "2.0",
- method: method,
- params: params,
- id: counter,
- })
- );
-
- sessionStorage.setItem("JSONRPCCounter", counter + 1);
-
- return xhr;
};
export default jsonrpc;
diff --git a/ui/js/lbryio.js b/ui/js/lbryio.js
index 336bd3d76..03b08496d 100644
--- a/ui/js/lbryio.js
+++ b/ui/js/lbryio.js
@@ -36,80 +36,56 @@ lbryio.getExchangeRates = function() {
};
lbryio.call = function(resource, action, params = {}, method = "get") {
- return new Promise((resolve, reject) => {
- if (!lbryio.enabled && (resource != "discover" || action != "list")) {
- console.log(__("Internal API disabled"));
- reject(new Error(__("LBRY internal API is disabled")));
- return;
+ 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")));
+ }
+
+ function checkStatus(response) {
+ if (response.status >= 200 && response.status < 300) {
+ return response;
+ } else {
+ var error = new Error(response.statusText);
+ error.response = response;
+ throw error;
+ }
+ }
+
+ function parseJSON(response) {
+ return response.json();
+ }
+
+ function makeRequest(url, options) {
+ return fetch(url, options).then(checkStatus).then(parseJSON).catch(e => {
+ throw new Error(__("Something went wrong making an internal API call."));
+ });
+ }
+
+ 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",
+ };
+
+ if (method == "post") {
+ options = {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ body: qs,
+ };
+ url = `${CONNECTION_STRING}${resource}/${action}`;
}
- const xhr = new XMLHttpRequest();
-
- xhr.addEventListener("error", function(event) {
- reject(
- new Error(__("Something went wrong making an internal API call."))
- );
- });
-
- xhr.addEventListener("timeout", function() {
- reject(new Error(__("XMLHttpRequest connection timed out")));
- });
-
- xhr.addEventListener("load", function() {
- const response = JSON.parse(xhr.responseText);
-
- if (!response.success) {
- if (reject) {
- const error = new Error(response.error);
- error.xhr = xhr;
- reject(error);
- } else {
- document.dispatchEvent(
- new CustomEvent("unhandledError", {
- detail: {
- connectionString: connectionString,
- method: action,
- params: params,
- message: response.error.message,
- ...(response.error.data ? { data: response.error.data } : {}),
- },
- })
- );
- }
- } else {
- resolve(response.data);
- }
- });
-
- lbryio
- .getAuthToken()
- .then(token => {
- const fullParams = { auth_token: token, ...params };
-
- if (method == "get") {
- xhr.open(
- "get",
- CONNECTION_STRING +
- resource +
- "/" +
- action +
- "?" +
- querystring.stringify(fullParams),
- true
- );
- xhr.send();
- } else if (method == "post") {
- xhr.open("post", CONNECTION_STRING + resource + "/" + action, true);
- xhr.setRequestHeader(
- "Content-type",
- "application/x-www-form-urlencoded"
- );
- xhr.send(querystring.stringify(fullParams));
- } else {
- reject(new Error(__("Invalid method")));
- }
- })
- .catch(reject);
+ return makeRequest(url, options).then(response => response.data);
});
};
diff --git a/ui/js/page/settings/view.jsx b/ui/js/page/settings/view.jsx
index e11a2443f..a4a3a2547 100644
--- a/ui/js/page/settings/view.jsx
+++ b/ui/js/page/settings/view.jsx
@@ -237,7 +237,7 @@ class SettingsPage extends React.PureComponent {
{
this.onInstantPurchaseEnabledChange(false);
@@ -247,7 +247,7 @@ class SettingsPage extends React.PureComponent {
|