diff --git a/CHANGELOG.md b/CHANGELOG.md index c35165bef..477ed8013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Web UI version numbers should always match the corresponding version of LBRY App * Support markdown makeup in claim description * Replaced free speech flag (used when image is missing) with labeled color tiles * Added a loading message to file actions + * URL is auto suggested in Publish Page ### Changed * Publishes now uses claims rather than files @@ -28,6 +29,11 @@ Web UI version numbers should always match the corresponding version of LBRY App * Restored feedback on claim amounts * Fixed hiding price input when Free is checked on publish form * Fixed hiding new identity fields on publish form + * Fixed files on downloaded tab not showing download progress + * Fixed downloading files that are deleted not being removed from the downloading list + * Fixed download progress bar not being cleared when a downloading file is deleted + * Fixed refresh regression after adding scroll position to history state + * Fixed app thinking downloads with 0 progress were downloaded after restart ### Deprecated * diff --git a/build/build.ps1 b/build/build.ps1 index 6c93b172c..569fbd875 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -46,8 +46,8 @@ dir dist # verify that binary was built/named correctly # sign binary nuget install secure-file -ExcludeVersion -secure-file\tools\secure-file -decrypt build\lbry2.pfx.enc -secret "$env:pfx_key" -& ${env:SIGNTOOL_PATH} sign /f build\lbry2.pfx /p "$env:key_pass" /tr http://tsa.starfieldtech.com /td SHA256 /fd SHA256 dist\*.exe +secure-file\tools\secure-file -decrypt build\lbry3.pfx.enc -secret "$env:pfx_key" +& ${env:SIGNTOOL_PATH} sign /f build\lbry3.pfx /p "$env:key_pass" /tr http://tsa.starfieldtech.com /td SHA256 /fd SHA256 dist\*.exe -python build\upload_assets.py \ No newline at end of file +python build\upload_assets.py diff --git a/build/lbry2.pfx.enc b/build/lbry2.pfx.enc deleted file mode 100644 index 46e52260a..000000000 Binary files a/build/lbry2.pfx.enc and /dev/null differ diff --git a/build/lbry3.pfx.enc b/build/lbry3.pfx.enc new file mode 100644 index 000000000..330cfc05b Binary files /dev/null and b/build/lbry3.pfx.enc differ diff --git a/ui/js/actions/app.js b/ui/js/actions/app.js index 26425aad5..e6d5879f8 100644 --- a/ui/js/actions/app.js +++ b/ui/js/actions/app.js @@ -13,6 +13,7 @@ import { doFetchDaemonSettings } from "actions/settings"; import { doAuthenticate } from "actions/user"; import { doFileList } from "actions/file_info"; import { toQueryString } from "util/query_params"; +import { parseQueryParams } from "util/query_params"; const { remote, ipcRenderer, shell } = require("electron"); const path = require("path"); @@ -76,6 +77,7 @@ export function doChangePath(path, options = {}) { export function doHistoryBack() { return function(dispatch, getState) { if (!history.state) return; + if (history.state.index === 0) return; history.back(); }; @@ -260,7 +262,9 @@ export function doAlertError(errorList) { export function doDaemonReady() { return function(dispatch, getState) { - history.replaceState({}, document.title, `#/discover`); + const path = window.location.hash || "#/discover"; + const params = parseQueryParams(path.split("?")[1] || ""); + history.replaceState({ params, index: 0 }, document.title, `${path}`); dispatch(doAuthenticate()); dispatch({ type: types.DAEMON_READY, diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js index 9d6f55408..b0e40ffa8 100644 --- a/ui/js/actions/content.js +++ b/ui/js/actions/content.js @@ -5,7 +5,7 @@ import lbryuri from "lbryuri"; import { selectBalance } from "selectors/wallet"; import { selectFileInfoForUri, - selectUrisDownloading, + selectDownloadingByOutpoint, } from "selectors/file_info"; import { selectResolvingUris } from "selectors/content"; import { selectCostInfoForUri } from "selectors/cost_info"; @@ -265,8 +265,9 @@ export function doPurchaseUri(uri, purchaseModalName) { const state = getState(); const balance = selectBalance(state); const fileInfo = selectFileInfoForUri(state, { uri }); - const downloadingByUri = selectUrisDownloading(state); - const alreadyDownloading = !!downloadingByUri[uri]; + const downloadingByOutpoint = selectDownloadingByOutpoint(state); + const alreadyDownloading = + fileInfo && !!downloadingByOutpoint[fileInfo.outpoint]; // we already fully downloaded the file. if (fileInfo && fileInfo.completed) { diff --git a/ui/js/actions/file_info.js b/ui/js/actions/file_info.js index d21df8d2a..9ed2df5a7 100644 --- a/ui/js/actions/file_info.js +++ b/ui/js/actions/file_info.js @@ -10,8 +10,11 @@ import { selectIsFetchingFileList, selectFileInfosByOutpoint, selectUrisLoading, + selectTotalDownloadProgress, } from "selectors/file_info"; -import { doCloseModal } from "actions/app"; +import { doCloseModal, doHistoryBack } from "actions/app"; +import setProgressBar from "util/setProgressBar"; +import batchActions from "util/batchActions"; const { shell } = require("electron"); @@ -119,7 +122,22 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) { }, }); - dispatch(doCloseModal()); + const totalProgress = selectTotalDownloadProgress(getState()); + setProgressBar(totalProgress); + }; +} + +export function doDeleteFileAndGoBack( + fileInfo, + deleteFromComputer, + abandonClaim +) { + return function(dispatch, getState) { + const actions = []; + actions.push(doCloseModal()); + actions.push(doHistoryBack()); + actions.push(doDeleteFile(fileInfo, deleteFromComputer, abandonClaim)); + dispatch(batchActions(...actions)); }; } diff --git a/ui/js/component/fileActions/view.jsx b/ui/js/component/fileActions/view.jsx index ad135a3cd..9ff0fbaa1 100644 --- a/ui/js/component/fileActions/view.jsx +++ b/ui/js/component/fileActions/view.jsx @@ -118,7 +118,10 @@ class FileActions extends React.PureComponent { /> ); - } else if (fileInfo === null && !downloading) { + } else if ( + (fileInfo === null || (fileInfo && fileInfo.written_bytes === 0)) && + !downloading + ) { if (!costInfo) { content = ; } else { diff --git a/ui/js/component/modalRemoveFile/index.js b/ui/js/component/modalRemoveFile/index.js index de54514d5..211bdff26 100644 --- a/ui/js/component/modalRemoveFile/index.js +++ b/ui/js/component/modalRemoveFile/index.js @@ -1,8 +1,9 @@ import React from "react"; import { connect } from "react-redux"; import { doCloseModal, doHistoryBack } from "actions/app"; -import { doDeleteFile } from "actions/file_info"; +import { doDeleteFileAndGoBack } from "actions/file_info"; import { makeSelectClaimForUriIsMine } from "selectors/claims"; +import batchActions from "util/batchActions"; import ModalRemoveFile from "./view"; @@ -19,8 +20,7 @@ const makeSelect = () => { const perform = dispatch => ({ closeModal: () => dispatch(doCloseModal()), deleteFile: (fileInfo, deleteFromComputer, abandonClaim) => { - dispatch(doHistoryBack()); - dispatch(doDeleteFile(fileInfo, deleteFromComputer, abandonClaim)); + dispatch(doDeleteFileAndGoBack(fileInfo, deleteFromComputer, abandonClaim)); }, }); diff --git a/ui/js/component/publishForm/view.jsx b/ui/js/component/publishForm/view.jsx index e73038e16..970787205 100644 --- a/ui/js/component/publishForm/view.jsx +++ b/ui/js/component/publishForm/view.jsx @@ -43,6 +43,7 @@ class PublishForm extends React.PureComponent { creatingChannel: false, modal: null, isFee: false, + customUrl: false, }; } @@ -203,6 +204,9 @@ class PublishForm extends React.PureComponent { handleNameChange(event) { var rawName = event.target.value; + this.setState({ + customUrl: Boolean(rawName.length), + }); this.nameChanged(rawName); } @@ -445,11 +449,24 @@ class PublishForm extends React.PureComponent { onFileChange() { if (this.refs.file.getValue()) { this.setState({ hasFile: true }); + if (!this.state.customUrl) { + let fileName = this._getFileName(this.refs.file.getValue()); + this.nameChanged(fileName); + } } else { this.setState({ hasFile: false }); } } + _getFileName(fileName) { + const path = require("path"); + const extension = path.extname(fileName); + + fileName = path.basename(fileName, extension); + fileName = fileName.replace(lbryuri.REGEXP_INVALID_URI, ""); + return fileName; + } + getNameBidHelpText() { if (this.state.prefillDone) { return __("Existing claim data was prefilled"); diff --git a/ui/js/component/userEmailVerify/view.jsx b/ui/js/component/userEmailVerify/view.jsx index d0c5581df..e1bcfdfd1 100644 --- a/ui/js/component/userEmailVerify/view.jsx +++ b/ui/js/component/userEmailVerify/view.jsx @@ -34,7 +34,6 @@ class UserEmailVerify extends React.PureComponent { { @@ -45,7 +44,7 @@ class UserEmailVerify extends React.PureComponent { {/* render help separately so it always shows */}

- {__("Email")}{" "} + {__("Check your email for a verification code. Email")}{" "} {" "} {__("if you did not receive or are having trouble with your code.")}

diff --git a/ui/js/lbry.js b/ui/js/lbry.js index 6b4730762..07dec7e5e 100644 --- a/ui/js/lbry.js +++ b/ui/js/lbry.js @@ -291,7 +291,7 @@ lbry.setClientSetting = function(setting, value) { lbry.formatName = function(name) { // Converts LBRY name to standard format (all lower case, no special characters, spaces replaced by dashes) name = name.replace("/s+/g", "-"); - name = name.toLowerCase().replace(/[^a-z0-9\-]/g, ""); + name = name.toLowerCase().replace(lbryuri.REGEXP_INVALID_URI, ""); return name; }; diff --git a/ui/js/lbryuri.js b/ui/js/lbryuri.js index 42a825949..a7f43890a 100644 --- a/ui/js/lbryuri.js +++ b/ui/js/lbryuri.js @@ -3,6 +3,8 @@ const CLAIM_ID_MAX_LEN = 40; const lbryuri = {}; +lbryuri.REGEXP_INVALID_URI = /[^A-Za-z0-9-]/g; + /** * Parses a LBRY name into its component parts. Throws errors with user-friendly * messages for invalid names. @@ -70,7 +72,7 @@ lbryuri.parse = function(uri, requireProto = false) { contentName = path; } - const nameBadChars = (channelName || name).match(/[^A-Za-z0-9-]/g); + const nameBadChars = (channelName || name).match(lbryuri.REGEXP_INVALID_URI); if (nameBadChars) { throw new Error( __( @@ -119,7 +121,7 @@ lbryuri.parse = function(uri, requireProto = false) { throw new Error(__("Only channel URIs may have a path.")); } - const pathBadChars = path.match(/[^A-Za-z0-9-]/g); + const pathBadChars = path.match(lbryuri.REGEXP_INVALID_URI); if (pathBadChars) { throw new Error( __(`Invalid character in path: %s`, pathBadChars.join(", ")) diff --git a/ui/js/reducers/file_info.js b/ui/js/reducers/file_info.js index 500fbdf82..cb5221c98 100644 --- a/ui/js/reducers/file_info.js +++ b/ui/js/reducers/file_info.js @@ -58,15 +58,15 @@ reducers[types.DOWNLOADING_STARTED] = function(state, action) { const { uri, outpoint, fileInfo } = action.data; const newByOutpoint = Object.assign({}, state.byOutpoint); - const newDownloading = Object.assign({}, state.urisDownloading); + const newDownloading = Object.assign({}, state.downloadingByOutpoin); const newLoading = Object.assign({}, state.urisLoading); - newDownloading[uri] = true; + newDownloading[outpoint] = true; newByOutpoint[outpoint] = fileInfo; delete newLoading[uri]; return Object.assign({}, state, { - urisDownloading: newDownloading, + downloadingByOutpoint: newDownloading, urisLoading: newLoading, byOutpoint: newByOutpoint, }); @@ -76,14 +76,14 @@ reducers[types.DOWNLOADING_PROGRESSED] = function(state, action) { const { uri, outpoint, fileInfo } = action.data; const newByOutpoint = Object.assign({}, state.byOutpoint); - const newDownloading = Object.assign({}, state.urisDownloading); + const newDownloading = Object.assign({}, state.downloadingByOutpoint); newByOutpoint[outpoint] = fileInfo; - newDownloading[uri] = true; + newDownloading[outpoint] = true; return Object.assign({}, state, { byOutpoint: newByOutpoint, - urisDownloading: newDownloading, + downloadingByOutpoint: newDownloading, }); }; @@ -91,14 +91,14 @@ reducers[types.DOWNLOADING_COMPLETED] = function(state, action) { const { uri, outpoint, fileInfo } = action.data; const newByOutpoint = Object.assign({}, state.byOutpoint); - const newDownloading = Object.assign({}, state.urisDownloading); + const newDownloading = Object.assign({}, state.downloadingByOutpoint); newByOutpoint[outpoint] = fileInfo; - delete newDownloading[uri]; + delete newDownloading[outpoint]; return Object.assign({}, state, { byOutpoint: newByOutpoint, - urisDownloading: newDownloading, + downloadingByOutpoint: newDownloading, }); }; @@ -106,11 +106,14 @@ reducers[types.FILE_DELETE] = function(state, action) { const { outpoint } = action.data; const newByOutpoint = Object.assign({}, state.byOutpoint); + const downloadingByOutpoint = Object.assign({}, state.downloadingByOutpoint); delete newByOutpoint[outpoint]; + delete downloadingByOutpoint[outpoint]; return Object.assign({}, state, { byOutpoint: newByOutpoint, + downloadingByOutpoint, }); }; diff --git a/ui/js/selectors/file_info.js b/ui/js/selectors/file_info.js index ef469ab02..1b3dc7982 100644 --- a/ui/js/selectors/file_info.js +++ b/ui/js/selectors/file_info.js @@ -39,14 +39,18 @@ export const makeSelectFileInfoForUri = () => { return createSelector(selectFileInfoForUri, fileInfo => fileInfo); }; -export const selectUrisDownloading = createSelector( +export const selectDownloadingByOutpoint = createSelector( _selectState, - state => state.urisDownloading || {} + state => state.downloadingByOutpoint || {} ); const selectDownloadingForUri = (state, props) => { - const byUri = selectUrisDownloading(state); - return byUri[props.uri]; + const byOutpoint = selectDownloadingByOutpoint(state); + const fileInfo = selectFileInfoForUri(state, props); + + if (!fileInfo) return false; + + return byOutpoint[fileInfo.outpoint]; }; export const makeSelectDownloadingForUri = () => { @@ -135,14 +139,14 @@ export const selectFileInfosByUri = createSelector( ); export const selectDownloadingFileInfos = createSelector( - selectUrisDownloading, - selectFileInfosByUri, - (urisDownloading, byUri) => { - const uris = Object.keys(urisDownloading); + selectDownloadingByOutpoint, + selectFileInfosByOutpoint, + (downloadingByOutpoint, fileInfosByOutpoint) => { + const outpoints = Object.keys(downloadingByOutpoint); const fileInfos = []; - uris.forEach(uri => { - const fileInfo = byUri[uri]; + outpoints.forEach(outpoint => { + const fileInfo = fileInfosByOutpoint[outpoint]; if (fileInfo) fileInfos.push(fileInfo); });