lbry-desktop/ui/redux/actions/app.js

695 lines
18 KiB
JavaScript
Raw Normal View History

2019-02-22 06:01:59 +01:00
// @if TARGET='app'
import { execSync } from 'child_process';
import isDev from 'electron-is-dev';
import { ipcRenderer, remote } from 'electron';
2019-02-22 06:01:59 +01:00
// @endif
2019-03-05 05:46:57 +01:00
import path from 'path';
2018-11-07 23:44:38 +01:00
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
2020-06-04 19:43:36 +02:00
import { DOMAIN } from 'config';
2019-08-14 05:04:08 +02:00
import {
Lbry,
doBalanceSubscribe,
doFetchFileInfos,
2019-08-14 05:04:08 +02:00
makeSelectClaimForUri,
makeSelectClaimIsMine,
2019-09-26 21:31:27 +02:00
doPopulateSharedUserState,
2019-09-26 18:07:11 +02:00
doFetchChannelListMine,
2019-10-04 19:29:57 +02:00
doClearPublish,
2019-10-15 06:20:12 +02:00
doPreferenceGet,
doClearSupport,
SHARED_PREFERENCES,
DAEMON_SETTINGS,
SETTINGS,
2019-08-14 05:04:08 +02:00
} from 'lbry-redux';
import { selectFollowedTagsList } from 'redux/selectors/tags';
2020-07-23 16:22:57 +02:00
import { doToast, doError, doNotificationList } from 'redux/actions/notifications';
2018-04-18 06:03:01 +02:00
import Native from 'native';
import {
doFetchDaemonSettings,
doSetAutoLaunch,
doSetDaemonSetting,
doFindFFmpeg,
doGetDaemonStatus,
} from 'redux/actions/settings';
2017-04-07 07:15:22 +02:00
import {
selectIsUpgradeSkipped,
2017-04-07 07:15:22 +02:00
selectUpdateUrl,
selectUpgradeDownloadItem,
selectUpgradeDownloadPath,
2017-07-29 01:31:10 +02:00
selectUpgradeFilename,
selectAutoUpdateDeclined,
selectRemoteVersion,
selectUpgradeTimer,
selectModal,
selectAllowAnalytics,
} from 'redux/selectors/app';
import { selectDaemonSettings, makeSelectClientSetting } from 'redux/selectors/settings';
2020-11-10 06:21:04 +01:00
import { selectUser, selectUserVerifiedEmail } from 'redux/selectors/user';
// import { selectDaemonSettings } from 'redux/selectors/settings';
import { doSyncSubscribe, doSetPrefsReady } from 'redux/actions/sync';
import { doAuthenticate } from 'redux/actions/user';
2018-09-24 05:44:42 +02:00
import { lbrySettings as config, version as appVersion } from 'package.json';
import analytics, { SHARE_INTERNAL } from 'analytics';
import { doSignOutCleanup } from 'util/saved-passwords';
2020-07-23 16:22:57 +02:00
import { doSocketConnect } from 'redux/actions/websocket';
import { stringifyServerParam, shouldSetSetting } from 'util/sync-settings';
2020-08-07 22:59:20 +02:00
import sha256 from 'crypto-js/sha256';
import Base64 from 'crypto-js/enc-base64';
2019-02-22 06:01:59 +01:00
// @if TARGET='app'
const { autoUpdater } = remote.require('electron-updater');
const { download } = remote.require('electron-dl');
const Fs = remote.require('fs');
2019-02-22 06:01:59 +01:00
// @endif
2017-12-13 22:36:30 +01:00
2017-11-16 22:39:10 +01:00
const CHECK_UPGRADE_INTERVAL = 10 * 60 * 1000;
2017-04-07 07:15:22 +02:00
export function doOpenModal(id, modalProps = {}) {
return {
type: ACTIONS.SHOW_MODAL,
data: {
id,
modalProps,
},
};
}
export function doHideModal() {
return {
type: ACTIONS.HIDE_MODAL,
};
}
2017-04-07 07:15:22 +02:00
export function doUpdateDownloadProgress(percent) {
return {
type: ACTIONS.UPGRADE_DOWNLOAD_PROGRESSED,
2017-04-07 07:15:22 +02:00
data: {
2017-12-13 22:36:30 +01:00
percent,
2017-06-06 23:19:12 +02:00
},
};
2017-04-07 07:15:22 +02:00
}
export function doSkipUpgrade() {
return {
type: ACTIONS.SKIP_UPGRADE,
2017-06-06 23:19:12 +02:00
};
2017-04-07 07:15:22 +02:00
}
export function doStartUpgrade() {
return (dispatch, getState) => {
2017-06-06 23:19:12 +02:00
const state = getState();
const upgradeDownloadPath = selectUpgradeDownloadPath(state);
2017-04-07 07:15:22 +02:00
ipcRenderer.send('upgrade', upgradeDownloadPath);
2017-06-06 23:19:12 +02:00
};
2017-04-07 07:15:22 +02:00
}
export function doDownloadUpgrade() {
return (dispatch, getState) => {
2019-02-22 06:01:59 +01:00
// @if TARGET='app'
2017-06-06 23:19:12 +02:00
const state = getState();
2017-04-07 07:15:22 +02:00
// Make a new directory within temp directory so the filename is guaranteed to be available
const dir = Fs.mkdtempSync(remote.app.getPath('temp') + path.sep);
const upgradeFilename = selectUpgradeFilename(state);
2017-04-07 07:15:22 +02:00
2017-12-13 22:36:30 +01:00
const options = {
2017-06-06 23:19:12 +02:00
onProgress: p => dispatch(doUpdateDownloadProgress(Math.round(p * 100))),
2017-04-07 07:15:22 +02:00
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.
*/
2017-04-07 07:15:22 +02:00
dispatch({
type: ACTIONS.UPGRADE_DOWNLOAD_COMPLETED,
data: {
downloadItem,
path: path.join(dir, upgradeFilename),
},
});
});
2017-04-07 07:15:22 +02:00
dispatch({
type: ACTIONS.UPGRADE_DOWNLOAD_STARTED,
2017-06-06 23:19:12 +02:00
});
dispatch(doHideModal());
dispatch(doOpenModal(MODALS.DOWNLOADING));
2019-02-22 06:01:59 +01:00
// @endif
2017-06-06 23:19:12 +02:00
};
2017-04-07 07:15:22 +02:00
}
export function doDownloadUpgradeRequested() {
// This means the user requested an upgrade by clicking the "upgrade" button in the navbar.
// If on Mac and Windows, we do some new behavior for the auto-update system.
// This will probably be reorganized once we get auto-update going on Linux and remove
// the old logic.
return dispatch => {
if (['win32', 'darwin'].includes(process.platform) || !!process.env.APPIMAGE) {
// electron-updater behavior
dispatch(doOpenModal(MODALS.AUTO_UPDATE_DOWNLOADED));
} else {
// Old behavior for Linux
dispatch(doDownloadUpgrade());
}
};
}
export function doClearUpgradeTimer() {
return (dispatch, getState) => {
const state = getState();
if (selectUpgradeTimer(state)) {
clearInterval(selectUpgradeTimer(state));
dispatch({
type: ACTIONS.CLEAR_UPGRADE_TIMER,
});
}
};
}
export function doAutoUpdate() {
return dispatch => {
dispatch({
type: ACTIONS.AUTO_UPDATE_DOWNLOADED,
});
dispatch(doOpenModal(MODALS.AUTO_UPDATE_DOWNLOADED));
dispatch(doClearUpgradeTimer());
};
}
export function doAutoUpdateDeclined() {
return dispatch => {
dispatch(doClearUpgradeTimer());
dispatch({
type: ACTIONS.AUTO_UPDATE_DECLINED,
});
2018-02-24 01:24:00 +01:00
};
}
2017-04-07 07:15:22 +02:00
export function doCancelUpgrade() {
return (dispatch, getState) => {
2017-06-06 23:19:12 +02:00
const state = getState();
const upgradeDownloadItem = selectUpgradeDownloadItem(state);
2017-04-07 07:15:22 +02:00
if (upgradeDownloadItem) {
/*
* Right now the remote reference to the download item gets garbage collected as soon as the
* the download is over (maybe even earlier), so trying to cancel a finished download may
* throw an error.
*/
try {
upgradeDownloadItem.cancel();
} catch (err) {
2019-07-23 10:05:51 +02:00
console.error(err); // eslint-disable-line no-console
2017-04-07 07:15:22 +02:00
}
}
dispatch({ type: ACTIONS.UPGRADE_CANCELLED });
2017-06-06 23:19:12 +02:00
};
2017-04-07 07:15:22 +02:00
}
export function doCheckUpgradeAvailable() {
return (dispatch, getState) => {
2017-06-06 23:19:12 +02:00
const state = getState();
dispatch({
type: ACTIONS.CHECK_UPGRADE_START,
});
2017-04-07 07:15:22 +02:00
if (['win32', 'darwin'].includes(process.platform) || !!process.env.APPIMAGE) {
// On Windows, Mac, and AppImage, updates happen silently through
// electron-updater.
const autoUpdateDeclined = selectAutoUpdateDeclined(state);
if (!autoUpdateDeclined && !isDev) {
autoUpdater.checkForUpdates();
}
return;
}
const success = ({ remoteVersion, upgradeAvailable }) => {
dispatch({
type: ACTIONS.CHECK_UPGRADE_SUCCESS,
data: {
upgradeAvailable,
remoteVersion,
},
});
if (
upgradeAvailable &&
!selectModal(state) &&
(!selectIsUpgradeSkipped(state) || remoteVersion !== selectRemoteVersion(state))
) {
dispatch(doOpenModal(MODALS.UPGRADE));
}
};
const fail = () => {
dispatch({
type: ACTIONS.CHECK_UPGRADE_FAIL,
});
};
2018-04-18 06:03:01 +02:00
Native.getAppVersionInfo().then(success, fail);
};
}
/*
Initiate a timer that will check for an app upgrade every 10 minutes.
*/
2017-11-16 22:39:10 +01:00
export function doCheckUpgradeSubscribe() {
return dispatch => {
2019-05-07 23:38:29 +02:00
const checkUpgradeTimer = setInterval(() => dispatch(doCheckUpgradeAvailable()), CHECK_UPGRADE_INTERVAL);
dispatch({
type: ACTIONS.CHECK_UPGRADE_SUBSCRIBE,
data: { checkUpgradeTimer },
2017-04-07 07:15:22 +02:00
});
2017-06-06 23:19:12 +02:00
};
2017-04-07 07:15:22 +02:00
}
export function doCheckDaemonVersion() {
return dispatch => {
2019-02-22 06:01:59 +01:00
// @if TARGET='app'
Lbry.version().then(({ lbrynet_version: lbrynetVersion }) => {
// Avoid the incompatible daemon modal if running in dev mode
// Lets you run a different daemon than the one specified in package.json
2020-02-05 19:01:07 +01:00
if (config.lbrynetDaemonVersion === lbrynetVersion || process.env.NODE_ENV !== 'production') {
return dispatch({
2018-05-11 01:06:41 +02:00
type: ACTIONS.DAEMON_VERSION_MATCH,
});
}
dispatch({
2018-05-11 01:06:41 +02:00
type: ACTIONS.DAEMON_VERSION_MISMATCH,
});
2020-02-05 19:01:07 +01:00
if (process.env.NODE_ENV === 'production') {
return dispatch(doOpenModal(MODALS.INCOMPATIBLE_DAEMON));
}
});
2019-02-22 06:01:59 +01:00
// @endif
// @if TARGET='web'
dispatch({
type: ACTIONS.DAEMON_VERSION_MATCH,
});
// @endif
};
}
2018-07-18 21:48:30 +02:00
export function doNotifyEncryptWallet() {
return dispatch => {
dispatch(doOpenModal(MODALS.WALLET_ENCRYPT));
2018-07-18 21:48:30 +02:00
};
}
export function doNotifyDecryptWallet() {
return dispatch => {
dispatch(doOpenModal(MODALS.WALLET_DECRYPT));
2018-07-18 21:48:30 +02:00
};
}
export function doNotifyUnlockWallet() {
return dispatch => {
dispatch(doOpenModal(MODALS.WALLET_UNLOCK));
2018-07-18 21:48:30 +02:00
};
}
export function doNotifyForgetPassword(props) {
2019-08-20 14:29:59 +02:00
return dispatch => {
dispatch(doOpenModal(MODALS.WALLET_PASSWORD_UNSAVE, props));
2019-08-20 14:29:59 +02:00
};
}
2017-04-07 07:15:22 +02:00
export function doAlertError(errorList) {
return dispatch => {
dispatch(doError(errorList));
2017-06-06 23:19:12 +02:00
};
2017-04-07 07:15:22 +02:00
}
export function doAlertWaitingForSync() {
2020-11-10 06:21:04 +01:00
return (dispatch, getState) => {
const state = getState();
const authenticated = selectUserVerifiedEmail(state);
dispatch(
doToast({
2020-11-10 06:21:04 +01:00
message:
!authenticated && IS_WEB
? __('Sign in or create an account to change this setting.')
: __('Please wait a bit, we are still getting your account ready.'),
isError: false,
})
);
2020-11-10 06:21:04 +01:00
};
}
2017-04-22 15:17:01 +02:00
export function doDaemonReady() {
return (dispatch, getState) => {
2017-11-16 22:39:10 +01:00
const state = getState();
// TODO: call doFetchDaemonSettings, then get usage data, and call doAuthenticate once they are loaded into the store
const shareUsageData = IS_WEB || window.localStorage.getItem(SHARE_INTERNAL) === 'true';
2020-03-12 17:36:45 +01:00
dispatch(
2020-06-04 19:43:36 +02:00
doAuthenticate(
appVersion,
undefined,
undefined,
shareUsageData,
status => {
const trendingAlgorithm =
status &&
status.wallet &&
status.wallet.connected_features &&
status.wallet.connected_features.trending_algorithm;
if (trendingAlgorithm) {
analytics.trendingAlgorithmEvent(trendingAlgorithm);
}
},
undefined,
2020-06-04 19:43:36 +02:00
DOMAIN
)
2020-03-12 17:36:45 +01:00
);
dispatch({ type: ACTIONS.DAEMON_READY });
2019-03-29 15:23:32 +01:00
2019-02-22 06:01:59 +01:00
// @if TARGET='app'
dispatch(doBalanceSubscribe());
2019-11-18 19:30:15 +01:00
dispatch(doSetAutoLaunch());
dispatch(doFindFFmpeg());
dispatch(doGetDaemonStatus());
2019-09-26 18:07:11 +02:00
dispatch(doFetchDaemonSettings());
dispatch(doFetchFileInfos());
2017-11-16 22:39:10 +01:00
if (!selectIsUpgradeSkipped(state)) {
dispatch(doCheckUpgradeAvailable());
}
dispatch(doCheckUpgradeSubscribe());
2019-02-22 06:01:59 +01:00
// @endif
2017-06-08 02:56:52 +02:00
};
2017-04-22 15:17:01 +02:00
}
2017-05-23 09:21:21 +02:00
export function doClearCache() {
2019-10-04 19:29:57 +02:00
return dispatch => {
2019-07-23 10:05:51 +02:00
// Need to update this to work with new version of redux-persist
// Leaving for now
// const reducersToClear = whiteListedReducers.filter(reducerKey => reducerKey !== 'tags');
// window.cacheStore.purge(reducersToClear);
2020-03-27 17:49:41 +01:00
window.sessionStorage.clear();
dispatch(doClearSupport());
2020-03-27 17:49:41 +01:00
window.location.reload();
2019-10-04 19:29:57 +02:00
return dispatch(doClearPublish());
};
}
export function doQuit() {
return () => {
2019-02-22 06:01:59 +01:00
// @if TARGET='app'
remote.app.quit();
2019-02-22 06:01:59 +01:00
// @endif
};
}
export function doQuitAnyDaemon() {
return dispatch => {
2019-02-22 06:01:59 +01:00
// @if TARGET='app'
2019-02-18 18:33:02 +01:00
Lbry.stop()
.catch(() => {
try {
if (process.platform === 'win32') {
execSync('taskkill /im lbrynet.exe /t /f');
} else {
execSync('pkill lbrynet');
}
} catch (error) {
dispatch(doAlertError(`Quitting daemon failed due to: ${error.message}`));
}
2019-02-22 06:01:59 +01:00
})
.finally(() => {
dispatch(doQuit());
2019-02-18 18:33:02 +01:00
});
2019-02-22 06:01:59 +01:00
// @endif
};
}
export function doChangeVolume(volume) {
return dispatch => {
dispatch({
type: ACTIONS.VOLUME_CHANGED,
data: {
volume,
},
});
};
}
2017-12-23 03:09:06 +01:00
export function doChangeMute(muted) {
return dispatch => {
dispatch({
type: ACTIONS.VOLUME_MUTED,
data: {
muted,
},
});
};
}
2018-11-07 23:44:38 +01:00
export function doClickCommentButton() {
return {
type: ACTIONS.ADD_COMMENT,
};
}
export function doToggleSearchExpanded() {
return {
type: ACTIONS.TOGGLE_SEARCH_EXPANDED,
};
}
2019-08-14 05:04:08 +02:00
export function doAnalyticsView(uri, timeToStart) {
return (dispatch, getState) => {
const state = getState();
const { txid, nout, claim_id: claimId } = makeSelectClaimForUri(uri)(state);
const claimIsMine = makeSelectClaimIsMine(uri)(state);
const outpoint = `${txid}:${nout}`;
if (claimIsMine) {
2019-09-21 18:45:52 +02:00
return Promise.resolve();
2019-08-14 05:04:08 +02:00
}
return analytics.apiLogView(uri, outpoint, claimId, timeToStart);
2019-08-14 05:04:08 +02:00
};
}
2019-09-26 18:07:11 +02:00
2020-08-07 22:59:20 +02:00
export function doAnalyticsBuffer(uri, bufferData) {
return (dispatch, getState) => {
const state = getState();
const claim = makeSelectClaimForUri(uri)(state);
const user = selectUser(state);
const {
value: { video, audio, source },
} = claim;
2020-08-27 20:18:45 +02:00
const timeAtBuffer = parseInt(bufferData.currentTime * 1000);
const bufferDuration = parseInt(bufferData.secondsToLoad * 1000);
2020-08-07 22:59:20 +02:00
const fileDurationInSeconds = (video && video.duration) || (audio && audio.duration);
const fileSize = source.size; // size in bytes
const fileSizeInBits = fileSize * 8;
const bitRate = parseInt(fileSizeInBits / fileDurationInSeconds);
const userIdHash = Base64.stringify(sha256(user.id));
analytics.videoBufferEvent(claim, {
timeAtBuffer,
bufferDuration,
bitRate,
userIdHash,
duration: fileDurationInSeconds,
playerPoweredBy: bufferData.playerPoweredBy,
2020-08-07 22:59:20 +02:00
});
};
}
export function doAnalyticsTagSync() {
return (dispatch, getState) => {
const state = getState();
const tags = selectFollowedTagsList(state);
const stringOfTags = tags.join(',');
if (stringOfTags) {
analytics.apiSyncTags({ content_tags: stringOfTags });
}
};
}
2020-05-21 17:38:28 +02:00
export function doAnaltyicsPurchaseEvent(fileInfo) {
return dispatch => {
let purchasePrice = fileInfo.purchase_receipt && fileInfo.purchase_receipt.amount;
if (purchasePrice) {
const purchaseInt = Number(Number(purchasePrice).toFixed(0));
analytics.purchaseEvent(purchaseInt);
}
};
}
2019-09-26 18:07:11 +02:00
export function doSignIn() {
return (dispatch, getState) => {
const state = getState();
const user = selectUser(state);
2020-07-23 16:22:57 +02:00
const notificationsEnabled = user.experimental_ui;
2020-07-23 16:22:57 +02:00
if (notificationsEnabled) {
dispatch(doSocketConnect());
dispatch(doNotificationList());
}
2019-09-26 18:07:11 +02:00
// @if TARGET='web'
dispatch(doBalanceSubscribe());
dispatch(doFetchChannelListMine());
2019-10-01 06:53:33 +02:00
// @endif
2019-09-26 18:07:11 +02:00
};
}
export function doSignOut() {
return dispatch => {
doSignOutCleanup()
2019-09-26 18:07:11 +02:00
.then(() => {
2019-10-03 23:40:54 +02:00
// @if TARGET='web'
window.persistor.purge();
// @endif
})
.then(() => {
setTimeout(() => {
location.reload();
});
2019-09-26 18:07:11 +02:00
})
.catch(() => location.reload());
};
}
2019-10-15 23:23:51 +02:00
export function doSetWelcomeVersion(version) {
return {
type: ACTIONS.SET_WELCOME_VERSION,
data: version,
};
}
export function doSetHasNavigated() {
return {
type: ACTIONS.SET_HAS_NAVIGATED,
data: true,
};
}
export function doToggle3PAnalytics(allowParam, doNotDispatch) {
return (dispatch, getState) => {
const state = getState();
const allowState = selectAllowAnalytics(state);
const allow = allowParam !== undefined && allowParam !== null ? allowParam : allowState;
analytics.toggleThirdParty(allow);
if (!doNotDispatch) {
return dispatch({
type: ACTIONS.SET_ALLOW_ANALYTICS,
data: allow,
});
}
};
}
export function doGetAndPopulatePreferences() {
const { SDK_SYNC_KEYS } = SHARED_PREFERENCES;
return (dispatch, getState) => {
const state = getState();
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
const hasVerifiedEmail = state.user && state.user.user && state.user.user.has_verified_email;
2020-08-07 22:59:20 +02:00
let preferenceKey;
// @if TARGET='app'
preferenceKey = syncEnabled && hasVerifiedEmail ? 'shared' : 'local';
// @endif
// @if TARGET='web'
2020-08-07 22:59:20 +02:00
preferenceKey = 'shared';
// @endif
function successCb(savedPreferences) {
const successState = getState();
const daemonSettings = selectDaemonSettings(successState);
if (savedPreferences !== null) {
dispatch(doPopulateSharedUserState(savedPreferences));
// @if TARGET='app'
const { settings } = savedPreferences.value;
2020-08-28 17:25:47 +02:00
if (settings) {
Object.entries(settings).forEach(([key, val]) => {
if (SDK_SYNC_KEYS.includes(key)) {
if (shouldSetSetting(key, val, daemonSettings[key])) {
if (key === DAEMON_SETTINGS.LBRYUM_SERVERS) {
const servers = stringifyServerParam(val);
dispatch(doSetDaemonSetting(key, servers, true));
} else {
dispatch(doSetDaemonSetting(key, val, true));
}
}
}
2020-08-28 17:25:47 +02:00
});
}
// @endif
} else {
dispatch(doSetPrefsReady());
2019-10-15 23:23:51 +02:00
}
2020-09-04 17:02:30 +02:00
return true;
}
2019-10-15 23:23:51 +02:00
function failCb() {
dispatch(
doToast({
isError: true,
message: __('Unable to load your saved preferences.'),
})
);
dispatch({
type: ACTIONS.SYNC_FATAL_ERROR,
});
2020-09-04 17:02:30 +02:00
return false;
}
2020-01-02 17:30:27 +01:00
return dispatch(doPreferenceGet(preferenceKey, successCb, failCb));
};
}
2019-10-15 23:23:51 +02:00
export function doHandleSyncComplete(error, hasNewData) {
return dispatch => {
if (!error) {
dispatch(doGetAndPopulatePreferences());
if (hasNewData) {
// we just got sync data, better update our channels
dispatch(doFetchChannelListMine());
}
2019-10-15 23:23:51 +02:00
}
};
}
2019-10-15 23:23:51 +02:00
export function doSyncWithPreferences() {
2020-09-04 17:02:30 +02:00
return dispatch => dispatch(doSyncSubscribe());
2019-10-15 23:23:51 +02:00
}
export function doToggleInterestedInYoutubeSync() {
return {
type: ACTIONS.TOGGLE_YOUTUBE_SYNC_INTEREST,
};
}
2020-11-09 18:22:38 +01:00
export function doToggleSplashAnimation() {
return {
type: ACTIONS.TOGGLE_SPLASH_ANIMATION,
};
}