Merge pull request #3021 from lbryio/sdk-sync
preference syncing with SDK
This commit is contained in:
commit
e83ccc77e2
17 changed files with 147 additions and 191 deletions
|
@ -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",
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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} />
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 => ({
|
||||||
|
|
|
@ -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 />
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
|
@ -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"
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue