Merge pull request #3021 from lbryio/sdk-sync

preference syncing with SDK
This commit is contained in:
Sean Yesmunt 2019-10-15 13:27:32 -04:00 committed by GitHub
commit e83ccc77e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 147 additions and 191 deletions

View file

@ -130,8 +130,8 @@
"husky": "^0.14.3", "husky": "^0.14.3",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#0a1c95a08835a6b892d853b156d4934e469c9589", "lbry-redux": "lbryio/lbry-redux#6edcf747e10919605b05b905214fe1d3286898e3",
"lbryinc": "lbryio/lbryinc#d1dba98bb6f1dc67bc0db4c0a20fc13b8a0de98b", "lbryinc": "lbryio/lbryinc#b8e1708ee4491db342c81576265e1b58f542bedb",
"lint-staged": "^7.0.2", "lint-staged": "^7.0.2",
"localforage": "^1.7.1", "localforage": "^1.7.1",
"lodash-es": "^4.17.14", "lodash-es": "^4.17.14",

View file

@ -21,6 +21,7 @@ export default appState => {
minWidth: 950, minWidth: 950,
minHeight: 600, minHeight: 600,
autoHideMenuBar: true, autoHideMenuBar: true,
titleBarStyle: 'hiddenInset',
show: false, show: false,
// Create the window using the state information. // Create the window using the state information.
x: windowState.x, x: windowState.x,

View file

@ -1,6 +1,7 @@
// @flow // @flow
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import classnames from 'classnames';
import analytics from 'analytics'; import analytics from 'analytics';
import { buildURI, parseURI } from 'lbry-redux'; import { buildURI, parseURI } from 'lbry-redux';
import Router from 'component/router/index'; import Router from 'component/router/index';
@ -15,6 +16,9 @@ import usePrevious from 'effects/use-previous';
import Button from 'component/button'; import Button from 'component/button';
export const MAIN_WRAPPER_CLASS = 'main-wrapper'; export const MAIN_WRAPPER_CLASS = 'main-wrapper';
// @if TARGET='app'
export const IS_MAC = process.platform === 'darwin';
// @endif
type Props = { type Props = {
alertError: (string | {}) => void, alertError: (string | {}) => void,
@ -54,6 +58,7 @@ function App(props: Props) {
} = props; } = props;
const appRef = useRef(); const appRef = useRef();
const isEnhancedLayout = useKonamiListener(); const isEnhancedLayout = useKonamiListener();
const [hasSignedIn, setHasSignedIn] = useState(false);
const userId = user && user.id; const userId = user && user.id;
const hasVerifiedEmail = user && user.has_verified_email; const hasVerifiedEmail = user && user.has_verified_email;
const isRewardApproved = user && user.is_reward_approved; const isRewardApproved = user && user.is_reward_approved;
@ -110,17 +115,27 @@ function App(props: Props) {
useEffect(() => { useEffect(() => {
// Wait for balance to be populated on desktop so we know when we can begin syncing // Wait for balance to be populated on desktop so we know when we can begin syncing
// @syncwithbalancefixme // @syncwithbalancefixme
if (hasVerifiedEmail && (IS_WEB || balance !== undefined)) { if (!hasSignedIn && hasVerifiedEmail && (IS_WEB || balance !== undefined)) {
signIn(); signIn();
setHasSignedIn(true);
} }
}, [hasVerifiedEmail, signIn, balance]); }, [hasVerifiedEmail, signIn, balance, hasSignedIn]);
if (!user) { if (!user) {
return null; return null;
} }
return ( return (
<div className={MAIN_WRAPPER_CLASS} ref={appRef} onContextMenu={e => openContextMenu(e)}> <div
className={classnames(MAIN_WRAPPER_CLASS, {
// @if TARGET='app'
[`${MAIN_WRAPPER_CLASS}--mac`]: IS_MAC,
// @endif
})}
ref={appRef}
onContextMenu={e => openContextMenu(e)}
>
<Router /> <Router />
<ModalRouter /> <ModalRouter />
<FileViewer pageUri={uri} /> <FileViewer pageUri={uri} />

View file

@ -11,6 +11,9 @@ import WunderBar from 'component/wunderbar';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button'; import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
import Tooltip from 'component/common/tooltip'; import Tooltip from 'component/common/tooltip';
// @if TARGET='app'
import { IS_MAC } from 'component/app/view';
// @endif
type Props = { type Props = {
balance: string, balance: string,
@ -62,7 +65,14 @@ const Header = (props: Props) => {
} }
return ( return (
<header className={classnames('header', { 'header--minimal': minimal })}> <header
className={classnames('header', {
'header--minimal': minimal,
// @if TARGET='app'
'header--mac': IS_MAC,
// @endif
})}
>
<div className="header__contents"> <div className="header__contents">
<div className="header__navigation"> <div className="header__navigation">
<Button <Button

View file

@ -2,8 +2,8 @@ import * as SETTINGS from 'constants/settings';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectBalance } from 'lbry-redux'; import { selectBalance } from 'lbry-redux';
import { selectEmailNewIsPending, selectEmailNewErrorMessage, doUserEmailNew } from 'lbryinc'; import { selectEmailNewIsPending, selectEmailNewErrorMessage, doUserEmailNew } from 'lbryinc';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { doSetClientSetting } from 'redux/actions/settings'; import { doSetClientSetting } from 'redux/actions/settings';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import UserEmailNew from './view'; import UserEmailNew from './view';
const select = state => ({ const select = state => ({

View file

@ -119,8 +119,8 @@ function UserSignIn(props: Props) {
<YoutubeTransferStatus /> <Confetti recycle={false} style={{ position: 'fixed' }} /> <YoutubeTransferStatus /> <Confetti recycle={false} style={{ position: 'fixed' }} />
</div> </div>
), ),
showSyncPassword && <SyncPassword />,
// @endif // @endif
showSyncPassword && <SyncPassword />,
showLoadingSpinner && ( showLoadingSpinner && (
<div className="main--empty"> <div className="main--empty">
<Spinner /> <Spinner />

View file

@ -54,7 +54,7 @@ if (process.env.SEARCH_API_URL) {
} }
// @if TARGET='web' // @if TARGET='web'
const SDK_API_URL = process.env.SDK_API_URL || 'https://api.lbry.tv/api/proxy'; const SDK_API_URL = process.env.SDK_API_URL || 'https://api.lbry.tv/api/v1/proxy';
Lbry.setDaemonConnectionString(SDK_API_URL); Lbry.setDaemonConnectionString(SDK_API_URL);
// @endif // @endif

View file

@ -52,7 +52,7 @@ type Props = {
themes: Array<string>, themes: Array<string>,
automaticDarkModeEnabled: boolean, automaticDarkModeEnabled: boolean,
autoplay: boolean, autoplay: boolean,
autoDownload: boolean, // autoDownload: boolean,
encryptWallet: () => void, encryptWallet: () => void,
decryptWallet: () => void, decryptWallet: () => void,
updateWalletStatus: () => void, updateWalletStatus: () => void,
@ -198,7 +198,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
autoplay, autoplay,
walletEncrypted, walletEncrypted,
osNotificationsEnabled, osNotificationsEnabled,
autoDownload, // autoDownload,
setDaemonSetting, setDaemonSetting,
setClientSetting, setClientSetting,
supportOption, supportOption,
@ -588,6 +588,8 @@ class SettingsPage extends React.PureComponent<Props, State> {
/> />
{/* @if TARGET='app' */} {/* @if TARGET='app' */}
{/*
Disabling below until we get downloads to work with shared subscriptions code
<FormField <FormField
type="checkbox" type="checkbox"
name="auto_download" name="auto_download"
@ -597,7 +599,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
helper={__( helper={__(
"The latest file from each of your subscriptions will be downloaded for quick access as soon as it's published." "The latest file from each of your subscriptions will be downloaded for quick access as soon as it's published."
)} )}
/> /> */}
<fieldset-section> <fieldset-section>
<FormField <FormField
name="max_connections" name="max_connections"

View file

@ -19,10 +19,11 @@ import {
doFetchChannelListMine, doFetchChannelListMine,
selectBalance, selectBalance,
doClearPublish, doClearPublish,
doPreferenceGet,
doToast,
} from 'lbry-redux'; } from 'lbry-redux';
import Native from 'native'; import Native from 'native';
import { doFetchDaemonSettings } from 'redux/actions/settings'; import { doFetchDaemonSettings } from 'redux/actions/settings';
import { doCheckSubscriptionsInit } from 'redux/actions/subscriptions';
import { import {
selectIsUpgradeSkipped, selectIsUpgradeSkipped,
selectUpdateUrl, selectUpdateUrl,
@ -34,7 +35,7 @@ import {
selectUpgradeTimer, selectUpgradeTimer,
selectModal, selectModal,
} from 'redux/selectors/app'; } from 'redux/selectors/app';
import { Lbryio, doAuthenticate, doGetSync, selectSyncHash, doResetSync } from 'lbryinc'; import { doAuthenticate, doGetSync, selectSyncHash, doResetSync } from 'lbryinc';
import { lbrySettings as config, version as appVersion } from 'package.json'; import { lbrySettings as config, version as appVersion } from 'package.json';
import { push } from 'connected-react-router'; import { push } from 'connected-react-router';
import analytics from 'analytics'; import analytics from 'analytics';
@ -331,7 +332,6 @@ export function doDaemonReady() {
dispatch(doCheckUpgradeAvailable()); dispatch(doCheckUpgradeAvailable());
} }
dispatch(doCheckUpgradeSubscribe()); dispatch(doCheckUpgradeSubscribe());
dispatch(doCheckSubscriptionsInit());
// @endif // @endif
}; };
} }
@ -439,37 +439,53 @@ export function doAnalyticsView(uri, timeToStart) {
export function doSignIn() { export function doSignIn() {
return (dispatch, getState) => { return (dispatch, getState) => {
function handlePreferencesAfterSync() {
function successCb(savedPreferences) {
dispatch(doPopulateSharedUserState(savedPreferences));
}
function failCb() {
dispatch(
doToast({
isError: true,
message: __('Unable to load your saved preferences.'),
})
);
}
doPreferenceGet('shared', successCb, failCb);
}
// The balance is subscribed to on launch for desktop // The balance is subscribed to on launch for desktop
// @if TARGET='web' // @if TARGET='web'
const { auth_token: authToken } = cookie.parse(document.cookie); const { auth_token: authToken } = cookie.parse(document.cookie);
Lbry.setApiHeader('X-Lbry-Auth-Token', authToken); Lbry.setApiHeader('X-Lbry-Auth-Token', authToken);
dispatch(doBalanceSubscribe()); dispatch(doBalanceSubscribe());
dispatch(doFetchChannelListMine()); dispatch(doFetchChannelListMine());
dispatch(doCheckSubscriptionsInit());
// @endif // @endif
// @if TARGET='app'
const state = getState(); const state = getState();
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state); const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
const hasSyncedBefore = selectSyncHash(state); const hasSyncedBefore = selectSyncHash(state);
const balance = selectBalance(state); const balance = selectBalance(state);
// For existing users, check if they've synced before, or have 0 balance // For existing users, check if they've synced before, or have 0 balance
if (syncEnabled && (hasSyncedBefore || balance === 0)) { // Always sync for web
const canSync = syncEnabled && (hasSyncedBefore || balance === 0 || IS_WEB);
if (canSync) {
getSavedPassword().then(password => { getSavedPassword().then(password => {
const passwordArgument = password === null ? '' : password; const passwordArgument = password === null ? '' : password;
dispatch(doGetSync(passwordArgument, !hasSyncedBefore));
// Only set the default account if they have never synced before
dispatch(doGetSync(passwordArgument, handlePreferencesAfterSync));
setInterval(() => { setInterval(() => {
dispatch(doGetSync(passwordArgument)); dispatch(doGetSync(passwordArgument, handlePreferencesAfterSync));
}, 1000 * 60 * 5); }, 1000 * 60 * 5);
}); });
} }
// @endif
Lbryio.call('user_settings', 'get').then(settings => {
dispatch(doPopulateSharedUserState(settings));
});
}; };
} }

View file

@ -1,16 +1,8 @@
// @flow // @flow
import { PAGE_SIZE } from 'constants/claim';
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import * as SETTINGS from 'constants/settings';
import * as NOTIFICATION_TYPES from 'constants/subscriptions';
import { Lbryio, rewards, doClaimRewardType } from 'lbryinc'; import { Lbryio, rewards, doClaimRewardType } from 'lbryinc';
import { selectSubscriptions, selectUnreadByChannel } from 'redux/selectors/subscriptions'; import { selectUnreadByChannel } from 'redux/selectors/subscriptions';
import { makeSelectClientSetting } from 'redux/selectors/settings'; import { parseURI, doResolveUris } from 'lbry-redux';
import { Lbry, parseURI, doResolveUris } from 'lbry-redux';
import { doPlayUri } from 'redux/actions/content';
const CHECK_SUBSCRIPTIONS_INTERVAL = 15 * 60 * 1000;
const SUBSCRIPTION_DOWNLOAD_LIMIT = 1;
export const doSetViewMode = (viewMode: ViewMode) => (dispatch: Dispatch) => export const doSetViewMode = (viewMode: ViewMode) => (dispatch: Dispatch) =>
dispatch({ dispatch({
@ -73,7 +65,6 @@ export const doFetchMySubscriptions = () => (dispatch: Dispatch, getState: GetSt
}); });
dispatch(doResolveUris(subscriptions.map(({ uri }) => uri))); dispatch(doResolveUris(subscriptions.map(({ uri }) => uri)));
dispatch(doCheckSubscriptions());
}) })
.catch(() => { .catch(() => {
dispatch({ dispatch({
@ -190,106 +181,6 @@ export const doRemoveUnreadSubscription = (channelUri: string, readUri: string)
dispatch(doRemoveUnreadSubscriptions(channelUri, [readUri])); dispatch(doRemoveUnreadSubscriptions(channelUri, [readUri]));
}; };
export const doCheckSubscription = (subscriptionUri: string, shouldNotify?: boolean) => (
dispatch: Dispatch,
getState: GetState
) => {
// no dispatching FETCH_CHANNEL_CLAIMS_STARTED; causes loading issues on <SubscriptionsPage>
const state = getState();
const shouldAutoDownload = makeSelectClientSetting(SETTINGS.AUTO_DOWNLOAD)(state);
const savedSubscription = state.subscriptions.subscriptions.find(sub => sub.uri === subscriptionUri);
if (!savedSubscription) {
throw Error(`Trying to find new content for ${subscriptionUri} but it doesn't exist in your subscriptions`);
}
dispatch({
type: ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED,
data: {
uri: subscriptionUri,
page: 1,
},
});
// We may be duplicating calls here. Can this logic be baked into doFetchClaimsByChannel?
Lbry.claim_search({
channel: subscriptionUri,
valid_channel_signature: true,
order_by: ['release_time'],
page: 1,
page_size: PAGE_SIZE,
}).then(claimListByChannel => {
const { items: claimsInChannel } = claimListByChannel;
// may happen if subscribed to an abandoned channel or an empty channel
if (!claimsInChannel || !claimsInChannel.length) {
return;
}
dispatch({
type: ACTIONS.FETCH_CHANNEL_CLAIMS_COMPLETED,
data: {
uri: subscriptionUri,
claims: claimsInChannel || [],
page: 1,
},
});
// Determine if the latest subscription currently saved is actually the latest subscription
const latestIndex = claimsInChannel.findIndex(claim => claim.permanent_url === savedSubscription.latest);
// If latest is -1, it is a newly subscribed channel or there have been 10+ claims published since last viewed
const latestIndexToNotify = latestIndex === -1 ? 10 : latestIndex;
// If latest is 0, nothing has changed
// Do not download/notify about new content, it would download/notify 10 claims per channel
if (latestIndex !== 0 && savedSubscription.latest) {
let downloadCount = 0;
const newUnread = [];
claimsInChannel.slice(0, latestIndexToNotify).forEach(claim => {
const uri = claim.canonical_url;
const shouldDownload =
shouldAutoDownload && Boolean(downloadCount < SUBSCRIPTION_DOWNLOAD_LIMIT && !claim.value.fee);
// Add the new content to the list of "un-read" subscriptions
if (shouldNotify) {
newUnread.push(uri);
}
if (shouldDownload) {
downloadCount += 1;
// this fails since something is not resolved/saved somewhere...
dispatch(doPlayUri(uri, true, true));
}
});
dispatch(
doUpdateUnreadSubscriptions(
subscriptionUri,
newUnread,
downloadCount > 0 ? NOTIFICATION_TYPES.DOWNLOADING : NOTIFICATION_TYPES.NOTIFY_ONLY
)
);
}
// Set the latest piece of content for a channel
// This allows the app to know if there has been new content since it was last set
const latest = claimsInChannel[0];
dispatch(
setSubscriptionLatest(
{
channelName: latest.signing_channel.name,
uri: latest.signing_channel.permanent_url,
},
latest.permanent_url
)
);
// calling FETCH_CHANNEL_CLAIMS_COMPLETED after not calling STARTED
// means it will delete a non-existent fetchingChannelClaims[uri]
});
};
export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dispatch, getState: GetState) => { export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dispatch, getState: GetState) => {
const { const {
settings: { daemonSettings }, settings: { daemonSettings },
@ -318,8 +209,6 @@ export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dis
dispatch(doClaimRewardType(rewards.TYPE_SUBSCRIPTION, { failSilently: true })); dispatch(doClaimRewardType(rewards.TYPE_SUBSCRIPTION, { failSilently: true }));
} }
dispatch(doCheckSubscription(subscription.uri, true));
}; };
export const doChannelUnsubscribe = (subscription: Subscription) => (dispatch: Dispatch, getState: GetState) => { export const doChannelUnsubscribe = (subscription: Subscription) => (dispatch: Dispatch, getState: GetState) => {
@ -342,27 +231,6 @@ export const doChannelUnsubscribe = (subscription: Subscription) => (dispatch: D
} }
}; };
export const doCheckSubscriptions = () => (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const subscriptions = selectSubscriptions(state);
subscriptions.forEach((sub: Subscription) => {
dispatch(doCheckSubscription(sub.uri, true));
});
};
export const doCheckSubscriptionsInit = () => (dispatch: Dispatch) => {
// doCheckSubscriptionsInit is called by doDaemonReady
// setTimeout below is a hack to ensure redux is hydrated when subscriptions are checked
// this will be replaced with <PersistGate> which requires a package upgrade
setTimeout(() => dispatch(doFetchMySubscriptions()), 5000);
const checkSubscriptionsTimer = setInterval(() => dispatch(doCheckSubscriptions()), CHECK_SUBSCRIPTIONS_INTERVAL);
dispatch({
type: ACTIONS.CHECK_SUBSCRIPTIONS_SUBSCRIBE,
data: { checkSubscriptionsTimer },
});
};
export const doFetchRecommendedSubscriptions = () => (dispatch: Dispatch) => { export const doFetchRecommendedSubscriptions = () => (dispatch: Dispatch) => {
dispatch({ dispatch({
type: ACTIONS.GET_SUGGESTED_SUBSCRIPTIONS_START, type: ACTIONS.GET_SUGGESTED_SUBSCRIPTIONS_START,

View file

@ -7,6 +7,8 @@
border-bottom: 1px solid $lbry-gray-1; border-bottom: 1px solid $lbry-gray-1;
box-shadow: var(--card-box-shadow) $lbry-gray-1; box-shadow: var(--card-box-shadow) $lbry-gray-1;
font-size: var(--font-body); font-size: var(--font-body);
-webkit-user-select: none;
-webkit-app-region: drag;
[data-mode='dark'] & { [data-mode='dark'] & {
background-color: var(--dm-color-05); background-color: var(--dm-color-05);
@ -26,6 +28,10 @@
border-bottom: none; border-bottom: none;
} }
.header--mac {
padding-top: var(--mac-titlebar-height);
}
.header__contents { .header__contents {
max-width: var(--page-max-width); max-width: var(--page-max-width);
height: calc(var(--header-height) - 1px); height: calc(var(--header-height) - 1px);

View file

@ -6,6 +6,10 @@
} }
} }
.main-wrapper--mac {
margin-top: calc(var(--header-height) + var(--mac-titlebar-height));
}
.main-wrapper__inner { .main-wrapper__inner {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;

View file

@ -8,6 +8,7 @@ $large-breakpoint: 1921px;
:root { :root {
// Width & spacing // Width & spacing
--page-max-width: 1420px; --page-max-width: 1420px;
--mac-titlebar-height: 1.5rem;
--mobile: 600px; --mobile: 600px;
--side-nav-width: 170px; --side-nav-width: 170px;
--spacing-miniscule: calc(2rem / 5); --spacing-miniscule: calc(2rem / 5);

View file

@ -1,3 +1,5 @@
import * as ACTIONS from 'constants/action_types';
import * as SETTINGS from 'constants/settings';
import { persistStore, persistReducer } from 'redux-persist'; import { persistStore, persistReducer } from 'redux-persist';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2'; import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import createCompressor from 'redux-persist-transform-compress'; import createCompressor from 'redux-persist-transform-compress';
@ -8,8 +10,10 @@ import thunk from 'redux-thunk';
import { createHashHistory, createBrowserHistory } from 'history'; import { createHashHistory, createBrowserHistory } from 'history';
import { routerMiddleware } from 'connected-react-router'; import { routerMiddleware } from 'connected-react-router';
import createRootReducer from './reducers'; import createRootReducer from './reducers';
import { Lbryio } from 'lbryinc'; import { buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux';
import isEqual from 'util/deep-equal'; import { doGetSync, selectUserVerifiedEmail } from 'lbryinc';
import { getSavedPassword } from 'util/saved-passwords';
import { makeSelectClientSetting } from 'redux/selectors/settings';
function isFunction(object) { function isFunction(object) {
return typeof object === 'function'; return typeof object === 'function';
@ -50,6 +54,7 @@ const appFilter = createFilter('app', ['hasClickedComment', 'searchOptionsExpand
const walletFilter = createFilter('wallet', ['receiveAddress']); const walletFilter = createFilter('wallet', ['receiveAddress']);
const searchFilter = createFilter('search', ['options']); const searchFilter = createFilter('search', ['options']);
const tagsFilter = createFilter('tags', ['followedTags']); const tagsFilter = createFilter('tags', ['followedTags']);
const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']);
const blockedFilter = createFilter('blocked', ['blockedChannels']); const blockedFilter = createFilter('blocked', ['blockedChannels']);
const whiteListedReducers = [ const whiteListedReducers = [
// @if TARGET='app' // @if TARGET='app'
@ -64,6 +69,7 @@ const whiteListedReducers = [
'blocked', 'blocked',
'settings', 'settings',
'sync', 'sync',
'subscriptions',
]; ];
const transforms = [ const transforms = [
@ -77,6 +83,7 @@ const transforms = [
searchFilter, searchFilter,
tagsFilter, tagsFilter,
contentFilter, contentFilter,
subscriptionsFilter,
createCompressor(), createCompressor(),
]; ];
@ -98,10 +105,51 @@ history = createHashHistory();
history = createBrowserHistory(); history = createBrowserHistory();
// @endif // @endif
const triggerSharedStateActions = [
ACTIONS.CHANNEL_SUBSCRIBE,
ACTIONS.CHANNEL_UNSUBSCRIBE,
LBRY_REDUX_ACTIONS.TOGGLE_TAG_FOLLOW,
LBRY_REDUX_ACTIONS.TOGGLE_BLOCK_CHANNEL,
LBRY_REDUX_ACTIONS.CREATE_CHANNEL_COMPLETED,
];
/**
* source: the reducer name
* property: the property in the reducer-specific state
* transform: optional method to modify the value to be stored
*
* See https://github.com/lbryio/lbry-redux/blob/master/src/redux/middleware/shared-state.js for the source
* This is based off v0.1
* If lbry-redux changes to another version, this code will need to be changed when upgrading
*/
const sharedStateFilters = {
tags: { source: 'tags', property: 'followedTags' },
subscriptions: {
source: 'subscriptions',
property: 'subscriptions',
transform: function(value) {
return value.map(({ uri }) => uri);
},
},
blocked: { source: 'blocked', property: 'blockedChannels' },
};
const sharedStateCb = ({ dispatch, getState }) => {
const state = getState();
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
const emailVerified = selectUserVerifiedEmail(state);
if (syncEnabled && emailVerified) {
getSavedPassword().then(savedPassword => {
dispatch(doGetSync(savedPassword));
});
}
};
const sharedStateMiddleware = buildSharedStateMiddleware(triggerSharedStateActions, sharedStateFilters, sharedStateCb);
const rootReducer = createRootReducer(history); const rootReducer = createRootReducer(history);
const persistedReducer = persistReducer(persistOptions, rootReducer); const persistedReducer = persistReducer(persistOptions, rootReducer);
const bulkThunk = createBulkThunkMiddleware(); const bulkThunk = createBulkThunkMiddleware();
const middleware = [routerMiddleware(history), thunk, bulkThunk]; const middleware = [sharedStateMiddleware, routerMiddleware(history), thunk, bulkThunk];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore( const store = createStore(
enableBatching(persistedReducer), enableBatching(persistedReducer),
@ -109,29 +157,6 @@ const store = createStore(
composeEnhancers(applyMiddleware(...middleware)) composeEnhancers(applyMiddleware(...middleware))
); );
let currentPayload;
store.subscribe(() => {
const state = store.getState();
const subscriptions = state.subscriptions.subscriptions.map(({ uri }) => uri);
const tags = state.tags.followedTags;
const authToken = state.user.accessToken;
const newPayload = {
version: '0.1',
shared: {
subscriptions,
tags,
},
};
if (!isEqual(newPayload, currentPayload)) {
currentPayload = newPayload;
if (authToken) {
Lbryio.call('user_settings', 'set', { settings: newPayload });
}
}
});
const persistor = persistStore(store); const persistor = persistStore(store);
window.persistor = persistor; window.persistor = persistor;

View file

@ -11,10 +11,17 @@ export const setSavedPassword = value => {
export const getSavedPassword = () => { export const getSavedPassword = () => {
return new Promise(resolve => { return new Promise(resolve => {
// @if TARGET='app'
ipcRenderer.once('get-password-response', (event, password) => { ipcRenderer.once('get-password-response', (event, password) => {
resolve(password); resolve(password);
}); });
ipcRenderer.send('get-password'); ipcRenderer.send('get-password');
// @endif
// @if TARGET='web'
// Will handle saved passwords on web differently
resolve('');
// @endif
}); });
}; };

View file

@ -803,5 +803,6 @@
"Enter a LBRY URL or search for videos, music, games and more": "Enter a LBRY URL or search for videos, music, games and more", "Enter a LBRY URL or search for videos, music, games and more": "Enter a LBRY URL or search for videos, music, games and more",
"This app will automatically download new free content from channels you are subscribed to. You may configure this in Settings or on the Subscriptions page.": "This app will automatically download new free content from channels you are subscribed to. You may configure this in Settings or on the Subscriptions page.", "This app will automatically download new free content from channels you are subscribed to. You may configure this in Settings or on the Subscriptions page.": "This app will automatically download new free content from channels you are subscribed to. You may configure this in Settings or on the Subscriptions page.",
"(Only available on the desktop app.)": "(Only available on the desktop app.)", "(Only available on the desktop app.)": "(Only available on the desktop app.)",
"If we have your email address, we will send you notifications related to new content. You may configure these emails from the Help page.": "If we have your email address, we will send you notifications related to new content. You may configure these emails from the Help page." "If we have your email address, we will send you notifications related to new content. You may configure these emails from the Help page.": "If we have your email address, we will send you notifications related to new content. You may configure these emails from the Help page.",
"Light": "Light"
} }

View file

@ -6870,17 +6870,17 @@ lazy-val@^1.0.3, lazy-val@^1.0.4:
yargs "^13.2.2" yargs "^13.2.2"
zstd-codec "^0.1.1" zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#0a1c95a08835a6b892d853b156d4934e469c9589: lbry-redux@lbryio/lbry-redux#6edcf747e10919605b05b905214fe1d3286898e3:
version "0.0.1" version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/0a1c95a08835a6b892d853b156d4934e469c9589" resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/6edcf747e10919605b05b905214fe1d3286898e3"
dependencies: dependencies:
proxy-polyfill "0.1.6" proxy-polyfill "0.1.6"
reselect "^3.0.0" reselect "^3.0.0"
uuid "^3.3.2" uuid "^3.3.2"
lbryinc@lbryio/lbryinc#d1dba98bb6f1dc67bc0db4c0a20fc13b8a0de98b: lbryinc@lbryio/lbryinc#b8e1708ee4491db342c81576265e1b58f542bedb:
version "0.0.1" version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/d1dba98bb6f1dc67bc0db4c0a20fc13b8a0de98b" resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/b8e1708ee4491db342c81576265e1b58f542bedb"
dependencies: dependencies:
reselect "^3.0.0" reselect "^3.0.0"