Compare commits
18 commits
master
...
feat/bette
Author | SHA1 | Date | |
---|---|---|---|
|
69ebf47cb5 | ||
|
1a7fdbb65c | ||
|
aec44841a4 | ||
|
15d3d0a5ca | ||
|
f727b8b7c6 | ||
|
58ec6a739c | ||
|
a69875fa88 | ||
|
bb5c444f63 | ||
|
48f0c5e413 | ||
|
256bf308b4 | ||
|
f8199d763c | ||
|
dd01ad2cb2 | ||
|
84f993200b | ||
|
441f13979d | ||
|
6b705c995a | ||
|
bd0fe4718f | ||
|
f092e8cb7b | ||
|
2e87b2fd22 |
136 changed files with 3347 additions and 1306 deletions
5
flow-typed/homepage.js
vendored
5
flow-typed/homepage.js
vendored
|
@ -4,12 +4,12 @@ declare type HomepageObject = {
|
||||||
options: any,
|
options: any,
|
||||||
route: string,
|
route: string,
|
||||||
title: string,
|
title: string,
|
||||||
}
|
};
|
||||||
|
|
||||||
declare type HomepageData = {
|
declare type HomepageData = {
|
||||||
[string]: HomepageObject,
|
[string]: HomepageObject,
|
||||||
default: any => any,
|
default: any => any,
|
||||||
}
|
};
|
||||||
|
|
||||||
declare type RowDataItem = {
|
declare type RowDataItem = {
|
||||||
title: any,
|
title: any,
|
||||||
|
@ -19,6 +19,7 @@ declare type RowDataItem = {
|
||||||
extra?: any,
|
extra?: any,
|
||||||
options?: {
|
options?: {
|
||||||
channelIds?: Array<string>,
|
channelIds?: Array<string>,
|
||||||
|
limitClaimsPerChannel?: number,
|
||||||
pageSize: number,
|
pageSize: number,
|
||||||
},
|
},
|
||||||
route?: string,
|
route?: string,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -142,7 +142,7 @@
|
||||||
"imagesloaded": "^4.1.4",
|
"imagesloaded": "^4.1.4",
|
||||||
"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#c494c92505cd531048de20bc37dc4d192b6ace01",
|
"lbry-redux": "lbryio/lbry-redux#9a1701372829c21cefbedacfc6033aec4dc7fa3c",
|
||||||
"lbryinc": "lbryio/lbryinc#7faea40d87b78ec91b901c62f501499dc4737025",
|
"lbryinc": "lbryio/lbryinc#7faea40d87b78ec91b901c62f501499dc4737025",
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
|
|
|
@ -858,7 +858,6 @@
|
||||||
"Tag selection": "Tag selection",
|
"Tag selection": "Tag selection",
|
||||||
"Select some tags to help us show you interesting things.": "Select some tags to help us show you interesting things.",
|
"Select some tags to help us show you interesting things.": "Select some tags to help us show you interesting things.",
|
||||||
"You are currently following %followingCount% tags": "You are currently following %followingCount% tags",
|
"You are currently following %followingCount% tags": "You are currently following %followingCount% tags",
|
||||||
"You are currently following %followingCount% tag": "You are currently following %followingCount% tag",
|
|
||||||
"Back": "Back",
|
"Back": "Back",
|
||||||
"Experimental Transcoding": "Experimental Transcoding",
|
"Experimental Transcoding": "Experimental Transcoding",
|
||||||
"A Folder containing FFmpeg": "A Folder containing FFmpeg",
|
"A Folder containing FFmpeg": "A Folder containing FFmpeg",
|
||||||
|
@ -1417,6 +1416,7 @@
|
||||||
"Never fear though, there are tons of ways to earn %lbc%. You can earn or purchase %lbc%, or you can have your friends send you some.": "Never fear though, there are tons of ways to earn %lbc%. You can earn or purchase %lbc%, or you can have your friends send you some.",
|
"Never fear though, there are tons of ways to earn %lbc%. You can earn or purchase %lbc%, or you can have your friends send you some.": "Never fear though, there are tons of ways to earn %lbc%. You can earn or purchase %lbc%, or you can have your friends send you some.",
|
||||||
"gaming, comedy, educational": "gaming, comedy, educational",
|
"gaming, comedy, educational": "gaming, comedy, educational",
|
||||||
"A channel is required to comment on %SITE_NAME%": "A channel is required to comment on %SITE_NAME%",
|
"A channel is required to comment on %SITE_NAME%": "A channel is required to comment on %SITE_NAME%",
|
||||||
|
"A channel is required to comment on lbry.tv": "A channel is required to comment on lbry.tv",
|
||||||
"A welcome bonus and thank you for your contribution to content freedom.": "A welcome bonus and thank you for your contribution to content freedom.",
|
"A welcome bonus and thank you for your contribution to content freedom.": "A welcome bonus and thank you for your contribution to content freedom.",
|
||||||
"You earned %lbc% new user reward.": "You earned %lbc% LBC new user reward.",
|
"You earned %lbc% new user reward.": "You earned %lbc% LBC new user reward.",
|
||||||
"Just press play on something. Anything at all. Yep, even that one. Everybody's doing it.": "Just press play on something. Anything at all. Yep, even that one. Everybody's doing it.",
|
"Just press play on something. Anything at all. Yep, even that one. Everybody's doing it.": "Just press play on something. Anything at all. Yep, even that one. Everybody's doing it.",
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 2.9 KiB |
BIN
static/img/lbry-favicon.png
Normal file
BIN
static/img/lbry-favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -26,10 +26,10 @@ if (isProduction) {
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
type Analytics = {
|
type Analytics = {
|
||||||
error: string => Promise<any>,
|
error: (string) => Promise<any>,
|
||||||
sentryError: ({} | string, {}) => Promise<any>,
|
sentryError: ({} | string, {}) => Promise<any>,
|
||||||
pageView: (string, ?string) => void,
|
pageView: (string, ?string) => void,
|
||||||
setUser: Object => void,
|
setUser: (Object) => void,
|
||||||
toggleInternal: (boolean, ?boolean) => void,
|
toggleInternal: (boolean, ?boolean) => void,
|
||||||
apiLogView: (string, string, string, ?number, ?() => void) => Promise<any>,
|
apiLogView: (string, string, string, ?number, ?() => void) => Promise<any>,
|
||||||
apiLogPublish: (ChannelClaim | StreamClaim) => void,
|
apiLogPublish: (ChannelClaim | StreamClaim) => void,
|
||||||
|
@ -39,6 +39,9 @@ type Analytics = {
|
||||||
playerStartedEvent: (?boolean) => void,
|
playerStartedEvent: (?boolean) => void,
|
||||||
videoFetchDuration: (string, number) => void,
|
videoFetchDuration: (string, number) => void,
|
||||||
videoStartEvent: (string, number) => void,
|
videoStartEvent: (string, number) => void,
|
||||||
|
adsFetchedEvent: () => void,
|
||||||
|
adsReceivedEvent: (any) => void,
|
||||||
|
adsErrorEvent: (any) => void,
|
||||||
videoBufferEvent: (
|
videoBufferEvent: (
|
||||||
StreamClaim,
|
StreamClaim,
|
||||||
{
|
{
|
||||||
|
@ -55,9 +58,9 @@ type Analytics = {
|
||||||
emailVerifiedEvent: () => void,
|
emailVerifiedEvent: () => void,
|
||||||
rewardEligibleEvent: () => void,
|
rewardEligibleEvent: () => void,
|
||||||
startupEvent: () => void,
|
startupEvent: () => void,
|
||||||
purchaseEvent: number => void,
|
purchaseEvent: (number) => void,
|
||||||
readyEvent: number => void,
|
readyEvent: (number) => void,
|
||||||
openUrlEvent: string => void,
|
openUrlEvent: (string) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
type LogPublishParams = {
|
type LogPublishParams = {
|
||||||
|
@ -75,8 +78,8 @@ if (window.localStorage.getItem(SHARE_INTERNAL) === 'true') internalAnalyticsEna
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
const analytics: Analytics = {
|
const analytics: Analytics = {
|
||||||
error: message => {
|
error: (message) => {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
if (internalAnalyticsEnabled && isProduction) {
|
if (internalAnalyticsEnabled && isProduction) {
|
||||||
return Lbryio.call('event', 'desktop_error', { error_message: message }).then(() => {
|
return Lbryio.call('event', 'desktop_error', { error_message: message }).then(() => {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
|
@ -87,9 +90,9 @@ const analytics: Analytics = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
sentryError: (error, errorInfo) => {
|
sentryError: (error, errorInfo) => {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
if (internalAnalyticsEnabled && isProduction) {
|
if (internalAnalyticsEnabled && isProduction) {
|
||||||
Sentry.withScope(scope => {
|
Sentry.withScope((scope) => {
|
||||||
scope.setExtras(errorInfo);
|
scope.setExtras(errorInfo);
|
||||||
const eventId = Sentry.captureException(error);
|
const eventId = Sentry.captureException(error);
|
||||||
resolve(eventId);
|
resolve(eventId);
|
||||||
|
@ -114,7 +117,7 @@ const analytics: Analytics = {
|
||||||
MatomoInstance.trackPageView(params);
|
MatomoInstance.trackPageView(params);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setUser: userId => {
|
setUser: (userId) => {
|
||||||
if (internalAnalyticsEnabled && userId) {
|
if (internalAnalyticsEnabled && userId) {
|
||||||
window._paq.push(['setUserId', String(userId)]);
|
window._paq.push(['setUserId', String(userId)]);
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
|
@ -188,7 +191,7 @@ const analytics: Analytics = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
apiSyncTags: params => {
|
apiSyncTags: (params) => {
|
||||||
if (internalAnalyticsEnabled && isProduction) {
|
if (internalAnalyticsEnabled && isProduction) {
|
||||||
Lbryio.call('content_tags', 'sync', params);
|
Lbryio.call('content_tags', 'sync', params);
|
||||||
}
|
}
|
||||||
|
@ -238,12 +241,21 @@ const analytics: Analytics = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
playerLoadedEvent: embedded => {
|
playerLoadedEvent: (embedded) => {
|
||||||
sendMatomoEvent('Player', 'Loaded', embedded ? 'embedded' : 'onsite');
|
sendMatomoEvent('Player', 'Loaded', embedded ? 'embedded' : 'onsite');
|
||||||
},
|
},
|
||||||
playerStartedEvent: embedded => {
|
playerStartedEvent: (embedded) => {
|
||||||
sendMatomoEvent('Player', 'Started', embedded ? 'embedded' : 'onsite');
|
sendMatomoEvent('Player', 'Started', embedded ? 'embedded' : 'onsite');
|
||||||
},
|
},
|
||||||
|
adsFetchedEvent: () => {
|
||||||
|
sendMatomoEvent('Media', 'AdsFetched');
|
||||||
|
},
|
||||||
|
adsReceivedEvent: (response) => {
|
||||||
|
sendMatomoEvent('Media', 'AdsReceived', JSON.stringify(response));
|
||||||
|
},
|
||||||
|
adsErrorEvent: (response) => {
|
||||||
|
sendMatomoEvent('Media', 'AdsError', JSON.stringify(response));
|
||||||
|
},
|
||||||
tagFollowEvent: (tag, following) => {
|
tagFollowEvent: (tag, following) => {
|
||||||
sendMatomoEvent('Tag', following ? 'Tag-Follow' : 'Tag-Unfollow', tag);
|
sendMatomoEvent('Tag', following ? 'Tag-Follow' : 'Tag-Unfollow', tag);
|
||||||
},
|
},
|
||||||
|
@ -316,7 +328,7 @@ analytics.pageView(
|
||||||
|
|
||||||
// Listen for url changes and report
|
// Listen for url changes and report
|
||||||
// This will include search queries
|
// This will include search queries
|
||||||
history.listen(location => {
|
history.listen((location) => {
|
||||||
const { pathname, search } = location;
|
const { pathname, search } = location;
|
||||||
|
|
||||||
const page = `${pathname}${search}`;
|
const page = `${pathname}${search}`;
|
||||||
|
|
|
@ -399,12 +399,12 @@ function App(props: Props) {
|
||||||
|
|
||||||
{/* @if TARGET='web' */}
|
{/* @if TARGET='web' */}
|
||||||
<YoutubeWelcome />
|
<YoutubeWelcome />
|
||||||
{!shouldHideNag && <OpenInAppLink uri={uri} />}
|
{false && !shouldHideNag && <OpenInAppLink uri={uri} />}
|
||||||
{!shouldHideNag && <NagContinueFirstRun />}
|
{!shouldHideNag && <NagContinueFirstRun />}
|
||||||
{(lbryTvApiStatus === STATUS_DEGRADED || lbryTvApiStatus === STATUS_FAILING) && !shouldHideNag && (
|
{(lbryTvApiStatus === STATUS_DEGRADED || lbryTvApiStatus === STATUS_FAILING) && !shouldHideNag && (
|
||||||
<NagDegradedPerformance onClose={() => setLbryTvApiStatus(STATUS_OK)} />
|
<NagDegradedPerformance onClose={() => setLbryTvApiStatus(STATUS_OK)} />
|
||||||
)}
|
)}
|
||||||
{lbryTvApiStatus === STATUS_OK && showAnalyticsNag && !shouldHideNag && (
|
{false && lbryTvApiStatus === STATUS_OK && showAnalyticsNag && !shouldHideNag && (
|
||||||
<NagDataCollection onClose={handleAnalyticsDismiss} />
|
<NagDataCollection onClose={handleAnalyticsDismiss} />
|
||||||
)}
|
)}
|
||||||
{/* @endif */}
|
{/* @endif */}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
import { makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
import { makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
|
|
||||||
import ChannelPage from './view';
|
import ChannelPage from './view';
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ const select = (state, props) => {
|
||||||
channelIsBlocked: makeSelectChannelIsMuted(props.uri)(state),
|
channelIsBlocked: makeSelectChannelIsMuted(props.uri)(state),
|
||||||
claim: props.uri && makeSelectClaimForUri(props.uri)(state),
|
claim: props.uri && makeSelectClaimForUri(props.uri)(state),
|
||||||
isAuthenticated: selectUserVerifiedEmail(state),
|
isAuthenticated: selectUserVerifiedEmail(state),
|
||||||
showMature: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
showMature: selectShowMatureContent(state),
|
||||||
tileLayout: makeSelectClientSetting(SETTINGS.TILE_LAYOUT)(state),
|
tileLayout: makeSelectClientSetting(SETTINGS.TILE_LAYOUT)(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -132,10 +132,12 @@ function ChannelContent(props: Props) {
|
||||||
|
|
||||||
{claim && claimsInChannel > 0 ? (
|
{claim && claimsInChannel > 0 ? (
|
||||||
<ClaimListDiscover
|
<ClaimListDiscover
|
||||||
|
hideLivestreamClaims
|
||||||
showHiddenByUser={viewHiddenChannels}
|
showHiddenByUser={viewHiddenChannels}
|
||||||
forceShowReposts
|
forceShowReposts
|
||||||
tileLayout={tileLayout}
|
tileLayout={tileLayout}
|
||||||
uris={searchResults}
|
uris={searchResults}
|
||||||
|
streamType={CS.CONTENT_ALL}
|
||||||
channelIds={[claim.claim_id]}
|
channelIds={[claim.claim_id]}
|
||||||
feeAmount={CS.FEE_AMOUNT_ANY}
|
feeAmount={CS.FEE_AMOUNT_ANY}
|
||||||
defaultOrderBy={CS.ORDER_BY_NEW}
|
defaultOrderBy={CS.ORDER_BY_NEW}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import analytics from 'analytics';
|
||||||
import LbcSymbol from 'component/common/lbc-symbol';
|
import LbcSymbol from 'component/common/lbc-symbol';
|
||||||
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
|
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
|
||||||
import WalletSpendableBalanceHelp from 'component/walletSpendableBalanceHelp';
|
import WalletSpendableBalanceHelp from 'component/walletSpendableBalanceHelp';
|
||||||
|
import { SIMPLE_SITE } from 'config';
|
||||||
|
|
||||||
const LANG_NONE = 'none';
|
const LANG_NONE = 'none';
|
||||||
|
|
||||||
|
@ -358,7 +359,7 @@ function ChannelForm(props: Props) {
|
||||||
<Card
|
<Card
|
||||||
body={
|
body={
|
||||||
<TagsSearch
|
<TagsSearch
|
||||||
suggestMature
|
suggestMature={!SIMPLE_SITE}
|
||||||
disableAutoFocus
|
disableAutoFocus
|
||||||
limitSelect={MAX_TAG_SELECT}
|
limitSelect={MAX_TAG_SELECT}
|
||||||
tagsPassedIn={params.tags || []}
|
tagsPassedIn={params.tags || []}
|
||||||
|
|
BIN
ui/component/channelThumbnail/spaceman.png
Normal file
BIN
ui/component/channelThumbnail/spaceman.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
|
@ -2,7 +2,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { parseURI } from 'lbry-redux';
|
import { parseURI } from 'lbry-redux';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Gerbil from './gerbil.png';
|
import Spaceman from './spaceman.png';
|
||||||
import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper';
|
import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper';
|
||||||
import ChannelStakedIndicator from 'component/channelStakedIndicator';
|
import ChannelStakedIndicator from 'component/channelStakedIndicator';
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ function ChannelThumbnail(props: Props) {
|
||||||
<img
|
<img
|
||||||
alt={__('Channel profile picture')}
|
alt={__('Channel profile picture')}
|
||||||
className="channel-thumbnail__default"
|
className="channel-thumbnail__default"
|
||||||
src={!thumbError && thumbnailPreview ? thumbnailPreview : Gerbil}
|
src={!thumbError && thumbnailPreview ? thumbnailPreview : Spaceman}
|
||||||
onError={() => setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
|
onError={() => setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -91,7 +91,7 @@ function ChannelThumbnail(props: Props) {
|
||||||
<img
|
<img
|
||||||
alt={__('Channel profile picture')}
|
alt={__('Channel profile picture')}
|
||||||
className="channel-thumbnail__custom"
|
className="channel-thumbnail__custom"
|
||||||
src={!thumbError ? thumbnailPreview || thumbnail : Gerbil}
|
src={!thumbError ? thumbnailPreview || thumbnail : Spaceman}
|
||||||
onError={() => setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
|
onError={() => setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -38,6 +38,7 @@ type Props = {
|
||||||
renderActions?: (Claim) => ?Node,
|
renderActions?: (Claim) => ?Node,
|
||||||
searchInLanguage: boolean,
|
searchInLanguage: boolean,
|
||||||
hideMenu?: boolean,
|
hideMenu?: boolean,
|
||||||
|
hideLivestreamClaims?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ClaimList(props: Props) {
|
export default function ClaimList(props: Props) {
|
||||||
|
@ -63,6 +64,7 @@ export default function ClaimList(props: Props) {
|
||||||
renderActions,
|
renderActions,
|
||||||
searchInLanguage,
|
searchInLanguage,
|
||||||
hideMenu,
|
hideMenu,
|
||||||
|
hideLivestreamClaims,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
|
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
|
||||||
|
@ -101,7 +103,14 @@ export default function ClaimList(props: Props) {
|
||||||
return tileLayout && !header ? (
|
return tileLayout && !header ? (
|
||||||
<section className="claim-grid">
|
<section className="claim-grid">
|
||||||
{urisLength > 0 &&
|
{urisLength > 0 &&
|
||||||
uris.map((uri) => <ClaimPreviewTile key={uri} uri={uri} showHiddenByUser={showHiddenByUser} />)}
|
uris.map((uri) => (
|
||||||
|
<ClaimPreviewTile
|
||||||
|
key={uri}
|
||||||
|
uri={uri}
|
||||||
|
showHiddenByUser={showHiddenByUser}
|
||||||
|
hideLivestreamClaims={hideLivestreamClaims}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
{!timedOut && urisLength === 0 && !loading && <div className="empty main--empty">{empty || noResultMsg}</div>}
|
{!timedOut && urisLength === 0 && !loading && <div className="empty main--empty">{empty || noResultMsg}</div>}
|
||||||
{timedOut && timedOutMessage && <div className="empty main--empty">{timedOutMessage}</div>}
|
{timedOut && timedOutMessage && <div className="empty main--empty">{timedOutMessage}</div>}
|
||||||
</section>
|
</section>
|
||||||
|
@ -157,6 +166,7 @@ export default function ClaimList(props: Props) {
|
||||||
renderActions={renderActions}
|
renderActions={renderActions}
|
||||||
showUserBlocked={showHiddenByUser}
|
showUserBlocked={showHiddenByUser}
|
||||||
showHiddenByUser={showHiddenByUser}
|
showHiddenByUser={showHiddenByUser}
|
||||||
|
hideLivestreamClaims={hideLivestreamClaims}
|
||||||
customShouldHide={(claim: StreamClaim) => {
|
customShouldHide={(claim: StreamClaim) => {
|
||||||
// Hack to hide spee.ch thumbnail publishes
|
// Hack to hide spee.ch thumbnail publishes
|
||||||
// If it meets these requirements, it was probably uploaded here:
|
// If it meets these requirements, it was probably uploaded here:
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||||
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
||||||
import { makeSelectClientSetting, selectLanguage } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectShowMatureContent, selectLanguage } from 'redux/selectors/settings';
|
||||||
import { selectModerationBlockList } from 'redux/selectors/comments';
|
import { selectModerationBlockList } from 'redux/selectors/comments';
|
||||||
import ClaimListDiscover from './view';
|
import ClaimListDiscover from './view';
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ const select = (state) => ({
|
||||||
claimSearchByQuery: selectClaimSearchByQuery(state),
|
claimSearchByQuery: selectClaimSearchByQuery(state),
|
||||||
claimSearchByQueryLastPageReached: selectClaimSearchByQueryLastPageReached(state),
|
claimSearchByQueryLastPageReached: selectClaimSearchByQueryLastPageReached(state),
|
||||||
loading: selectFetchingClaimSearch(state),
|
loading: selectFetchingClaimSearch(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
showNsfw: selectShowMatureContent(state),
|
||||||
hideReposts: makeSelectClientSetting(SETTINGS.HIDE_REPOSTS)(state),
|
hideReposts: makeSelectClientSetting(SETTINGS.HIDE_REPOSTS)(state),
|
||||||
languageSetting: selectLanguage(state),
|
languageSetting: selectLanguage(state),
|
||||||
mutedUris: selectMutedChannels(state),
|
mutedUris: selectMutedChannels(state),
|
||||||
|
|
|
@ -23,7 +23,7 @@ type Props = {
|
||||||
personalView: boolean,
|
personalView: boolean,
|
||||||
doToggleTagFollowDesktop: (string) => void,
|
doToggleTagFollowDesktop: (string) => void,
|
||||||
meta?: Node,
|
meta?: Node,
|
||||||
showNsfw: boolean,
|
// showNsfw: boolean,
|
||||||
hideReposts: boolean,
|
hideReposts: boolean,
|
||||||
history: { action: string, push: (string) => void, replace: (string) => void },
|
history: { action: string, push: (string) => void, replace: (string) => void },
|
||||||
location: { search: string, pathname: string },
|
location: { search: string, pathname: string },
|
||||||
|
@ -66,6 +66,9 @@ type Props = {
|
||||||
searchInLanguage: boolean,
|
searchInLanguage: boolean,
|
||||||
scrollAnchor?: string,
|
scrollAnchor?: string,
|
||||||
showHiddenByUser?: boolean,
|
showHiddenByUser?: boolean,
|
||||||
|
limitClaimsPerChannel?: number,
|
||||||
|
releaseTime?: string,
|
||||||
|
hideLivestreamClaims?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ClaimListDiscover(props: Props) {
|
function ClaimListDiscover(props: Props) {
|
||||||
|
@ -78,7 +81,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
loading,
|
loading,
|
||||||
meta,
|
meta,
|
||||||
channelIds,
|
channelIds,
|
||||||
showNsfw,
|
// showNsfw,
|
||||||
hideReposts,
|
hideReposts,
|
||||||
history,
|
history,
|
||||||
location,
|
location,
|
||||||
|
@ -93,8 +96,8 @@ function ClaimListDiscover(props: Props) {
|
||||||
claimType,
|
claimType,
|
||||||
pageSize,
|
pageSize,
|
||||||
defaultClaimType,
|
defaultClaimType,
|
||||||
streamType,
|
streamType = CS.FILE_VIDEO,
|
||||||
defaultStreamType,
|
defaultStreamType = CS.FILE_VIDEO,
|
||||||
freshness,
|
freshness,
|
||||||
defaultFreshness = CS.FRESH_WEEK,
|
defaultFreshness = CS.FRESH_WEEK,
|
||||||
renderProperties,
|
renderProperties,
|
||||||
|
@ -113,8 +116,11 @@ function ClaimListDiscover(props: Props) {
|
||||||
forceShowReposts = false,
|
forceShowReposts = false,
|
||||||
languageSetting,
|
languageSetting,
|
||||||
searchInLanguage,
|
searchInLanguage,
|
||||||
|
limitClaimsPerChannel,
|
||||||
|
releaseTime,
|
||||||
scrollAnchor,
|
scrollAnchor,
|
||||||
showHiddenByUser = false,
|
showHiddenByUser = false,
|
||||||
|
hideLivestreamClaims,
|
||||||
} = props;
|
} = props;
|
||||||
const didNavigateForward = history.action === 'PUSH';
|
const didNavigateForward = history.action === 'PUSH';
|
||||||
const { search } = location;
|
const { search } = location;
|
||||||
|
@ -153,12 +159,12 @@ function ClaimListDiscover(props: Props) {
|
||||||
const durationParam = urlParams.get(CS.DURATION_KEY) || null;
|
const durationParam = urlParams.get(CS.DURATION_KEY) || null;
|
||||||
const channelIdsInUrl = urlParams.get(CS.CHANNEL_IDS_KEY);
|
const channelIdsInUrl = urlParams.get(CS.CHANNEL_IDS_KEY);
|
||||||
const channelIdsParam = channelIdsInUrl ? channelIdsInUrl.split(',') : channelIds;
|
const channelIdsParam = channelIdsInUrl ? channelIdsInUrl.split(',') : channelIds;
|
||||||
const feeAmountParam = urlParams.get('fee_amount') || feeAmount;
|
const feeAmountParam = urlParams.get('fee_amount') || feeAmount || CS.FEE_AMOUNT_ONLY_FREE;
|
||||||
const originalPageSize = pageSize || CS.PAGE_SIZE;
|
const originalPageSize = pageSize || CS.PAGE_SIZE;
|
||||||
const dynamicPageSize = isLargeScreen ? Math.ceil(originalPageSize * (3 / 2)) : originalPageSize;
|
const dynamicPageSize = isLargeScreen ? Math.ceil(originalPageSize * (3 / 2)) : originalPageSize;
|
||||||
const historyAction = history.action;
|
const historyAction = history.action;
|
||||||
|
|
||||||
let orderParam = orderBy || urlParams.get(CS.ORDER_BY_KEY) || defaultOrderBy;
|
let orderParam = orderBy || urlParams.get(CS.ORDER_BY_KEY) || defaultOrderBy || orderParamEntry;
|
||||||
|
|
||||||
if (!orderParam) {
|
if (!orderParam) {
|
||||||
if (historyAction === 'POP') {
|
if (historyAction === 'POP') {
|
||||||
|
@ -202,6 +208,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
fee_amount?: string,
|
fee_amount?: string,
|
||||||
has_source?: boolean,
|
has_source?: boolean,
|
||||||
has_no_source?: boolean,
|
has_no_source?: boolean,
|
||||||
|
limit_claims_per_channel?: number,
|
||||||
} = {
|
} = {
|
||||||
page_size: dynamicPageSize,
|
page_size: dynamicPageSize,
|
||||||
page,
|
page,
|
||||||
|
@ -213,7 +220,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
not_channel_ids:
|
not_channel_ids:
|
||||||
// If channelIdsParam were passed in, we don't need not_channel_ids
|
// If channelIdsParam were passed in, we don't need not_channel_ids
|
||||||
!channelIdsParam ? mutedAndBlockedChannelIds : [],
|
!channelIdsParam ? mutedAndBlockedChannelIds : [],
|
||||||
not_tags: !showNsfw ? MATURE_TAGS : [],
|
not_tags: MATURE_TAGS,
|
||||||
order_by:
|
order_by:
|
||||||
orderParam === CS.ORDER_BY_TRENDING
|
orderParam === CS.ORDER_BY_TRENDING
|
||||||
? CS.ORDER_BY_TRENDING_VALUE
|
? CS.ORDER_BY_TRENDING_VALUE
|
||||||
|
@ -226,6 +233,10 @@ function ClaimListDiscover(props: Props) {
|
||||||
options.has_source = true;
|
options.has_source = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (limitClaimsPerChannel) {
|
||||||
|
options.limit_claims_per_channel = limitClaimsPerChannel;
|
||||||
|
}
|
||||||
|
|
||||||
if (feeAmountParam && claimType !== CS.CLAIM_CHANNEL) {
|
if (feeAmountParam && claimType !== CS.CLAIM_CHANNEL) {
|
||||||
options.fee_amount = feeAmountParam;
|
options.fee_amount = feeAmountParam;
|
||||||
}
|
}
|
||||||
|
@ -255,7 +266,9 @@ function ClaimListDiscover(props: Props) {
|
||||||
options.reposted_claim_id = repostedClaimId;
|
options.reposted_claim_id = repostedClaimId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (claimType !== CS.CLAIM_CHANNEL) {
|
if (releaseTime) {
|
||||||
|
options.release_time = releaseTime;
|
||||||
|
} else if (claimType !== CS.CLAIM_CHANNEL) {
|
||||||
if (orderParam === CS.ORDER_BY_TOP && freshnessParam !== CS.FRESH_ALL) {
|
if (orderParam === CS.ORDER_BY_TOP && freshnessParam !== CS.FRESH_ALL) {
|
||||||
options.release_time = `>${Math.floor(moment().subtract(1, freshnessParam).startOf('hour').unix())}`;
|
options.release_time = `>${Math.floor(moment().subtract(1, freshnessParam).startOf('hour').unix())}`;
|
||||||
} else if (orderParam === CS.ORDER_BY_NEW || orderParam === CS.ORDER_BY_TRENDING) {
|
} else if (orderParam === CS.ORDER_BY_NEW || orderParam === CS.ORDER_BY_TRENDING) {
|
||||||
|
@ -333,9 +346,25 @@ function ClaimListDiscover(props: Props) {
|
||||||
|
|
||||||
const hasMatureTags = tagsParam && tagsParam.split(',').some((t) => MATURE_TAGS.includes(t));
|
const hasMatureTags = tagsParam && tagsParam.split(',').some((t) => MATURE_TAGS.includes(t));
|
||||||
const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
|
const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
|
||||||
const claimSearchResult = claimSearchByQuery[claimSearchCacheQuery];
|
let claimSearchResult = claimSearchByQuery[claimSearchCacheQuery];
|
||||||
const claimSearchResultLastPageReached = claimSearchByQueryLastPageReached[claimSearchCacheQuery];
|
const claimSearchResultLastPageReached = claimSearchByQueryLastPageReached[claimSearchCacheQuery];
|
||||||
|
|
||||||
|
// uncomment to fix an item on a page
|
||||||
|
// const fixUri = 'lbry://@corbettreport#0/lbryodysee#5';
|
||||||
|
// if (
|
||||||
|
// orderParam === CS.ORDER_BY_NEW &&
|
||||||
|
// claimSearchResult &&
|
||||||
|
// claimSearchResult.length > 2 &&
|
||||||
|
// window.location.pathname === '/$/rabbithole'
|
||||||
|
// ) {
|
||||||
|
// if (claimSearchResult.indexOf(fixUri) !== -1) {
|
||||||
|
// claimSearchResult.splice(claimSearchResult.indexOf(fixUri), 1);
|
||||||
|
// } else {
|
||||||
|
// claimSearchResult.pop();
|
||||||
|
// }
|
||||||
|
// claimSearchResult.splice(2, 0, fixUri);
|
||||||
|
// }
|
||||||
|
|
||||||
const [prevOptions, setPrevOptions] = React.useState(null);
|
const [prevOptions, setPrevOptions] = React.useState(null);
|
||||||
|
|
||||||
if (!isJustScrollingToNewPage(prevOptions, options)) {
|
if (!isJustScrollingToNewPage(prevOptions, options)) {
|
||||||
|
@ -455,7 +484,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
claimType={claimType}
|
claimType={claimType}
|
||||||
streamType={streamType}
|
streamType={streamType}
|
||||||
defaultStreamType={defaultStreamType}
|
defaultStreamType={defaultStreamType}
|
||||||
feeAmount={feeAmount}
|
// feeAmount={feeAmount}
|
||||||
orderBy={orderBy}
|
orderBy={orderBy}
|
||||||
defaultOrderBy={defaultOrderBy}
|
defaultOrderBy={defaultOrderBy}
|
||||||
hideAdvancedFilter={hideAdvancedFilter}
|
hideAdvancedFilter={hideAdvancedFilter}
|
||||||
|
@ -492,6 +521,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
includeSupportAction={includeSupportAction}
|
includeSupportAction={includeSupportAction}
|
||||||
injectedItem={injectedItem}
|
injectedItem={injectedItem}
|
||||||
showHiddenByUser={showHiddenByUser}
|
showHiddenByUser={showHiddenByUser}
|
||||||
|
hideLivestreamClaims={hideLivestreamClaims}
|
||||||
/>
|
/>
|
||||||
{loading && (
|
{loading && (
|
||||||
<div className="claim-grid">
|
<div className="claim-grid">
|
||||||
|
@ -520,6 +550,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
includeSupportAction={includeSupportAction}
|
includeSupportAction={includeSupportAction}
|
||||||
injectedItem={injectedItem}
|
injectedItem={injectedItem}
|
||||||
showHiddenByUser={showHiddenByUser}
|
showHiddenByUser={showHiddenByUser}
|
||||||
|
hideLivestreamClaims={hideLivestreamClaims}
|
||||||
/>
|
/>
|
||||||
{loading && new Array(dynamicPageSize).fill(1).map((x, i) => <ClaimPreview key={i} placeholder="loading" />)}
|
{loading && new Array(dynamicPageSize).fill(1).map((x, i) => <ClaimPreview key={i} placeholder="loading" />)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,14 +2,14 @@ import { connect } from 'react-redux';
|
||||||
import { selectFetchingClaimSearch, SETTINGS } from 'lbry-redux';
|
import { selectFetchingClaimSearch, SETTINGS } from 'lbry-redux';
|
||||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
||||||
import { makeSelectClientSetting, selectLanguage } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectShowMatureContent, selectLanguage } from 'redux/selectors/settings';
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
import ClaimListHeader from './view';
|
import ClaimListHeader from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = (state) => ({
|
||||||
followedTags: selectFollowedTags(state),
|
followedTags: selectFollowedTags(state),
|
||||||
loading: selectFetchingClaimSearch(state),
|
loading: selectFetchingClaimSearch(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
showNsfw: selectShowMatureContent(state),
|
||||||
searchInLanguage: makeSelectClientSetting(SETTINGS.SEARCH_IN_LANGUAGE)(state),
|
searchInLanguage: makeSelectClientSetting(SETTINGS.SEARCH_IN_LANGUAGE)(state),
|
||||||
languageSetting: selectLanguage(state),
|
languageSetting: selectLanguage(state),
|
||||||
});
|
});
|
||||||
|
|
|
@ -127,6 +127,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
const abandoned = !isResolvingUri && !claim;
|
const abandoned = !isResolvingUri && !claim;
|
||||||
const shouldHideActions = hideActions || type === 'small' || type === 'tooltip';
|
const shouldHideActions = hideActions || type === 'small' || type === 'tooltip';
|
||||||
const canonicalUrl = claim && claim.canonical_url;
|
const canonicalUrl = claim && claim.canonical_url;
|
||||||
|
|
||||||
let isValid = false;
|
let isValid = false;
|
||||||
if (uri) {
|
if (uri) {
|
||||||
try {
|
try {
|
||||||
|
@ -248,106 +249,110 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
'claim-preview__wrapper--small': type === 'small',
|
'claim-preview__wrapper--small': type === 'small',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{!hideRepostLabel && <ClaimRepostAuthor uri={uri} />}
|
<>
|
||||||
|
{!hideRepostLabel && <ClaimRepostAuthor uri={uri} />}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={classnames('claim-preview', {
|
className={classnames('claim-preview', {
|
||||||
'claim-preview--small': type === 'small' || type === 'tooltip',
|
'claim-preview--small': type === 'small' || type === 'tooltip',
|
||||||
'claim-preview--large': type === 'large',
|
'claim-preview--large': type === 'large',
|
||||||
'claim-preview--inline': type === 'inline',
|
'claim-preview--inline': type === 'inline',
|
||||||
'claim-preview--tooltip': type === 'tooltip',
|
'claim-preview--tooltip': type === 'tooltip',
|
||||||
'claim-preview--channel': isChannelUri,
|
'claim-preview--channel': isChannelUri,
|
||||||
'claim-preview--visited': !isChannelUri && !claimIsMine && hasVisitedUri,
|
'claim-preview--visited': !isChannelUri && !claimIsMine && hasVisitedUri,
|
||||||
'claim-preview--pending': pending,
|
'claim-preview--pending': pending,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{isChannelUri && claim ? (
|
{isChannelUri && claim ? (
|
||||||
<UriIndicator uri={contentUri} link>
|
<UriIndicator uri={contentUri} link>
|
||||||
<ChannelThumbnail uri={contentUri} />
|
<ChannelThumbnail uri={contentUri} />
|
||||||
</UriIndicator>
|
</UriIndicator>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{!pending ? (
|
{!pending ? (
|
||||||
<NavLink {...navLinkProps}>
|
|
||||||
<FileThumbnail thumbnail={thumbnailUrl}>
|
|
||||||
{/* @if TARGET='app' */}
|
|
||||||
{claim && (
|
|
||||||
<div className="claim-preview__hover-actions">
|
|
||||||
<FileDownloadLink uri={canonicalUrl} hideOpenButton hideDownloadStatus />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* @endif */}
|
|
||||||
{!isRepost && !isChannelUri && (
|
|
||||||
<div className="claim-preview__file-property-overlay">
|
|
||||||
<FileProperties uri={contentUri} small />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</FileThumbnail>
|
|
||||||
</NavLink>
|
|
||||||
) : (
|
|
||||||
<FileThumbnail thumbnail={thumbnailUrl} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="claim-preview__text">
|
|
||||||
<div className="claim-preview-metadata">
|
|
||||||
<div className="claim-preview-info">
|
|
||||||
{pending ? (
|
|
||||||
<ClaimPreviewTitle uri={contentUri} />
|
|
||||||
) : (
|
|
||||||
<NavLink {...navLinkProps}>
|
<NavLink {...navLinkProps}>
|
||||||
<ClaimPreviewTitle uri={uri} />
|
<FileThumbnail thumbnail={thumbnailUrl}>
|
||||||
|
{/* @if TARGET='app' */}
|
||||||
|
{claim && (
|
||||||
|
<div className="claim-preview__hover-actions">
|
||||||
|
<FileDownloadLink uri={canonicalUrl} hideOpenButton hideDownloadStatus />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* @endif */}
|
||||||
|
{!isRepost && !isChannelUri && !isLivestream && (
|
||||||
|
<div className="claim-preview__file-property-overlay">
|
||||||
|
<FileProperties uri={contentUri} small />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</FileThumbnail>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
) : (
|
||||||
|
<FileThumbnail thumbnail={thumbnailUrl} />
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* {type !== 'small' && !isChannelUri && signingChannel && SIMPLE_SITE && (
|
<div className="claim-preview__text">
|
||||||
|
<div className="claim-preview-metadata">
|
||||||
|
<div className="claim-preview-info">
|
||||||
|
{pending ? (
|
||||||
|
<ClaimPreviewTitle uri={contentUri} />
|
||||||
|
) : (
|
||||||
|
<NavLink {...navLinkProps}>
|
||||||
|
<ClaimPreviewTitle uri={uri} />
|
||||||
|
</NavLink>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* {type !== 'small' && !isChannelUri && signingChannel && SIMPLE_SITE && (
|
||||||
<ChannelThumbnail uri={signingChannel.permanent_url} />
|
<ChannelThumbnail uri={signingChannel.permanent_url} />
|
||||||
)} */}
|
)} */}
|
||||||
|
</div>
|
||||||
|
<ClaimPreviewSubtitle uri={uri} type={type} />
|
||||||
|
{(pending || !!reflectingProgress) && <PublishPending uri={uri} />}
|
||||||
</div>
|
</div>
|
||||||
<ClaimPreviewSubtitle uri={uri} type={type} />
|
{type !== 'small' && (
|
||||||
{(pending || !!reflectingProgress) && <PublishPending uri={uri} />}
|
<div className="claim-preview__actions">
|
||||||
|
{!pending && (
|
||||||
|
<>
|
||||||
|
{renderActions && claim && renderActions(claim)}
|
||||||
|
{shouldHideActions || renderActions ? null : actions !== undefined ? (
|
||||||
|
actions
|
||||||
|
) : (
|
||||||
|
<div className="claim-preview__primary-actions">
|
||||||
|
{!isChannelUri && signingChannel && (
|
||||||
|
<div className="claim-preview__channel-staked">
|
||||||
|
<ChannelThumbnail uri={signingChannel.permanent_url} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isChannelUri && !channelIsBlocked && !claimIsMine && (
|
||||||
|
<SubscribeButton
|
||||||
|
uri={contentUri.startsWith('lbry://') ? contentUri : `lbry://${contentUri}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{includeSupportAction && <ClaimSupportButton uri={uri} />}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{claim && (
|
||||||
|
<React.Fragment>
|
||||||
|
{typeof properties === 'function' ? (
|
||||||
|
properties(claim)
|
||||||
|
) : properties !== undefined ? (
|
||||||
|
properties
|
||||||
|
) : (
|
||||||
|
<ClaimTags uri={uri} type={type} />
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{type !== 'small' && (
|
|
||||||
<div className="claim-preview__actions">
|
|
||||||
{!pending && (
|
|
||||||
<>
|
|
||||||
{renderActions && claim && renderActions(claim)}
|
|
||||||
{shouldHideActions || renderActions ? null : actions !== undefined ? (
|
|
||||||
actions
|
|
||||||
) : (
|
|
||||||
<div className="claim-preview__primary-actions">
|
|
||||||
{!isChannelUri && signingChannel && (
|
|
||||||
<div className="claim-preview__channel-staked">
|
|
||||||
<ChannelThumbnail uri={signingChannel.permanent_url} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isChannelUri && !channelIsBlocked && !claimIsMine && (
|
|
||||||
<SubscribeButton uri={contentUri.startsWith('lbry://') ? contentUri : `lbry://${contentUri}`} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{includeSupportAction && <ClaimSupportButton uri={uri} />}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{claim && (
|
|
||||||
<React.Fragment>
|
|
||||||
{typeof properties === 'function' ? (
|
|
||||||
properties(claim)
|
|
||||||
) : properties !== undefined ? (
|
|
||||||
properties
|
|
||||||
) : (
|
|
||||||
<ClaimTags uri={uri} type={type} />
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{!hideMenu && <ClaimMenuList uri={uri} />}
|
||||||
{!hideMenu && <ClaimMenuList uri={uri} />}
|
</>
|
||||||
</WrapperElement>
|
</WrapperElement>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
doFileGet,
|
doFileGet,
|
||||||
makeSelectChannelForClaimUri,
|
makeSelectChannelForClaimUri,
|
||||||
makeSelectClaimIsNsfw,
|
makeSelectClaimIsNsfw,
|
||||||
|
makeSelectClaimIsStreamPlaceholder,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||||
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||||
|
@ -25,6 +26,7 @@ const select = (state, props) => ({
|
||||||
blockedChannelUris: selectMutedChannels(state),
|
blockedChannelUris: selectMutedChannels(state),
|
||||||
showMature: selectShowMatureContent(state),
|
showMature: selectShowMatureContent(state),
|
||||||
isMature: makeSelectClaimIsNsfw(props.uri)(state),
|
isMature: makeSelectClaimIsNsfw(props.uri)(state),
|
||||||
|
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
|
|
|
@ -40,6 +40,8 @@ type Props = {
|
||||||
isMature: boolean,
|
isMature: boolean,
|
||||||
showMature: boolean,
|
showMature: boolean,
|
||||||
showHiddenByUser?: boolean,
|
showHiddenByUser?: boolean,
|
||||||
|
hideLivestreamClaims?: boolean,
|
||||||
|
isLivestream: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ClaimPreviewTile(props: Props) {
|
function ClaimPreviewTile(props: Props) {
|
||||||
|
@ -60,6 +62,8 @@ function ClaimPreviewTile(props: Props) {
|
||||||
isMature,
|
isMature,
|
||||||
showMature,
|
showMature,
|
||||||
showHiddenByUser,
|
showHiddenByUser,
|
||||||
|
hideLivestreamClaims,
|
||||||
|
isLivestream,
|
||||||
} = props;
|
} = props;
|
||||||
const isRepost = claim && claim.repost_channel_url;
|
const isRepost = claim && claim.repost_channel_url;
|
||||||
const shouldFetch = claim === undefined;
|
const shouldFetch = claim === undefined;
|
||||||
|
@ -137,7 +141,7 @@ function ClaimPreviewTile(props: Props) {
|
||||||
shouldHide = blockedChannelUris.some((blockedUri) => blockedUri === claim.permanent_url);
|
shouldHide = blockedChannelUris.some((blockedUri) => blockedUri === claim.permanent_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldHide) {
|
if (shouldHide || (isLivestream && hideLivestreamClaims)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doClaimSearch, selectClaimSearchByQuery, selectFetchingClaimSearchByQuery, SETTINGS } from 'lbry-redux';
|
import { doClaimSearch, selectClaimSearchByQuery, selectFetchingClaimSearchByQuery, SETTINGS } from 'lbry-redux';
|
||||||
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
import ClaimListDiscover from './view';
|
import ClaimListDiscover from './view';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
claimSearchByQuery: selectClaimSearchByQuery(state),
|
claimSearchByQuery: selectClaimSearchByQuery(state),
|
||||||
fetchingClaimSearchByQuery: selectFetchingClaimSearchByQuery(state),
|
fetchingClaimSearchByQuery: selectFetchingClaimSearchByQuery(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
showNsfw: selectShowMatureContent(state),
|
||||||
hideReposts: makeSelectClientSetting(SETTINGS.HIDE_REPOSTS)(state),
|
hideReposts: makeSelectClientSetting(SETTINGS.HIDE_REPOSTS)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { ENABLE_NO_SOURCE_CLAIMS } from 'config';
|
import { ENABLE_NO_SOURCE_CLAIMS } from 'config';
|
||||||
|
import * as CS from 'constants/claim_search';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux';
|
import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux';
|
||||||
import ClaimPreviewTile from 'component/claimPreviewTile';
|
import ClaimPreviewTile from 'component/claimPreviewTile';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
prefixUris?: Array<string>,
|
|
||||||
uris: Array<string>,
|
uris: Array<string>,
|
||||||
doClaimSearch: ({}) => void,
|
doClaimSearch: ({}) => void,
|
||||||
showNsfw: boolean,
|
showNsfw: boolean,
|
||||||
|
@ -32,6 +32,8 @@ type Props = {
|
||||||
timestamp?: string,
|
timestamp?: string,
|
||||||
feeAmount?: string,
|
feeAmount?: string,
|
||||||
limitClaimsPerChannel?: number,
|
limitClaimsPerChannel?: number,
|
||||||
|
streamTypes?: Array<string>,
|
||||||
|
pin?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ClaimTilesDiscover(props: Props) {
|
function ClaimTilesDiscover(props: Props) {
|
||||||
|
@ -49,16 +51,18 @@ function ClaimTilesDiscover(props: Props) {
|
||||||
releaseTime,
|
releaseTime,
|
||||||
languages,
|
languages,
|
||||||
claimType,
|
claimType,
|
||||||
prefixUris,
|
|
||||||
timestamp,
|
timestamp,
|
||||||
feeAmount,
|
feeAmount,
|
||||||
limitClaimsPerChannel,
|
limitClaimsPerChannel,
|
||||||
fetchingClaimSearchByQuery,
|
fetchingClaimSearchByQuery,
|
||||||
|
streamTypes,
|
||||||
|
pin,
|
||||||
} = props;
|
} = props;
|
||||||
const { location } = useHistory();
|
const { location } = useHistory();
|
||||||
const urlParams = new URLSearchParams(location.search);
|
const urlParams = new URLSearchParams(location.search);
|
||||||
const feeAmountInUrl = urlParams.get('fee_amount');
|
const feeAmountInUrl = urlParams.get('fee_amount');
|
||||||
const feeAmountParam = feeAmountInUrl || feeAmount;
|
const feeAmountParam = feeAmountInUrl || feeAmount || CS.FEE_AMOUNT_ONLY_FREE;
|
||||||
|
|
||||||
const options: {
|
const options: {
|
||||||
page_size: number,
|
page_size: number,
|
||||||
no_totals: boolean,
|
no_totals: boolean,
|
||||||
|
@ -89,6 +93,7 @@ function ClaimTilesDiscover(props: Props) {
|
||||||
channel_ids: channelIds || [],
|
channel_ids: channelIds || [],
|
||||||
not_channel_ids: [],
|
not_channel_ids: [],
|
||||||
order_by: orderBy || ['trending_group', 'trending_mixed'],
|
order_by: orderBy || ['trending_group', 'trending_mixed'],
|
||||||
|
stream_types: streamTypes || [CS.FILE_VIDEO],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!ENABLE_NO_SOURCE_CLAIMS && (!claimType || claimType === 'stream')) {
|
if (!ENABLE_NO_SOURCE_CLAIMS && (!claimType || claimType === 'stream')) {
|
||||||
|
@ -129,12 +134,23 @@ function ClaimTilesDiscover(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
|
const claimSearchCacheQuery = createNormalizedClaimSearchKey(options);
|
||||||
const uris = (prefixUris || []).concat(claimSearchByQuery[claimSearchCacheQuery] || []);
|
const uris = claimSearchByQuery[claimSearchCacheQuery] || [];
|
||||||
|
|
||||||
// Don't use the query from createNormalizedClaimSearchKey for the effect since that doesn't include page & release_time
|
// Don't use the query from createNormalizedClaimSearchKey for the effect since that doesn't include page & release_time
|
||||||
const optionsStringForEffect = JSON.stringify(options);
|
const optionsStringForEffect = JSON.stringify(options);
|
||||||
const isLoading = fetchingClaimSearchByQuery[claimSearchCacheQuery];
|
const isLoading = fetchingClaimSearchByQuery[claimSearchCacheQuery];
|
||||||
const shouldPerformSearch = !isLoading && uris.length === 0;
|
const shouldPerformSearch = !isLoading && uris.length === 0;
|
||||||
|
|
||||||
|
const fixUri = 'lbry://@samtime#1/us-gov-tries-suing-a-cryptocurrency-lbry#8';
|
||||||
|
if (pin && uris && uris.length > 2 && window.location.pathname === '/') {
|
||||||
|
if (uris.indexOf(fixUri) !== -1) {
|
||||||
|
uris.splice(uris.indexOf(fixUri), 1);
|
||||||
|
} else {
|
||||||
|
uris.pop();
|
||||||
|
}
|
||||||
|
uris.splice(2, 0, fixUri);
|
||||||
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (shouldPerformSearch) {
|
if (shouldPerformSearch) {
|
||||||
const searchOptions = JSON.parse(optionsStringForEffect);
|
const searchOptions = JSON.parse(optionsStringForEffect);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { FormField, Form } from 'component/common/form';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import SelectChannel from 'component/selectChannel';
|
import SelectChannel from 'component/selectChannel';
|
||||||
import usePersistedState from 'effects/use-persisted-state';
|
import usePersistedState from 'effects/use-persisted-state';
|
||||||
import { FF_MAX_CHARS_IN_COMMENT } from 'constants/form-field';
|
import { FF_MAX_CHARS_IN_COMMENT, FF_MAX_CHARS_IN_LIVESTREAM_COMMENT } from 'constants/form-field';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import type { ElementRef } from 'react';
|
import type { ElementRef } from 'react';
|
||||||
import emoji from 'emoji-dictionary';
|
import emoji from 'emoji-dictionary';
|
||||||
|
@ -33,11 +33,15 @@ type Props = {
|
||||||
parentId: string,
|
parentId: string,
|
||||||
isReply: boolean,
|
isReply: boolean,
|
||||||
isPostingComment: boolean,
|
isPostingComment: boolean,
|
||||||
activeChannel: string,
|
|
||||||
activeChannelClaim: ?ChannelClaim,
|
activeChannelClaim: ?ChannelClaim,
|
||||||
livestream?: boolean,
|
bottom: boolean,
|
||||||
|
onSubmit: (string, string) => void,
|
||||||
|
livestream: boolean,
|
||||||
|
authenticated: boolean,
|
||||||
|
embed?: boolean,
|
||||||
toast: (string) => void,
|
toast: (string) => void,
|
||||||
claimIsMine: boolean,
|
claimIsMine: boolean,
|
||||||
|
commentingEnabled: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function CommentCreate(props: Props) {
|
export function CommentCreate(props: Props) {
|
||||||
|
@ -53,12 +57,20 @@ export function CommentCreate(props: Props) {
|
||||||
parentId,
|
parentId,
|
||||||
isPostingComment,
|
isPostingComment,
|
||||||
activeChannelClaim,
|
activeChannelClaim,
|
||||||
|
onSubmit,
|
||||||
|
bottom,
|
||||||
livestream,
|
livestream,
|
||||||
|
authenticated,
|
||||||
|
embed,
|
||||||
toast,
|
toast,
|
||||||
claimIsMine,
|
claimIsMine,
|
||||||
|
commentingEnabled,
|
||||||
} = props;
|
} = props;
|
||||||
const buttonref: ElementRef<any> = React.useRef();
|
const buttonref: ElementRef<any> = React.useRef();
|
||||||
const { push } = useHistory();
|
const {
|
||||||
|
push,
|
||||||
|
location: { pathname },
|
||||||
|
} = useHistory();
|
||||||
const { claim_id: claimId } = claim;
|
const { claim_id: claimId } = claim;
|
||||||
const [commentValue, setCommentValue] = React.useState('');
|
const [commentValue, setCommentValue] = React.useState('');
|
||||||
const [lastCommentTime, setLastCommentTime] = React.useState();
|
const [lastCommentTime, setLastCommentTime] = React.useState();
|
||||||
|
@ -80,7 +92,7 @@ export function CommentCreate(props: Props) {
|
||||||
|
|
||||||
function altEnterListener(e: SyntheticKeyboardEvent<*>) {
|
function altEnterListener(e: SyntheticKeyboardEvent<*>) {
|
||||||
const KEYCODE_ENTER = 13;
|
const KEYCODE_ENTER = 13;
|
||||||
if ((e.ctrlKey || e.metaKey) && e.keyCode === KEYCODE_ENTER) {
|
if ((livestream || e.ctrlKey || e.metaKey) && e.keyCode === KEYCODE_ENTER) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
buttonref.current.click();
|
buttonref.current.click();
|
||||||
}
|
}
|
||||||
|
@ -101,9 +113,7 @@ export function CommentCreate(props: Props) {
|
||||||
: (lastCommentTime - Date.now()) / 1000 + COMMENT_SLOW_MODE_SECONDS;
|
: (lastCommentTime - Date.now()) / 1000 + COMMENT_SLOW_MODE_SECONDS;
|
||||||
|
|
||||||
if (livestream && !claimIsMine && timeUntilCanComment > 0) {
|
if (livestream && !claimIsMine && timeUntilCanComment > 0) {
|
||||||
toast(
|
toast(__('Slowmode is on. You can comment again in %time% seconds.', { time: Math.ceil(timeUntilCanComment) }));
|
||||||
__('Slowmode is on. You can comment again in %time% seconds.', { time: Math.ceil(timeUntilCanComment) })
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +122,10 @@ export function CommentCreate(props: Props) {
|
||||||
setCommentValue('');
|
setCommentValue('');
|
||||||
setLastCommentTime(Date.now());
|
setLastCommentTime(Date.now());
|
||||||
|
|
||||||
|
if (onSubmit) {
|
||||||
|
onSubmit(commentValue, activeChannelClaim.name);
|
||||||
|
}
|
||||||
|
|
||||||
if (onDoneReplying) {
|
if (onDoneReplying) {
|
||||||
onDoneReplying();
|
onDoneReplying();
|
||||||
}
|
}
|
||||||
|
@ -126,9 +140,18 @@ export function CommentCreate(props: Props) {
|
||||||
|
|
||||||
useEffect(() => setCharCount(commentValue.length), [commentValue]);
|
useEffect(() => setCharCount(commentValue.length), [commentValue]);
|
||||||
|
|
||||||
if (!hasChannels) {
|
if (!commentingEnabled || !hasChannels) {
|
||||||
return (
|
return (
|
||||||
<div role="button" onClick={() => push(`/$/${PAGES.CHANNEL_NEW}`)}>
|
<div
|
||||||
|
role="button"
|
||||||
|
onClick={() =>
|
||||||
|
embed
|
||||||
|
? window.open(`https://odysee.com/$/${PAGES.AUTH}?redirect=/$/${PAGES.LIVESTREAM}`)
|
||||||
|
: authenticated
|
||||||
|
? push(`/$/${PAGES.CHANNEL_NEW}?redirect=${pathname}`)
|
||||||
|
: push(`/$/${PAGES.AUTH}?redirect=${pathname}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
<FormField
|
<FormField
|
||||||
type="textarea"
|
type="textarea"
|
||||||
name={'comment_signup_prompt'}
|
name={'comment_signup_prompt'}
|
||||||
|
@ -136,7 +159,7 @@ export function CommentCreate(props: Props) {
|
||||||
label={isFetchingChannels ? __('Comment') : undefined}
|
label={isFetchingChannels ? __('Comment') : undefined}
|
||||||
/>
|
/>
|
||||||
<div className="section__actions">
|
<div className="section__actions">
|
||||||
<Button disabled button="primary" label={__('Post --[button to submit something]--')} requiresAuth={IS_WEB} />
|
<Button disabled button="primary" label={__('Post --[button to submit something]--')} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -148,6 +171,7 @@ export function CommentCreate(props: Props) {
|
||||||
className={classnames('comment__create', {
|
className={classnames('comment__create', {
|
||||||
'comment__create--reply': isReply,
|
'comment__create--reply': isReply,
|
||||||
'comment__create--nested-reply': isNested,
|
'comment__create--nested-reply': isNested,
|
||||||
|
'comment__create--bottom': bottom,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
|
@ -171,7 +195,7 @@ export function CommentCreate(props: Props) {
|
||||||
charCount={charCount}
|
charCount={charCount}
|
||||||
onChange={handleCommentChange}
|
onChange={handleCommentChange}
|
||||||
autoFocus={isReply}
|
autoFocus={isReply}
|
||||||
textAreaMaxLength={FF_MAX_CHARS_IN_COMMENT}
|
textAreaMaxLength={livestream ? FF_MAX_CHARS_IN_LIVESTREAM_COMMENT : FF_MAX_CHARS_IN_COMMENT}
|
||||||
/>
|
/>
|
||||||
{livestream && hasChannels && (
|
{livestream && hasChannels && (
|
||||||
<div className="livestream__emoji-actions">
|
<div className="livestream__emoji-actions">
|
||||||
|
@ -190,7 +214,11 @@ export function CommentCreate(props: Props) {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="section__actions section__actions--no-margin">
|
<div
|
||||||
|
className={classnames('section__actions', {
|
||||||
|
'section__actions--no-margin': !livestream,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
ref={buttonref}
|
ref={buttonref}
|
||||||
button="primary"
|
button="primary"
|
||||||
|
|
|
@ -77,7 +77,7 @@ export default function CommentReactions(props: Props) {
|
||||||
<Button
|
<Button
|
||||||
requiresAuth={IS_WEB}
|
requiresAuth={IS_WEB}
|
||||||
title={__('Upvote')}
|
title={__('Upvote')}
|
||||||
icon={ICONS.UPVOTE}
|
icon={myReacts.includes(REACTION_TYPES.LIKE) ? ICONS.FIRE_ACTIVE : ICONS.FIRE}
|
||||||
className={classnames('comment__action', {
|
className={classnames('comment__action', {
|
||||||
'comment__action--active': myReacts && myReacts.includes(REACTION_TYPES.LIKE),
|
'comment__action--active': myReacts && myReacts.includes(REACTION_TYPES.LIKE),
|
||||||
})}
|
})}
|
||||||
|
@ -87,7 +87,7 @@ export default function CommentReactions(props: Props) {
|
||||||
<Button
|
<Button
|
||||||
requiresAuth={IS_WEB}
|
requiresAuth={IS_WEB}
|
||||||
title={__('Downvote')}
|
title={__('Downvote')}
|
||||||
icon={ICONS.DOWNVOTE}
|
icon={myReacts.includes(REACTION_TYPES.DISLIKE) ? ICONS.SLIME_ACTIVE : ICONS.SLIME}
|
||||||
className={classnames('comment__action', {
|
className={classnames('comment__action', {
|
||||||
'comment__action--active': myReacts && myReacts.includes(REACTION_TYPES.DISLIKE),
|
'comment__action--active': myReacts && myReacts.includes(REACTION_TYPES.DISLIKE),
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -20,6 +20,8 @@ type Props = {
|
||||||
defaultExpand?: boolean,
|
defaultExpand?: boolean,
|
||||||
nag?: Node,
|
nag?: Node,
|
||||||
smallTitle?: boolean,
|
smallTitle?: boolean,
|
||||||
|
onClick?: () => void,
|
||||||
|
children?: any, // not sure how this works
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Card(props: Props) {
|
export default function Card(props: Props) {
|
||||||
|
@ -37,12 +39,21 @@ export default function Card(props: Props) {
|
||||||
smallTitle = false,
|
smallTitle = false,
|
||||||
defaultExpand,
|
defaultExpand,
|
||||||
nag,
|
nag,
|
||||||
|
onClick,
|
||||||
|
children,
|
||||||
} = props;
|
} = props;
|
||||||
const [expanded, setExpanded] = useState(defaultExpand);
|
const [expanded, setExpanded] = useState(defaultExpand);
|
||||||
const expandable = defaultExpand !== undefined;
|
const expandable = defaultExpand !== undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={classnames(className, 'card')}>
|
<section
|
||||||
|
className={classnames(className, 'card')}
|
||||||
|
onClick={() => {
|
||||||
|
if (onClick) {
|
||||||
|
onClick();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
{(title || subtitle) && (
|
{(title || subtitle) && (
|
||||||
<div
|
<div
|
||||||
className={classnames('card__header--between', {
|
className={classnames('card__header--between', {
|
||||||
|
@ -93,6 +104,7 @@ export default function Card(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{actions && <div className="card__main-actions">{actions}</div>}
|
{actions && <div className="card__main-actions">{actions}</div>}
|
||||||
|
{children && <div className="card__main-actions">{children}</div>}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ type Props = {
|
||||||
myChannels: ?Array<ChannelClaim>,
|
myChannels: ?Array<ChannelClaim>,
|
||||||
doToast: ({ message: string }) => void,
|
doToast: ({ message: string }) => void,
|
||||||
clearPlayingUri: () => void,
|
clearPlayingUri: () => void,
|
||||||
|
hideRepost?: boolean,
|
||||||
isLivestreamClaim: boolean,
|
isLivestreamClaim: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ function FileActions(props: Props) {
|
||||||
myChannels,
|
myChannels,
|
||||||
clearPlayingUri,
|
clearPlayingUri,
|
||||||
doToast,
|
doToast,
|
||||||
|
hideRepost,
|
||||||
isLivestreamClaim,
|
isLivestreamClaim,
|
||||||
} = props;
|
} = props;
|
||||||
const {
|
const {
|
||||||
|
@ -83,23 +85,25 @@ function FileActions(props: Props) {
|
||||||
|
|
||||||
const lhsSection = (
|
const lhsSection = (
|
||||||
<>
|
<>
|
||||||
{ENABLE_FILE_REACTIONS && <FileReactions uri={uri} />}
|
{ENABLE_FILE_REACTIONS && <FileReactions uri={uri} livestream={isLivestreamClaim} />}
|
||||||
<ClaimSupportButton uri={uri} fileAction />
|
<ClaimSupportButton uri={uri} fileAction />
|
||||||
<Button
|
{!hideRepost && (
|
||||||
button="alt"
|
<Button
|
||||||
className="button--file-action"
|
button="alt"
|
||||||
icon={ICONS.REPOST}
|
className="button--file-action"
|
||||||
label={
|
icon={ICONS.REPOST}
|
||||||
claim.meta.reposted > 1 ? __(`%repost_total% Reposts`, { repost_total: claim.meta.reposted }) : __('Repost')
|
label={
|
||||||
}
|
claim.meta.reposted > 1 ? __(`%repost_total% Reposts`, { repost_total: claim.meta.reposted }) : __('Repost')
|
||||||
description={__('Repost')}
|
}
|
||||||
requiresAuth={IS_WEB}
|
description={__('Repost')}
|
||||||
onClick={handleRepostClick}
|
requiresAuth={IS_WEB}
|
||||||
/>
|
onClick={handleRepostClick}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
className="button--file-action"
|
className="button--file-action"
|
||||||
icon={ICONS.SHARE}
|
icon={ICONS.SHARE}
|
||||||
label={__('Share')}
|
label={isMobile ? undefined : __('Share')}
|
||||||
title={__('Share')}
|
title={__('Share')}
|
||||||
onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, webShareable })}
|
onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, webShareable })}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -50,7 +50,7 @@ function FileDescription(props: Props) {
|
||||||
<FileDetails uri={uri} />
|
<FileDetails uri={uri} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="section__actions--between">
|
<div className="card__bottom-actions">
|
||||||
{expanded ? (
|
{expanded ? (
|
||||||
<Button button="link" label={__('Less')} onClick={() => setExpanded(!expanded)} />
|
<Button button="link" label={__('Less')} onClick={() => setExpanded(!expanded)} />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import * as REACTION_TYPES from 'constants/reactions';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
@ -8,25 +9,51 @@ import NudgeFloating from 'component/nudgeFloating';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
claim: StreamClaim,
|
claim: StreamClaim,
|
||||||
doFetchReactions: string => void,
|
doFetchReactions: (string) => void,
|
||||||
doReactionLike: string => void,
|
doReactionLike: (string) => void,
|
||||||
doReactionDislike: string => void,
|
doReactionDislike: (string) => void,
|
||||||
uri: string,
|
uri: string,
|
||||||
likeCount: number,
|
likeCount: number,
|
||||||
dislikeCount: number,
|
dislikeCount: number,
|
||||||
myReaction: ?string,
|
myReaction: ?string,
|
||||||
|
livestream?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function FileReactions(props: Props) {
|
function FileReactions(props: Props) {
|
||||||
const { claim, uri, doFetchReactions, doReactionLike, doReactionDislike, likeCount, dislikeCount } = props;
|
const {
|
||||||
|
claim,
|
||||||
|
uri,
|
||||||
|
doFetchReactions,
|
||||||
|
doReactionLike,
|
||||||
|
doReactionDislike,
|
||||||
|
myReaction,
|
||||||
|
likeCount,
|
||||||
|
dislikeCount,
|
||||||
|
livestream,
|
||||||
|
} = props;
|
||||||
const claimId = claim && claim.claim_id;
|
const claimId = claim && claim.claim_id;
|
||||||
const channel = claim && claim.signing_channel && claim.signing_channel.name;
|
const channel = claim && claim.signing_channel && claim.signing_channel.name;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (claimId) {
|
function fetchReactions() {
|
||||||
doFetchReactions(claimId);
|
doFetchReactions(claimId);
|
||||||
}
|
}
|
||||||
}, [claimId, doFetchReactions]);
|
|
||||||
|
let fetchInterval;
|
||||||
|
if (claimId) {
|
||||||
|
fetchReactions();
|
||||||
|
|
||||||
|
if (livestream) {
|
||||||
|
fetchInterval = setInterval(fetchReactions, 10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (fetchInterval) {
|
||||||
|
clearInterval(fetchInterval);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [claimId, doFetchReactions, livestream]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -41,20 +68,46 @@ function FileReactions(props: Props) {
|
||||||
title={__('I like this')}
|
title={__('I like this')}
|
||||||
requiresAuth={IS_WEB}
|
requiresAuth={IS_WEB}
|
||||||
authSrc="filereaction_like"
|
authSrc="filereaction_like"
|
||||||
className={classnames('button--file-action')}
|
className={classnames('button--file-action', { 'button--fire': myReaction === REACTION_TYPES.LIKE })}
|
||||||
label={formatNumberWithCommas(likeCount, 0)}
|
label={
|
||||||
|
<>
|
||||||
|
{myReaction === REACTION_TYPES.LIKE && (
|
||||||
|
<>
|
||||||
|
<div className="button__fire-glow" />
|
||||||
|
<div className="button__fire-particle1" />
|
||||||
|
<div className="button__fire-particle2" />
|
||||||
|
<div className="button__fire-particle3" />
|
||||||
|
<div className="button__fire-particle4" />
|
||||||
|
<div className="button__fire-particle5" />
|
||||||
|
<div className="button__fire-particle6" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{formatNumberWithCommas(likeCount, 0)}
|
||||||
|
</>
|
||||||
|
}
|
||||||
iconSize={18}
|
iconSize={18}
|
||||||
icon={ICONS.UPVOTE}
|
icon={myReaction === REACTION_TYPES.LIKE ? ICONS.FIRE_ACTIVE : ICONS.FIRE}
|
||||||
onClick={() => doReactionLike(uri)}
|
onClick={() => doReactionLike(uri)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
requiresAuth={IS_WEB}
|
requiresAuth={IS_WEB}
|
||||||
authSrc={'filereaction_dislike'}
|
authSrc={'filereaction_dislike'}
|
||||||
title={__('I dislike this')}
|
title={__('I dislike this')}
|
||||||
className={classnames('button--file-action')}
|
className={classnames('button--file-action', { 'button--slime': myReaction === REACTION_TYPES.DISLIKE })}
|
||||||
label={formatNumberWithCommas(dislikeCount, 0)}
|
label={
|
||||||
|
<>
|
||||||
|
{myReaction === REACTION_TYPES.DISLIKE && (
|
||||||
|
<>
|
||||||
|
<div className="button__slime-stain" />
|
||||||
|
<div className="button__slime-drop1" />
|
||||||
|
<div className="button__slime-drop2" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{formatNumberWithCommas(dislikeCount, 0)}
|
||||||
|
</>
|
||||||
|
}
|
||||||
iconSize={18}
|
iconSize={18}
|
||||||
icon={ICONS.DOWNVOTE}
|
icon={myReaction === REACTION_TYPES.DISLIKE ? ICONS.SLIME_ACTIVE : ICONS.SLIME}
|
||||||
onClick={() => doReactionDislike(uri)}
|
onClick={() => doReactionDislike(uri)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -23,8 +23,8 @@ function FileSubtitle(props: Props) {
|
||||||
<FileViewCount uri={uri} />
|
<FileViewCount uri={uri} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{ /* did I need these params? */ }
|
||||||
<FileActions uri={uri} />
|
<FileActions uri={uri} hideRepost={livestream} livestream={livestream} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import { SIMPLE_SITE } from 'config';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import HelpLink from 'component/common/help-link';
|
import HelpLink from 'component/common/help-link';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
claim: ?StreamClaim,
|
claim: ?StreamClaim,
|
||||||
fetchViewCount: string => void,
|
fetchViewCount: (string) => void,
|
||||||
uri: string,
|
uri: string,
|
||||||
viewCount: string,
|
viewCount: string,
|
||||||
};
|
};
|
||||||
|
@ -12,7 +13,6 @@ type Props = {
|
||||||
function FileViewCount(props: Props) {
|
function FileViewCount(props: Props) {
|
||||||
const { claim, uri, fetchViewCount, viewCount } = props;
|
const { claim, uri, fetchViewCount, viewCount } = props;
|
||||||
const claimId = claim && claim.claim_id;
|
const claimId = claim && claim.claim_id;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (claimId) {
|
if (claimId) {
|
||||||
fetchViewCount(claimId);
|
fetchViewCount(claimId);
|
||||||
|
@ -24,7 +24,7 @@ function FileViewCount(props: Props) {
|
||||||
return (
|
return (
|
||||||
<span className="media__subtitle--centered">
|
<span className="media__subtitle--centered">
|
||||||
{viewCount !== 1 ? __('%view_count% views', { view_count: formattedViewCount }) : __('1 view')}
|
{viewCount !== 1 ? __('%view_count% views', { view_count: formattedViewCount }) : __('1 view')}
|
||||||
<HelpLink href="https://lbry.com/faq/views" />
|
{!SIMPLE_SITE && <HelpLink href="https://lbry.com/faq/views" />}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ import FilePrice from 'component/filePrice';
|
||||||
import { formatLbryUrlForWeb } from 'util/url';
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { URL } from 'config';
|
import { URL } from 'config';
|
||||||
import * as ICONS from 'constants/icons';
|
import OdyseeLogo from 'component/header/odysee_logo.png';
|
||||||
|
import OdyseeLogoWithText from 'component/header/odysee_white.png';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
|
@ -36,7 +37,10 @@ function FileViewerEmbeddedTitle(props: Props) {
|
||||||
{...contentLinkProps}
|
{...contentLinkProps}
|
||||||
/>
|
/>
|
||||||
<div className="file-viewer__embedded-info">
|
<div className="file-viewer__embedded-info">
|
||||||
<Button className="file-viewer__overlay-logo" icon={ICONS.LBRY} aria-label={__('Home')} {...lbryLinkProps} />
|
<Button className="file-viewer__overlay-logo" aria-label={__('Home')} {...lbryLinkProps}>
|
||||||
|
<img src={OdyseeLogo} className=" mobile-only" />
|
||||||
|
<img src={OdyseeLogoWithText} className=" mobile-hidden" />
|
||||||
|
</Button>
|
||||||
{isInApp && <FilePrice uri={uri} />}
|
{isInApp && <FilePrice uri={uri} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,6 +19,8 @@ import ChannelThumbnail from 'component/channelThumbnail';
|
||||||
import { remote } from 'electron';
|
import { remote } from 'electron';
|
||||||
import { IS_MAC } from 'component/app/view';
|
import { IS_MAC } from 'component/app/view';
|
||||||
// @endif
|
// @endif
|
||||||
|
import OdyseeLogoWithWhiteText from './odysee_white.png';
|
||||||
|
import OdyseeLogoWithText from './odysee.png';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
user: ?User,
|
user: ?User,
|
||||||
|
@ -86,8 +88,8 @@ const Header = (props: Props) => {
|
||||||
sidebarOpen,
|
sidebarOpen,
|
||||||
setSidebarOpen,
|
setSidebarOpen,
|
||||||
isAbsoluteSideNavHidden,
|
isAbsoluteSideNavHidden,
|
||||||
user,
|
|
||||||
hideCancel,
|
hideCancel,
|
||||||
|
user,
|
||||||
activeChannelClaim,
|
activeChannelClaim,
|
||||||
} = props;
|
} = props;
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
@ -98,7 +100,7 @@ const Header = (props: Props) => {
|
||||||
const isPwdResetPage = history.location.pathname.includes(PAGES.AUTH_PASSWORD_RESET);
|
const isPwdResetPage = history.location.pathname.includes(PAGES.AUTH_PASSWORD_RESET);
|
||||||
const hasBackout = Boolean(backout);
|
const hasBackout = Boolean(backout);
|
||||||
const { backLabel, backNavDefault, title: backTitle, simpleTitle: simpleBackTitle } = backout || {};
|
const { backLabel, backNavDefault, title: backTitle, simpleTitle: simpleBackTitle } = backout || {};
|
||||||
const notificationsEnabled = (user && user.experimental_ui) || false;
|
// const notificationsEnabled = (user && user.experimental_ui) || false; // fix this
|
||||||
const livestreamEnabled = (ENABLE_NO_SOURCE_CLAIMS && user && user.experimental_ui) || false;
|
const livestreamEnabled = (ENABLE_NO_SOURCE_CLAIMS && user && user.experimental_ui) || false;
|
||||||
const activeChannelUrl = activeChannelClaim && activeChannelClaim.permanent_url;
|
const activeChannelUrl = activeChannelClaim && activeChannelClaim.permanent_url;
|
||||||
|
|
||||||
|
@ -233,19 +235,12 @@ const Header = (props: Props) => {
|
||||||
icon={ICONS.MENU}
|
icon={ICONS.MENU}
|
||||||
onClick={() => setSidebarOpen(!sidebarOpen)}
|
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||||||
>
|
>
|
||||||
{isAbsoluteSideNavHidden && isMobile && notificationsEnabled && <NotificationBubble />}
|
{isAbsoluteSideNavHidden && isMobile && <NotificationBubble />}
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
className="header__navigation-item header__navigation-item--lbry"
|
className="header__navigation-item header__navigation-item--lbry header__navigation-item--button-mobile"
|
||||||
// @if TARGET='app'
|
|
||||||
label={'LBRY'}
|
|
||||||
// @endif
|
|
||||||
// @if TARGET='web'
|
|
||||||
label={LOGO_TITLE} // eslint-disable-line
|
|
||||||
// @endif
|
|
||||||
icon={ICONS.LBRY}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (history.location.pathname === '/') window.location.reload();
|
if (history.location.pathname === '/') window.location.reload();
|
||||||
}}
|
}}
|
||||||
|
@ -255,7 +250,12 @@ const Header = (props: Props) => {
|
||||||
}}
|
}}
|
||||||
// @endif
|
// @endif
|
||||||
{...homeButtonNavigationProps}
|
{...homeButtonNavigationProps}
|
||||||
/>
|
>
|
||||||
|
<img
|
||||||
|
src={currentTheme === 'light' ? OdyseeLogoWithText : OdyseeLogoWithWhiteText}
|
||||||
|
className="header__odysee"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
{!authHeader && (
|
{!authHeader && (
|
||||||
<div className="header__center">
|
<div className="header__center">
|
||||||
|
@ -272,7 +272,7 @@ const Header = (props: Props) => {
|
||||||
|
|
||||||
<HeaderMenuButtons
|
<HeaderMenuButtons
|
||||||
authenticated={authenticated}
|
authenticated={authenticated}
|
||||||
notificationsEnabled={notificationsEnabled}
|
notificationsEnabled
|
||||||
history={history}
|
history={history}
|
||||||
handleThemeToggle={handleThemeToggle}
|
handleThemeToggle={handleThemeToggle}
|
||||||
currentTheme={currentTheme}
|
currentTheme={currentTheme}
|
||||||
|
@ -425,6 +425,10 @@ function HeaderMenuButtons(props: HeaderMenuButtonProps) {
|
||||||
<Icon aria-hidden icon={ICONS.CHANNEL} />
|
<Icon aria-hidden icon={ICONS.CHANNEL} />
|
||||||
{__('New Channel')}
|
{__('New Channel')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.YOUTUBE_SYNC}`)}>
|
||||||
|
<Icon aria-hidden icon={ICONS.YOUTUBE} />
|
||||||
|
{__('Sync YouTube Channel')}
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
{livestreamEnabled && (
|
{livestreamEnabled && (
|
||||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.LIVESTREAM}`)}>
|
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.LIVESTREAM}`)}>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import LivestreamFeed from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
comments: makeSelectTopLevelCommentsForUri(props.uri)(state),
|
comments: makeSelectTopLevelCommentsForUri(props.uri)(state).slice(0, 25),
|
||||||
fetchingComments: selectIsFetchingComments(state),
|
fetchingComments: selectIsFetchingComments(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ export default function LivestreamFeed(props: Props) {
|
||||||
function handleScroll() {
|
function handleScroll() {
|
||||||
if (element) {
|
if (element) {
|
||||||
const scrollHeight = element.scrollHeight - element.offsetHeight;
|
const scrollHeight = element.scrollHeight - element.offsetHeight;
|
||||||
const isAtBottom = scrollHeight === element.scrollTop;
|
const isAtBottom = scrollHeight <= element.scrollTop + 100;
|
||||||
|
|
||||||
if (!isAtBottom) {
|
if (!isAtBottom) {
|
||||||
hasScrolledComments.current = true;
|
hasScrolledComments.current = true;
|
||||||
|
@ -71,7 +71,7 @@ export default function LivestreamFeed(props: Props) {
|
||||||
// Only update comment scroll if the user hasn't scrolled up to view old comments
|
// Only update comment scroll if the user hasn't scrolled up to view old comments
|
||||||
// If they have, do nothing
|
// If they have, do nothing
|
||||||
if (!hasScrolledComments.current || !performedInitialScroll) {
|
if (!hasScrolledComments.current || !performedInitialScroll) {
|
||||||
element.scrollTop = element.scrollHeight - element.offsetHeight;
|
setTimeout(() => (element.scrollTop = element.scrollHeight - element.offsetHeight + 100), 20);
|
||||||
|
|
||||||
if (!performedInitialScroll) {
|
if (!performedInitialScroll) {
|
||||||
setPerformedInitialScroll(true);
|
setPerformedInitialScroll(true);
|
||||||
|
|
9
ui/component/livestreamFeed/index.js
Normal file
9
ui/component/livestreamFeed/index.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { makeSelectClaimForUri } from 'lbry-redux';
|
||||||
|
import LivestreamFeed from './view';
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select)(LivestreamFeed);
|
38
ui/component/livestreamFeed/view.jsx
Normal file
38
ui/component/livestreamFeed/view.jsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// @flow
|
||||||
|
import { BITWAVE_USERNAME, BITWAVE_EMBED_URL } from 'constants/livestream';
|
||||||
|
import React from 'react';
|
||||||
|
import FileTitleSection from 'component/fileTitleSection';
|
||||||
|
import LivestreamComments from 'component/livestreamComments';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
uri: string,
|
||||||
|
claim: ?StreamClaim,
|
||||||
|
activeViewers: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LivestreamFeed(props: Props) {
|
||||||
|
const { claim, uri, activeViewers } = props;
|
||||||
|
|
||||||
|
if (!claim) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="section card-stack">
|
||||||
|
<div className="file-render file-render--video livestream">
|
||||||
|
<div className="file-viewer">
|
||||||
|
<iframe
|
||||||
|
src={`${BITWAVE_EMBED_URL}/${BITWAVE_USERNAME}?skin=odysee&autoplay=1`}
|
||||||
|
scrolling="no"
|
||||||
|
allowFullScreen
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FileTitleSection uri={uri} livestream activeViewers={activeViewers} />
|
||||||
|
</div>
|
||||||
|
<LivestreamComments uri={uri} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ export default function LivestreamLink(props: Props) {
|
||||||
const { channelClaim } = props;
|
const { channelClaim } = props;
|
||||||
const [livestreamClaim, setLivestreamClaim] = React.useState(false);
|
const [livestreamClaim, setLivestreamClaim] = React.useState(false);
|
||||||
const [isLivestreaming, setIsLivestreaming] = React.useState(false);
|
const [isLivestreaming, setIsLivestreaming] = React.useState(false);
|
||||||
const livestreamChannelId = channelClaim.claim_id || ''; // TODO: fail in a safer way, probably
|
const livestreamChannelId = (channelClaim && channelClaim.claim_id) || ''; // TODO: fail in a safer way, probably
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (livestreamChannelId) {
|
if (livestreamChannelId) {
|
||||||
|
@ -64,11 +64,12 @@ export default function LivestreamLink(props: Props) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
// gonna pass the wrapper in so I don't have to rewrite the dmca/blocking logic in claimPreview.
|
||||||
<Card
|
const element = (props: { children: any }) => (
|
||||||
className="livestream__channel-link"
|
<Card className="livestream__channel-link" title={__('Live stream in progress')}>
|
||||||
title={__('Live stream in progress')}
|
{props.children}
|
||||||
actions={<ClaimPreview uri={livestreamClaim.canonical_url} livestream type="inline" />}
|
</Card>
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return <ClaimPreview uri={livestreamClaim.canonical_url} wrapperElement={element} type="inline" />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,12 @@ import classnames from 'classnames';
|
||||||
type Props = {
|
type Props = {
|
||||||
unseenCount: number,
|
unseenCount: number,
|
||||||
inline: boolean,
|
inline: boolean,
|
||||||
user: ?User,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function NotificationHeaderButton(props: Props) {
|
export default function NotificationHeaderButton(props: Props) {
|
||||||
const { unseenCount, inline = false, user } = props;
|
const { unseenCount, inline = false } = props;
|
||||||
const notificationsEnabled = user && user.experimental_ui;
|
|
||||||
|
|
||||||
if (unseenCount === 0 || !notificationsEnabled) {
|
if (unseenCount === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,8 @@ export default function NotificationHeaderButton(props: Props) {
|
||||||
// notifications,
|
// notifications,
|
||||||
// fetching,
|
// fetching,
|
||||||
doSeeAllNotifications,
|
doSeeAllNotifications,
|
||||||
user,
|
// user,
|
||||||
} = props;
|
} = props;
|
||||||
const notificationsEnabled = user && user.experimental_ui;
|
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
|
|
||||||
function handleMenuClick() {
|
function handleMenuClick() {
|
||||||
|
@ -32,10 +31,6 @@ export default function NotificationHeaderButton(props: Props) {
|
||||||
push(`/$/${PAGES.NOTIFICATIONS}`);
|
push(`/$/${PAGES.NOTIFICATIONS}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!notificationsEnabled) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onClick={handleMenuClick}
|
onClick={handleMenuClick}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import type { Node } from 'react';
|
import type { Node } from 'react';
|
||||||
|
import * as PAGES from 'constants/pages';
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import SideNavigation from 'component/sideNavigation';
|
import SideNavigation from 'component/sideNavigation';
|
||||||
|
@ -21,7 +22,6 @@ type Props = {
|
||||||
isUpgradeAvailable: boolean,
|
isUpgradeAvailable: boolean,
|
||||||
authPage: boolean,
|
authPage: boolean,
|
||||||
filePage: boolean,
|
filePage: boolean,
|
||||||
homePage: boolean,
|
|
||||||
noHeader: boolean,
|
noHeader: boolean,
|
||||||
noFooter: boolean,
|
noFooter: boolean,
|
||||||
noSideNavigation: boolean,
|
noSideNavigation: boolean,
|
||||||
|
@ -56,16 +56,20 @@ function Page(props: Props) {
|
||||||
const {
|
const {
|
||||||
location: { pathname },
|
location: { pathname },
|
||||||
} = useHistory();
|
} = useHistory();
|
||||||
const [sidebarOpen, setSidebarOpen] = usePersistedState('sidebar', true);
|
const [sidebarOpen, setSidebarOpen] = usePersistedState('sidebar', false);
|
||||||
const isMediumScreen = useIsMediumScreen();
|
const isMediumScreen = useIsMediumScreen();
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
let isOnFilePage = false;
|
let isOnFilePage = false;
|
||||||
try {
|
try {
|
||||||
const url = pathname.slice(1).replace(/:/g, '#');
|
if (pathname.includes(`/$/${PAGES.LIVESTREAM}`)) {
|
||||||
const { isChannel } = parseURI(url);
|
|
||||||
if (!isChannel) {
|
|
||||||
isOnFilePage = true;
|
isOnFilePage = true;
|
||||||
|
} else {
|
||||||
|
const url = pathname.slice(1).replace(/:/g, '#');
|
||||||
|
const { isChannel } = parseURI(url);
|
||||||
|
if (!isChannel) {
|
||||||
|
isOnFilePage = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ function PublishAdditionalOptions(props: Props) {
|
||||||
type="select"
|
type="select"
|
||||||
name="content_language"
|
name="content_language"
|
||||||
value={language}
|
value={language}
|
||||||
onChange={event => updatePublishForm({ language: event.target.value })}
|
onChange={(event) => updatePublishForm({ language: event.target.value })}
|
||||||
>
|
>
|
||||||
{Object.entries(SUPPORTED_LANGUAGES).map(([langkey, langName]) => (
|
{Object.entries(SUPPORTED_LANGUAGES).map(([langkey, langName]) => (
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
|
@ -176,12 +176,12 @@ function PublishAdditionalOptions(props: Props) {
|
||||||
licenseUrl: newLicenseUrl,
|
licenseUrl: newLicenseUrl,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
handleLicenseDescriptionChange={event =>
|
handleLicenseDescriptionChange={(event) =>
|
||||||
updatePublishForm({
|
updatePublishForm({
|
||||||
otherLicenseDescription: event.target.value,
|
otherLicenseDescription: event.target.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
handleLicenseUrlChange={event => updatePublishForm({ licenseUrl: event.target.value })}
|
handleLicenseUrlChange={(event) => updatePublishForm({ licenseUrl: event.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -346,7 +346,8 @@ function PublishFile(props: Props) {
|
||||||
subtitle={isStillEditing && __('You are currently editing your upload.')}
|
subtitle={isStillEditing && __('You are currently editing your upload.')}
|
||||||
actions={
|
actions={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<PublishName />
|
<PublishName uri={uri} />
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
type="text"
|
type="text"
|
||||||
name="content_title"
|
name="content_title"
|
||||||
|
@ -358,12 +359,16 @@ function PublishFile(props: Props) {
|
||||||
/>
|
/>
|
||||||
{isPublishFile && (
|
{isPublishFile && (
|
||||||
<FileSelector
|
<FileSelector
|
||||||
label={__('File')}
|
label={__('Video file')}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
currentPath={currentFile}
|
currentPath={currentFile}
|
||||||
onFileChosen={handleFileChange}
|
onFileChosen={handleFileChange}
|
||||||
|
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
|
||||||
|
accept="video/mp4,video/x-m4v,video/*"
|
||||||
|
placeholder={__('Select video file to upload')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{isPublishFile && getMessage()}
|
||||||
|
|
||||||
{isPublishPost && (
|
{isPublishPost && (
|
||||||
<PostEditor
|
<PostEditor
|
||||||
|
@ -375,7 +380,7 @@ function PublishFile(props: Props) {
|
||||||
setCurrentFileType={setCurrentFileType}
|
setCurrentFileType={setCurrentFileType}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isPublishFile && getMessage()}
|
|
||||||
{/* @if TARGET='app' */}
|
{/* @if TARGET='app' */}
|
||||||
{isPublishFile && (
|
{isPublishFile && (
|
||||||
<FormField
|
<FormField
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
doPrepareEdit,
|
doPrepareEdit,
|
||||||
doCheckPublishNameAvailability,
|
doCheckPublishNameAvailability,
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
|
selectMyChannelClaims,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { doPublishDesktop } from 'redux/actions/publish';
|
import { doPublishDesktop } from 'redux/actions/publish';
|
||||||
import { selectUnclaimedRewardValue } from 'redux/selectors/rewards';
|
import { selectUnclaimedRewardValue } from 'redux/selectors/rewards';
|
||||||
|
@ -35,6 +36,7 @@ const select = (state) => ({
|
||||||
modal: selectModal(state),
|
modal: selectModal(state),
|
||||||
enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state),
|
enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state),
|
||||||
activeChannelClaim: selectActiveChannelClaim(state),
|
activeChannelClaim: selectActiveChannelClaim(state),
|
||||||
|
myChannels: selectMyChannelClaims(state),
|
||||||
incognito: selectIncognito(state),
|
incognito: selectIncognito(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import Card from 'component/common/card';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import * as PUBLISH_MODES from 'constants/publish_types';
|
import * as PUBLISH_MODES from 'constants/publish_types';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
|
import Spinner from 'component/spinner';
|
||||||
|
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
@ -201,6 +202,13 @@ function PublishForm(props: Props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if you enter the page and it is stuck in publishing, "stop it."
|
||||||
|
useEffect(() => {
|
||||||
|
if (publishing) {
|
||||||
|
clearPublish();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!thumbnail) {
|
if (!thumbnail) {
|
||||||
resetThumbnailStatus();
|
resetThumbnailStatus();
|
||||||
|
@ -249,6 +257,13 @@ function PublishForm(props: Props) {
|
||||||
}
|
}
|
||||||
}, [name, activeChannelName, resolveUri, updatePublishForm, checkAvailability]);
|
}, [name, activeChannelName, resolveUri, updatePublishForm, checkAvailability]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// because editingURI is lbry://channel_short/claim_long and that particular shape won't map to the claimId yet
|
||||||
|
if (editingURI) {
|
||||||
|
resolveUri(editingURI);
|
||||||
|
}
|
||||||
|
}, [editingURI, resolveUri]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updatePublishForm({
|
updatePublishForm({
|
||||||
isMarkdownPost: mode === PUBLISH_MODES.POST,
|
isMarkdownPost: mode === PUBLISH_MODES.POST,
|
||||||
|
@ -396,6 +411,15 @@ function PublishForm(props: Props) {
|
||||||
}
|
}
|
||||||
}, [autoSwitchMode, editingURI, fileMimeType, myClaimForUri, mode, setMode, setAutoSwitchMode]);
|
}, [autoSwitchMode, editingURI, fileMimeType, myClaimForUri, mode, setMode, setAutoSwitchMode]);
|
||||||
|
|
||||||
|
if (publishing) {
|
||||||
|
return (
|
||||||
|
<div className="main--empty">
|
||||||
|
<h1 className="section__subtitle">{__('Publishing...')}</h1>
|
||||||
|
<Spinner delayed />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Editing claim uri
|
// Editing claim uri
|
||||||
return (
|
return (
|
||||||
<div className="card-stack">
|
<div className="card-stack">
|
||||||
|
@ -415,6 +439,7 @@ function PublishForm(props: Props) {
|
||||||
<Button
|
<Button
|
||||||
key={String(modeName)}
|
key={String(modeName)}
|
||||||
icon={modeName}
|
icon={modeName}
|
||||||
|
iconSize={18}
|
||||||
label={__(MODE_TO_I18N_STR[String(modeName)] || '---')}
|
label={__(MODE_TO_I18N_STR[String(modeName)] || '---')}
|
||||||
button="alt"
|
button="alt"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -433,7 +458,7 @@ function PublishForm(props: Props) {
|
||||||
{mode === PUBLISH_MODES.FILE && <PublishDescription disabled={formDisabled} />}
|
{mode === PUBLISH_MODES.FILE && <PublishDescription disabled={formDisabled} />}
|
||||||
<Card actions={<SelectThumbnail />} />
|
<Card actions={<SelectThumbnail />} />
|
||||||
<TagsSelect
|
<TagsSelect
|
||||||
suggestMature
|
suggestMature={!SIMPLE_SITE}
|
||||||
disableAutoFocus
|
disableAutoFocus
|
||||||
hideHeader
|
hideHeader
|
||||||
label={__('Selected Tags')}
|
label={__('Selected Tags')}
|
||||||
|
|
|
@ -13,7 +13,6 @@ type Props = {
|
||||||
|
|
||||||
function NameHelpText(props: Props) {
|
function NameHelpText(props: Props) {
|
||||||
const { uri, myClaimForUri, onEditMyClaim, isStillEditing } = props;
|
const { uri, myClaimForUri, onEditMyClaim, isStillEditing } = props;
|
||||||
|
|
||||||
let nameHelpText;
|
let nameHelpText;
|
||||||
|
|
||||||
if (isStillEditing) {
|
if (isStillEditing) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import { SEARCH_OPTIONS } from 'constants/search';
|
||||||
import { SHOW_ADS, SIMPLE_SITE } from 'config';
|
import { SHOW_ADS, SIMPLE_SITE } from 'config';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ClaimList from 'component/claimList';
|
import ClaimList from 'component/claimList';
|
||||||
|
@ -32,7 +33,13 @@ export default function RecommendedContent(props: Props) {
|
||||||
if (stringifiedClaim) {
|
if (stringifiedClaim) {
|
||||||
const jsonClaim = JSON.parse(stringifiedClaim);
|
const jsonClaim = JSON.parse(stringifiedClaim);
|
||||||
if (jsonClaim && jsonClaim.value && jsonClaim.claim_id) {
|
if (jsonClaim && jsonClaim.value && jsonClaim.claim_id) {
|
||||||
const options: Options = { size: 20, related_to: jsonClaim.claim_id, isBackgroundSearch: true };
|
const options: Options = {
|
||||||
|
size: 20,
|
||||||
|
related_to: jsonClaim.claim_id,
|
||||||
|
isBackgroundSearch: true,
|
||||||
|
[SEARCH_OPTIONS.CLAIM_TYPE]: SEARCH_OPTIONS.INCLUDE_FILES,
|
||||||
|
[SEARCH_OPTIONS.MEDIA_VIDEO]: true,
|
||||||
|
};
|
||||||
if (jsonClaim && !mature) {
|
if (jsonClaim && !mature) {
|
||||||
options['nsfw'] = false;
|
options['nsfw'] = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,20 +20,20 @@ import Spinner from 'component/spinner';
|
||||||
type Props = {
|
type Props = {
|
||||||
doToast: ({ message: string }) => void,
|
doToast: ({ message: string }) => void,
|
||||||
doClearRepostError: () => void,
|
doClearRepostError: () => void,
|
||||||
doRepost: StreamRepostOptions => Promise<*>,
|
doRepost: (StreamRepostOptions) => Promise<*>,
|
||||||
title: string,
|
title: string,
|
||||||
claim?: StreamClaim,
|
claim?: StreamClaim,
|
||||||
enteredContentClaim?: StreamClaim,
|
enteredContentClaim?: StreamClaim,
|
||||||
balance: number,
|
balance: number,
|
||||||
channels: ?Array<ChannelClaim>,
|
channels: ?Array<ChannelClaim>,
|
||||||
doCheckPublishNameAvailability: string => Promise<*>,
|
doCheckPublishNameAvailability: (string) => Promise<*>,
|
||||||
error: ?string,
|
error: ?string,
|
||||||
reposting: boolean,
|
reposting: boolean,
|
||||||
uri: string,
|
uri: string,
|
||||||
name: string,
|
name: string,
|
||||||
contentUri: string,
|
contentUri: string,
|
||||||
setRepostUri: string => void,
|
setRepostUri: (string) => void,
|
||||||
setContentUri: string => void,
|
setContentUri: (string) => void,
|
||||||
doCheckPendingClaims: () => void,
|
doCheckPendingClaims: () => void,
|
||||||
redirectUri?: string,
|
redirectUri?: string,
|
||||||
passedRepostAmount: number,
|
passedRepostAmount: number,
|
||||||
|
@ -88,7 +88,7 @@ function RepostCreate(props: Props) {
|
||||||
const repostUrlName = `lbry://${!activeChannelClaim ? '' : `${activeChannelClaim.name}/`}`;
|
const repostUrlName = `lbry://${!activeChannelClaim ? '' : `${activeChannelClaim.name}/`}`;
|
||||||
|
|
||||||
const contentFirstRender = React.useRef(true);
|
const contentFirstRender = React.useRef(true);
|
||||||
const setAutoRepostBid = amount => {
|
const setAutoRepostBid = (amount) => {
|
||||||
if (balance && balance > 0.02) {
|
if (balance && balance > 0.02) {
|
||||||
if (uri) {
|
if (uri) {
|
||||||
setRepostBid(0.01);
|
setRepostBid(0.01);
|
||||||
|
@ -113,7 +113,7 @@ function RepostCreate(props: Props) {
|
||||||
const isLbryUrl = value.startsWith('lbry://') && value !== 'lbry://';
|
const isLbryUrl = value.startsWith('lbry://') && value !== 'lbry://';
|
||||||
const error = '';
|
const error = '';
|
||||||
|
|
||||||
const addLbryIfNot = term => {
|
const addLbryIfNot = (term) => {
|
||||||
return term.startsWith('lbry://') ? term : `lbry://${term}`;
|
return term.startsWith('lbry://') ? term : `lbry://${term}`;
|
||||||
};
|
};
|
||||||
if (wasCopiedFromWeb) {
|
if (wasCopiedFromWeb) {
|
||||||
|
@ -178,7 +178,7 @@ function RepostCreate(props: Props) {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (enteredRepostName && isNameValid(enteredRepostName, false)) {
|
if (enteredRepostName && isNameValid(enteredRepostName, false)) {
|
||||||
doCheckPublishNameAvailability(enteredRepostName).then(r => setAvailable(r));
|
doCheckPublishNameAvailability(enteredRepostName).then((r) => setAvailable(r));
|
||||||
}
|
}
|
||||||
}, [enteredRepostName, doCheckPublishNameAvailability]);
|
}, [enteredRepostName, doCheckPublishNameAvailability]);
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ function RepostCreate(props: Props) {
|
||||||
<div>
|
<div>
|
||||||
{uri && (
|
{uri && (
|
||||||
<fieldset-section>
|
<fieldset-section>
|
||||||
<ClaimPreview key={uri} uri={uri} actions={''} type={'inline'} showNullPlaceholder />
|
<ClaimPreview key={uri} uri={uri} actions={''} showNullPlaceholder />
|
||||||
</fieldset-section>
|
</fieldset-section>
|
||||||
)}
|
)}
|
||||||
{!uri && name && (
|
{!uri && name && (
|
||||||
|
@ -327,7 +327,7 @@ function RepostCreate(props: Props) {
|
||||||
name="content_url"
|
name="content_url"
|
||||||
value={enteredContent}
|
value={enteredContent}
|
||||||
error={contentError}
|
error={contentError}
|
||||||
onChange={event => setEnteredContentUri(event.target.value)}
|
onChange={(event) => setEnteredContentUri(event.target.value)}
|
||||||
placeholder={__('Enter a name or %domain% URL', { domain: SITE_URL })}
|
placeholder={__('Enter a name or %domain% URL', { domain: SITE_URL })}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -359,7 +359,7 @@ function RepostCreate(props: Props) {
|
||||||
type="text"
|
type="text"
|
||||||
name="repost_name"
|
name="repost_name"
|
||||||
value={enteredRepostName}
|
value={enteredRepostName}
|
||||||
onChange={event => setEnteredRepostName(event.target.value)}
|
onChange={(event) => setEnteredRepostName(event.target.value)}
|
||||||
placeholder={__('MyFunName')}
|
placeholder={__('MyFunName')}
|
||||||
/>
|
/>
|
||||||
</fieldset-group>
|
</fieldset-group>
|
||||||
|
@ -384,8 +384,8 @@ function RepostCreate(props: Props) {
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
disabled={!enteredRepostName || resolvingRepost}
|
disabled={!enteredRepostName || resolvingRepost}
|
||||||
onChange={event => setRepostBid(event.target.value)}
|
onChange={(event) => setRepostBid(event.target.value)}
|
||||||
onWheel={e => e.stopPropagation()}
|
onWheel={(e) => e.stopPropagation()}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
||||||
|
|
|
@ -216,6 +216,7 @@ function AppRouter(props: Props) {
|
||||||
|
|
||||||
<Route path={`/`} exact component={HomePage} />
|
<Route path={`/`} exact component={HomePage} />
|
||||||
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
|
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
|
||||||
|
<Route path={`/$/${PAGES.WILD_WEST}`} exact component={DiscoverPage} />
|
||||||
{/* $FlowFixMe */}
|
{/* $FlowFixMe */}
|
||||||
{dynamicRoutes.map((dynamicRouteProps: RowDataItem) => (
|
{dynamicRoutes.map((dynamicRouteProps: RowDataItem) => (
|
||||||
<Route
|
<Route
|
||||||
|
@ -283,6 +284,7 @@ function AppRouter(props: Props) {
|
||||||
|
|
||||||
<Route path={`/$/${PAGES.EMBED}/:claimName`} exact component={EmbedWrapperPage} />
|
<Route path={`/$/${PAGES.EMBED}/:claimName`} exact component={EmbedWrapperPage} />
|
||||||
<Route path={`/$/${PAGES.EMBED}/:claimName/:claimId`} exact component={EmbedWrapperPage} />
|
<Route path={`/$/${PAGES.EMBED}/:claimName/:claimId`} exact component={EmbedWrapperPage} />
|
||||||
|
|
||||||
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
||||||
<Route path="/:claimName" exact component={ShowPage} />
|
<Route path="/:claimName" exact component={ShowPage} />
|
||||||
<Route path="/:claimName/:streamName" exact component={ShowPage} />
|
<Route path="/:claimName/:streamName" exact component={ShowPage} />
|
||||||
|
|
|
@ -33,33 +33,34 @@ const SearchOptions = (props: Props) => {
|
||||||
/>
|
/>
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<Form className="search__options">
|
<Form className="search__options">
|
||||||
<fieldset>
|
{false && (
|
||||||
<legend className="search__legend">{__('Search For')}</legend>
|
<fieldset>
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
option: SEARCH_OPTIONS.INCLUDE_FILES,
|
option: SEARCH_OPTIONS.INCLUDE_FILES,
|
||||||
label: __('Files'),
|
label: __('Files'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
option: SEARCH_OPTIONS.INCLUDE_CHANNELS,
|
option: SEARCH_OPTIONS.INCLUDE_CHANNELS,
|
||||||
label: __('Channels'),
|
label: __('Channels'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
option: SEARCH_OPTIONS.INCLUDE_FILES_AND_CHANNELS,
|
option: SEARCH_OPTIONS.INCLUDE_FILES_AND_CHANNELS,
|
||||||
label: __('Everything'),
|
label: __('Everything'),
|
||||||
},
|
},
|
||||||
].map(({ option, label }) => (
|
].map(({ option, label }) => (
|
||||||
<FormField
|
<FormField
|
||||||
key={option}
|
key={option}
|
||||||
name={option}
|
name={option}
|
||||||
type="radio"
|
type="radio"
|
||||||
blockWrap={false}
|
blockWrap={false}
|
||||||
label={label}
|
label={label}
|
||||||
checked={options[SEARCH_OPTIONS.CLAIM_TYPE] === option}
|
checked={options[SEARCH_OPTIONS.CLAIM_TYPE] === option}
|
||||||
onChange={() => setSearchOption(SEARCH_OPTIONS.CLAIM_TYPE, option)}
|
onChange={() => setSearchOption(SEARCH_OPTIONS.CLAIM_TYPE, option)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
)}
|
||||||
|
|
||||||
<fieldset disabled={isFilteringByChannel}>
|
<fieldset disabled={isFilteringByChannel}>
|
||||||
<legend className="search__legend">{__('Type')}</legend>
|
<legend className="search__legend">{__('Type')}</legend>
|
||||||
|
@ -70,7 +71,7 @@ const SearchOptions = (props: Props) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
option: SEARCH_OPTIONS.MEDIA_AUDIO,
|
option: SEARCH_OPTIONS.MEDIA_AUDIO,
|
||||||
label: __('Audio'),
|
label: __('Music'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
option: SEARCH_OPTIONS.MEDIA_IMAGE,
|
option: SEARCH_OPTIONS.MEDIA_IMAGE,
|
||||||
|
@ -98,22 +99,24 @@ const SearchOptions = (props: Props) => {
|
||||||
))}
|
))}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
{false && (
|
||||||
<legend className="search__legend">{__('Other Options')}</legend>
|
<fieldset>
|
||||||
<FormField
|
<legend className="search__legend">{__('Other Options')}</legend>
|
||||||
type="select"
|
<FormField
|
||||||
name="result-count"
|
type="select"
|
||||||
value={resultCount}
|
name="result-count"
|
||||||
onChange={(e) => setSearchOption(SEARCH_OPTIONS.RESULT_COUNT, e.target.value)}
|
value={resultCount}
|
||||||
blockWrap={false}
|
onChange={(e) => setSearchOption(SEARCH_OPTIONS.RESULT_COUNT, e.target.value)}
|
||||||
label={__('Returned Results')}
|
blockWrap={false}
|
||||||
>
|
label={__('Returned Results')}
|
||||||
<option value={10}>10</option>
|
>
|
||||||
<option value={30}>30</option>
|
<option value={10}>10</option>
|
||||||
<option value={50}>50</option>
|
<option value={30}>30</option>
|
||||||
<option value={100}>100</option>
|
<option value={50}>50</option>
|
||||||
</FormField>
|
<option value={100}>100</option>
|
||||||
</fieldset>
|
</FormField>
|
||||||
|
</fieldset>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -99,7 +99,7 @@ class SelectThumbnail extends React.PureComponent<Props, State> {
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
src={thumbnailSrc}
|
src={thumbnailSrc}
|
||||||
alt={__('Thumbnail Preview')}
|
alt={__('Thumbnail Preview')}
|
||||||
onError={e => {
|
onError={(e) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
thumbnailError: true,
|
thumbnailError: true,
|
||||||
});
|
});
|
||||||
|
@ -131,9 +131,9 @@ class SelectThumbnail extends React.PureComponent<Props, State> {
|
||||||
<FileSelector
|
<FileSelector
|
||||||
currentPath={thumbnailPath}
|
currentPath={thumbnailPath}
|
||||||
label={__('Thumbnail')}
|
label={__('Thumbnail')}
|
||||||
placeholder={__('Choose a thumbnail')}
|
placeholder={__('Thumbnails that entice a viewer to watch a video work best')}
|
||||||
accept={accept}
|
accept={accept}
|
||||||
onFileChosen={file => openModal(MODALS.CONFIRM_THUMBNAIL_UPLOAD, { file })}
|
onFileChosen={(file) => openModal(MODALS.CONFIRM_THUMBNAIL_UPLOAD, { file })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{status === THUMBNAIL_STATUSES.COMPLETE && thumbnail && (
|
{status === THUMBNAIL_STATUSES.COMPLETE && thumbnail && (
|
||||||
|
|
|
@ -51,6 +51,7 @@ type Props = {
|
||||||
type SideNavLink = {
|
type SideNavLink = {
|
||||||
title: string,
|
title: string,
|
||||||
link?: string,
|
link?: string,
|
||||||
|
route?: string,
|
||||||
onClick?: () => any,
|
onClick?: () => any,
|
||||||
icon: string,
|
icon: string,
|
||||||
extra?: Node,
|
extra?: Node,
|
||||||
|
@ -60,7 +61,6 @@ type SideNavLink = {
|
||||||
function SideNavigation(props: Props) {
|
function SideNavigation(props: Props) {
|
||||||
const {
|
const {
|
||||||
subscriptions,
|
subscriptions,
|
||||||
followedTags,
|
|
||||||
doSignOut,
|
doSignOut,
|
||||||
email,
|
email,
|
||||||
purchaseSuccess,
|
purchaseSuccess,
|
||||||
|
@ -72,6 +72,7 @@ function SideNavigation(props: Props) {
|
||||||
unseenCount,
|
unseenCount,
|
||||||
homepageData,
|
homepageData,
|
||||||
user,
|
user,
|
||||||
|
followedTags,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { EXTRA_SIDEBAR_LINKS } = homepageData;
|
const { EXTRA_SIDEBAR_LINKS } = homepageData;
|
||||||
|
@ -211,7 +212,7 @@ function SideNavigation(props: Props) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const notificationsEnabled = user && user.experimental_ui;
|
const notificationsEnabled = SIMPLE_SITE || (user && user.experimental_ui);
|
||||||
const isAuthenticated = Boolean(email);
|
const isAuthenticated = Boolean(email);
|
||||||
// SIDE LINKS: FOLLOWING, HOME, [FULL,] [EXTRA]
|
// SIDE LINKS: FOLLOWING, HOME, [FULL,] [EXTRA]
|
||||||
let SIDE_LINKS: Array<SideNavLink> = [];
|
let SIDE_LINKS: Array<SideNavLink> = [];
|
||||||
|
@ -291,8 +292,13 @@ function SideNavigation(props: Props) {
|
||||||
<li className="navigation-link">
|
<li className="navigation-link">
|
||||||
<Button label={__('FAQ')} href="https://odysee.com/@OdyseeHelp:b" />
|
<Button label={__('FAQ')} href="https://odysee.com/@OdyseeHelp:b" />
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li className="navigation-link">
|
<li className="navigation-link">
|
||||||
<Button label={__('Support')} href="https://lbry.com/support" />
|
<Button label={__('Community Guidelines')} href="https://odysee.com/@OdyseeHelp:b/Community-Guidelines:c" />
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li className="navigation-link">
|
||||||
|
<Button label={__('Support --[used in footer; general help/support]--')} href="https://lbry.com/support" />
|
||||||
</li>
|
</li>
|
||||||
<li className="navigation-link">
|
<li className="navigation-link">
|
||||||
<Button label={__('Terms')} href="https://lbry.com/termsofservice" />
|
<Button label={__('Terms')} href="https://lbry.com/termsofservice" />
|
||||||
|
@ -325,7 +331,7 @@ function SideNavigation(props: Props) {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
const { hideForUnauth, ...passedProps } = linkProps;
|
const { hideForUnauth, ...passedProps } = linkProps;
|
||||||
return !email && linkProps.hideForUnauth && IS_WEB ? null : (
|
return !email && linkProps.hideForUnauth && IS_WEB ? null : (
|
||||||
<li key={linkProps.link}>
|
<li key={linkProps.route || linkProps.link}>
|
||||||
<Button
|
<Button
|
||||||
{...passedProps}
|
{...passedProps}
|
||||||
label={__(linkProps.title)}
|
label={__(linkProps.title)}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
} from 'redux/selectors/subscriptions';
|
} from 'redux/selectors/subscriptions';
|
||||||
import { makeSelectPermanentUrlForUri } from 'lbry-redux';
|
import { makeSelectPermanentUrlForUri } from 'lbry-redux';
|
||||||
import { doToast } from 'redux/actions/notifications';
|
import { doToast } from 'redux/actions/notifications';
|
||||||
import { selectUser } from 'redux/selectors/user';
|
|
||||||
import SubscribeButton from './view';
|
import SubscribeButton from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
@ -15,7 +14,6 @@ const select = (state, props) => ({
|
||||||
firstRunCompleted: selectFirstRunCompleted(state),
|
firstRunCompleted: selectFirstRunCompleted(state),
|
||||||
permanentUrl: makeSelectPermanentUrlForUri(props.uri)(state),
|
permanentUrl: makeSelectPermanentUrlForUri(props.uri)(state),
|
||||||
notificationsDisabled: makeSelectNotificationsDisabled(props.uri)(state),
|
notificationsDisabled: makeSelectNotificationsDisabled(props.uri)(state),
|
||||||
user: selectUser(state),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, {
|
export default connect(select, {
|
||||||
|
|
|
@ -15,13 +15,12 @@ type SubscriptionArgs = {
|
||||||
type Props = {
|
type Props = {
|
||||||
permanentUrl: ?string,
|
permanentUrl: ?string,
|
||||||
isSubscribed: boolean,
|
isSubscribed: boolean,
|
||||||
doChannelSubscribe: SubscriptionArgs => void,
|
doChannelSubscribe: (SubscriptionArgs) => void,
|
||||||
doChannelUnsubscribe: SubscriptionArgs => void,
|
doChannelUnsubscribe: (SubscriptionArgs) => void,
|
||||||
showSnackBarOnSubscribe: boolean,
|
showSnackBarOnSubscribe: boolean,
|
||||||
doToast: ({ message: string }) => void,
|
doToast: ({ message: string }) => void,
|
||||||
shrinkOnMobile: boolean,
|
shrinkOnMobile: boolean,
|
||||||
notificationsDisabled: boolean,
|
notificationsDisabled: boolean,
|
||||||
user: ?User,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function SubscribeButton(props: Props) {
|
export default function SubscribeButton(props: Props) {
|
||||||
|
@ -34,14 +33,12 @@ export default function SubscribeButton(props: Props) {
|
||||||
doToast,
|
doToast,
|
||||||
shrinkOnMobile = false,
|
shrinkOnMobile = false,
|
||||||
notificationsDisabled,
|
notificationsDisabled,
|
||||||
user,
|
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const buttonRef = useRef();
|
const buttonRef = useRef();
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
let isHovering = useHover(buttonRef);
|
let isHovering = useHover(buttonRef);
|
||||||
isHovering = isMobile ? true : isHovering;
|
isHovering = isMobile ? true : isHovering;
|
||||||
const uiNotificationsEnabled = user && user.experimental_ui;
|
|
||||||
|
|
||||||
const { channelName } = parseURI(permanentUrl);
|
const { channelName } = parseURI(permanentUrl);
|
||||||
const claimName = '@' + channelName;
|
const claimName = '@' + channelName;
|
||||||
|
@ -66,7 +63,7 @@ export default function SubscribeButton(props: Props) {
|
||||||
requiresAuth={IS_WEB}
|
requiresAuth={IS_WEB}
|
||||||
label={label}
|
label={label}
|
||||||
title={titlePrefix}
|
title={titlePrefix}
|
||||||
onClick={e => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
subscriptionHandler({
|
subscriptionHandler({
|
||||||
|
@ -80,7 +77,7 @@ export default function SubscribeButton(props: Props) {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{isSubscribed && uiNotificationsEnabled && (
|
{isSubscribed && (
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
button="alt"
|
||||||
icon={notificationsDisabled ? ICONS.BELL : ICONS.BELL_ON}
|
icon={notificationsDisabled ? ICONS.BELL : ICONS.BELL_ON}
|
||||||
|
|
|
@ -53,7 +53,7 @@ function SyncPassword(props: Props) {
|
||||||
error={passwordError && __('Wrong password for %email%', { email })}
|
error={passwordError && __('Wrong password for %email%', { email })}
|
||||||
label={__('Password for %email%', { email })}
|
label={__('Password for %email%', { email })}
|
||||||
value={password}
|
value={password}
|
||||||
onChange={e => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
name="remember-password"
|
name="remember-password"
|
||||||
|
@ -75,7 +75,7 @@ function SyncPassword(props: Props) {
|
||||||
<I18nMessage
|
<I18nMessage
|
||||||
tokens={{
|
tokens={{
|
||||||
help: <Button button="link" label={__('help guide')} href="https://lbry.com/faq/account-sync" />,
|
help: <Button button="link" label={__('help guide')} href="https://lbry.com/faq/account-sync" />,
|
||||||
email: <Button button="link" label={'help@lbry.com'} href="mailto:help@lbry.com" />,
|
email: <Button button="link" label={'help@odysee.com'} href="mailto:help@odysee.com" />,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
If you are having issues, checkout our %help% or email us at %email%.
|
If you are having issues, checkout our %help% or email us at %email%.
|
||||||
|
|
|
@ -11,24 +11,23 @@ import { AUTO_FOLLOW_CHANNELS, SIMPLE_SITE } from 'config';
|
||||||
type Props = {
|
type Props = {
|
||||||
subscribedChannels: Array<Subscription>,
|
subscribedChannels: Array<Subscription>,
|
||||||
onContinue: () => void,
|
onContinue: () => void,
|
||||||
onBack: () => void,
|
|
||||||
channelSubscribe: (sub: Subscription) => void,
|
channelSubscribe: (sub: Subscription) => void,
|
||||||
homepageData: any,
|
homepageData: any,
|
||||||
};
|
};
|
||||||
|
|
||||||
const channelsToSubscribe = AUTO_FOLLOW_CHANNELS.trim()
|
const channelsToSubscribe = AUTO_FOLLOW_CHANNELS.trim()
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.filter(x => x !== '');
|
.filter((x) => x !== '');
|
||||||
|
|
||||||
function UserChannelFollowIntro(props: Props) {
|
function UserChannelFollowIntro(props: Props) {
|
||||||
const { subscribedChannels, channelSubscribe, onContinue, onBack, homepageData } = props;
|
const { subscribedChannels, channelSubscribe, onContinue, homepageData } = props;
|
||||||
const { PRIMARY_CONTENT_CHANNEL_IDS } = homepageData;
|
const { PRIMARY_CONTENT_CHANNEL_IDS } = homepageData;
|
||||||
const followingCount = (subscribedChannels && subscribedChannels.length) || 0;
|
const followingCount = (subscribedChannels && subscribedChannels.length) || 0;
|
||||||
|
|
||||||
// subscribe to lbry
|
// subscribe to lbry
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (channelsToSubscribe && channelsToSubscribe.length) {
|
if (channelsToSubscribe && channelsToSubscribe.length) {
|
||||||
channelsToSubscribe.forEach(c =>
|
channelsToSubscribe.forEach((c) =>
|
||||||
channelSubscribe({
|
channelSubscribe({
|
||||||
channelName: parseURI(c).claimName,
|
channelName: parseURI(c).claimName,
|
||||||
uri: c,
|
uri: c,
|
||||||
|
@ -41,25 +40,25 @@ function UserChannelFollowIntro(props: Props) {
|
||||||
<Card
|
<Card
|
||||||
title={__('Find channels to follow')}
|
title={__('Find channels to follow')}
|
||||||
subtitle={__(
|
subtitle={__(
|
||||||
'LBRY works better if you find and follow a couple creators you like. You can also block channels you never want to see.'
|
'Odysee works better if you find and follow a couple creators you like. You can also block channels you never want to see.'
|
||||||
)}
|
)}
|
||||||
actions={
|
actions={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className="section__actions--between">
|
|
||||||
<Button button="secondary" onClick={onBack} label={__('Back')} />
|
|
||||||
<Button
|
|
||||||
button={subscribedChannels.length < 1 ? 'alt' : 'primary'}
|
|
||||||
onClick={onContinue}
|
|
||||||
label={subscribedChannels.length < 1 ? __('Skip') : __('Continue')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="section__body">
|
<div className="section__body">
|
||||||
<ClaimListDiscover
|
<ClaimListDiscover
|
||||||
defaultOrderBy={CS.ORDER_BY_TOP}
|
hideFilters
|
||||||
|
meta={
|
||||||
|
<Button
|
||||||
|
button={subscribedChannels.length < 1 ? 'alt' : 'primary'}
|
||||||
|
onClick={onContinue}
|
||||||
|
label={subscribedChannels.length < 1 ? __('Skip') : __('Continue')}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
defaultOrderBy={CS.ORDER_BY_TRENDING}
|
||||||
defaultFreshness={CS.FRESH_ALL}
|
defaultFreshness={CS.FRESH_ALL}
|
||||||
claimType="channel"
|
claimIds={SIMPLE_SITE ? PRIMARY_CONTENT_CHANNEL_IDS : undefined}
|
||||||
claimIds={SIMPLE_SITE ? undefined : PRIMARY_CONTENT_CHANNEL_IDS}
|
claimType={CS.CLAIM_CHANNEL}
|
||||||
defaultTags={followingCount > 3 ? CS.TAGS_FOLLOWED : undefined}
|
maxPages={3}
|
||||||
/>
|
/>
|
||||||
{followingCount > 0 && (
|
{followingCount > 0 && (
|
||||||
<Nag
|
<Nag
|
||||||
|
|
|
@ -100,7 +100,7 @@ function UserEmailNew(props: Props) {
|
||||||
<Card
|
<Card
|
||||||
title={__('Join %SITE_NAME%', { SITE_NAME })}
|
title={__('Join %SITE_NAME%', { SITE_NAME })}
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
subtitle={__('An account with lbry.tv allows you to earn rewards and backup your data.')}
|
subtitle={__('An account with %domain% allows you to earn rewards and backup your data.', { domain: DOMAIN })}
|
||||||
// @endif
|
// @endif
|
||||||
actions={
|
actions={
|
||||||
<div className={classnames({ 'card--disabled': DOMAIN === 'lbry.tv' })}>
|
<div className={classnames({ 'card--disabled': DOMAIN === 'lbry.tv' })}>
|
||||||
|
|
|
@ -8,10 +8,10 @@ const THIRTY_SECONDS_IN_MS = 30000;
|
||||||
type Props = {
|
type Props = {
|
||||||
email: string,
|
email: string,
|
||||||
isReturningUser: boolean,
|
isReturningUser: boolean,
|
||||||
resendVerificationEmail: string => void,
|
resendVerificationEmail: (string) => void,
|
||||||
resendingEmail: boolean,
|
resendingEmail: boolean,
|
||||||
checkEmailVerified: () => void,
|
checkEmailVerified: () => void,
|
||||||
toast: string => void,
|
toast: (string) => void,
|
||||||
user: {
|
user: {
|
||||||
has_verified_email: boolean,
|
has_verified_email: boolean,
|
||||||
},
|
},
|
||||||
|
@ -102,7 +102,7 @@ class UserEmailVerify extends React.PureComponent<Props, State> {
|
||||||
<p className="help--card-actions">
|
<p className="help--card-actions">
|
||||||
<I18nMessage
|
<I18nMessage
|
||||||
tokens={{
|
tokens={{
|
||||||
help_link: <Button button="link" href="mailto:help@lbry.com" label="help@lbry.com" />,
|
help_link: <Button button="link" href="mailto:help@odysee.com" label="help@odysee.com" />,
|
||||||
chat_link: <Button button="link" href="https://chat.lbry.com" label={__('chat')} />,
|
chat_link: <Button button="link" href="https://chat.lbry.com" label={__('chat')} />,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import I18nMessage from 'component/i18nMessage';
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
verifyUserPhone: string => void,
|
verifyUserPhone: (string) => void,
|
||||||
resetPhone: () => void,
|
resetPhone: () => void,
|
||||||
phoneErrorMessage: string,
|
phoneErrorMessage: string,
|
||||||
phone: string,
|
phone: string,
|
||||||
|
@ -61,7 +61,7 @@ class UserPhoneVerify extends React.PureComponent<Props, State> {
|
||||||
name="code"
|
name="code"
|
||||||
placeholder="1234"
|
placeholder="1234"
|
||||||
value={this.state.code}
|
value={this.state.code}
|
||||||
onChange={event => {
|
onChange={(event) => {
|
||||||
this.handleCodeChanged(event);
|
this.handleCodeChanged(event);
|
||||||
}}
|
}}
|
||||||
label={__('Verification Code')}
|
label={__('Verification Code')}
|
||||||
|
@ -72,7 +72,7 @@ class UserPhoneVerify extends React.PureComponent<Props, State> {
|
||||||
<p className="help">
|
<p className="help">
|
||||||
<I18nMessage
|
<I18nMessage
|
||||||
tokens={{
|
tokens={{
|
||||||
help_link: <Button button="link" href="mailto:help@lbry.com" label="help@lbry.com" />,
|
help_link: <Button button="link" href="mailto:help@odysee.com" label="help@odysee.com" />,
|
||||||
chat_link: <Button button="link" href="https://chat.lbry.com" label={__('chat')} />,
|
chat_link: <Button button="link" href="https://chat.lbry.com" label={__('chat')} />,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as PAGES from 'constants/pages';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
|
@ -7,7 +6,6 @@ import UserEmailNew from 'component/userEmailNew';
|
||||||
import UserEmailVerify from 'component/userEmailVerify';
|
import UserEmailVerify from 'component/userEmailVerify';
|
||||||
import UserFirstChannel from 'component/userFirstChannel';
|
import UserFirstChannel from 'component/userFirstChannel';
|
||||||
import UserChannelFollowIntro from 'component/userChannelFollowIntro';
|
import UserChannelFollowIntro from 'component/userChannelFollowIntro';
|
||||||
import UserTagFollowIntro from 'component/userTagFollowIntro';
|
|
||||||
import YoutubeSync from 'page/youtubeSync';
|
import YoutubeSync from 'page/youtubeSync';
|
||||||
import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
|
import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
|
||||||
import { YOUTUBE_STATUSES } from 'lbryinc';
|
import { YOUTUBE_STATUSES } from 'lbryinc';
|
||||||
|
@ -42,7 +40,6 @@ type Props = {
|
||||||
creatingChannel: boolean,
|
creatingChannel: boolean,
|
||||||
setClientSetting: (string, boolean, ?boolean) => void,
|
setClientSetting: (string, boolean, ?boolean) => void,
|
||||||
followingAcknowledged: boolean,
|
followingAcknowledged: boolean,
|
||||||
tagsAcknowledged: boolean,
|
|
||||||
rewardsAcknowledged: boolean,
|
rewardsAcknowledged: boolean,
|
||||||
interestedInYoutubeSync: boolean,
|
interestedInYoutubeSync: boolean,
|
||||||
doToggleInterestedInYoutubeSync: () => void,
|
doToggleInterestedInYoutubeSync: () => void,
|
||||||
|
@ -67,7 +64,6 @@ function UserSignUp(props: Props) {
|
||||||
fetchingChannels,
|
fetchingChannels,
|
||||||
creatingChannel,
|
creatingChannel,
|
||||||
followingAcknowledged,
|
followingAcknowledged,
|
||||||
tagsAcknowledged,
|
|
||||||
rewardsAcknowledged,
|
rewardsAcknowledged,
|
||||||
setClientSetting,
|
setClientSetting,
|
||||||
interestedInYoutubeSync,
|
interestedInYoutubeSync,
|
||||||
|
@ -118,8 +114,7 @@ function UserSignUp(props: Props) {
|
||||||
interestedInYoutubeSync);
|
interestedInYoutubeSync);
|
||||||
const showYoutubeTransfer = hasVerifiedEmail && hasYoutubeChannels && !isYoutubeTransferComplete;
|
const showYoutubeTransfer = hasVerifiedEmail && hasYoutubeChannels && !isYoutubeTransferComplete;
|
||||||
const showFollowIntro = step === 'channels' || (hasVerifiedEmail && !followingAcknowledged);
|
const showFollowIntro = step === 'channels' || (hasVerifiedEmail && !followingAcknowledged);
|
||||||
const showTagsIntro = step === 'tags' || (hasVerifiedEmail && !tagsAcknowledged);
|
const canHijackSignInFlowWithSpinner = hasVerifiedEmail && !showFollowIntro && !rewardsAcknowledged;
|
||||||
const canHijackSignInFlowWithSpinner = hasVerifiedEmail && !showFollowIntro && !showTagsIntro && !rewardsAcknowledged;
|
|
||||||
const isCurrentlyFetchingSomething = fetchingChannels || claimingReward || syncingWallet || creatingChannel;
|
const isCurrentlyFetchingSomething = fetchingChannels || claimingReward || syncingWallet || creatingChannel;
|
||||||
const isWaitingForSomethingToFinish =
|
const isWaitingForSomethingToFinish =
|
||||||
// If the user has claimed the email award, we need to wait until the balance updates sometime in the future
|
// If the user has claimed the email award, we need to wait until the balance updates sometime in the future
|
||||||
|
@ -206,22 +201,6 @@ function UserSignUp(props: Props) {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
showTagsIntro && (
|
|
||||||
<UserTagFollowIntro
|
|
||||||
onContinue={() => {
|
|
||||||
let url = `/$/${PAGES.AUTH}?reset_scroll=1&${STEP_PARAM}=channels`;
|
|
||||||
if (redirect) {
|
|
||||||
url += `&${REDIRECT_PARAM}=${redirect}`;
|
|
||||||
}
|
|
||||||
if (shouldRedirectImmediately) {
|
|
||||||
url += `&${REDIRECT_IMMEDIATELY_PARAM}=true`;
|
|
||||||
}
|
|
||||||
|
|
||||||
replace(url);
|
|
||||||
setSettingAndSync(SETTINGS.TAGS_ACKNOWLEDGED, true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
showYoutubeTransfer && (
|
showYoutubeTransfer && (
|
||||||
<div>
|
<div>
|
||||||
<YoutubeTransferStatus /> <Confetti recycle={false} style={{ position: 'fixed' }} />
|
<YoutubeTransferStatus /> <Confetti recycle={false} style={{ position: 'fixed' }} />
|
||||||
|
|
|
@ -12,7 +12,7 @@ import LbcSymbol from 'component/common/lbc-symbol';
|
||||||
type Props = {
|
type Props = {
|
||||||
errorMessage: ?string,
|
errorMessage: ?string,
|
||||||
isPending: boolean,
|
isPending: boolean,
|
||||||
verifyUserIdentity: string => void,
|
verifyUserIdentity: (string) => void,
|
||||||
verifyPhone: () => void,
|
verifyPhone: () => void,
|
||||||
fetchUser: () => void,
|
fetchUser: () => void,
|
||||||
skipLink?: string,
|
skipLink?: string,
|
||||||
|
@ -60,7 +60,7 @@ class UserVerify extends React.PureComponent<Props> {
|
||||||
SITE_NAME,
|
SITE_NAME,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Verified accounts are eligible to earn LBRY Credits for views, watching and reposting content, sharing
|
Verified accounts are eligible to earn LBRY Credits for views, watching and reposting videos, sharing
|
||||||
invite links etc. Verifying also helps us keep the %SITE_NAME% community safe too! %Refresh% or %Skip%.
|
invite links etc. Verifying also helps us keep the %SITE_NAME% community safe too! %Refresh% or %Skip%.
|
||||||
</I18nMessage>
|
</I18nMessage>
|
||||||
</p>
|
</p>
|
||||||
|
@ -73,9 +73,7 @@ class UserVerify extends React.PureComponent<Props> {
|
||||||
<Card
|
<Card
|
||||||
icon={ICONS.PHONE}
|
icon={ICONS.PHONE}
|
||||||
title={__('Verify phone number')}
|
title={__('Verify phone number')}
|
||||||
subtitle={__(
|
subtitle={__('You will receive an SMS text message confirming your phone number is valid.')}
|
||||||
'You will receive an SMS text message confirming your phone number is valid. Does not work for Canada and possibly other regions.'
|
|
||||||
)}
|
|
||||||
actions={
|
actions={
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -51,14 +51,14 @@ type Props = {
|
||||||
toggleVideoTheaterMode: () => void,
|
toggleVideoTheaterMode: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
type VideoJSOptions = {
|
// type VideoJSOptions = {
|
||||||
controls: boolean,
|
// controls: boolean,
|
||||||
preload: string,
|
// preload: string,
|
||||||
playbackRates: Array<number>,
|
// playbackRates: Array<number>,
|
||||||
responsive: boolean,
|
// responsive: boolean,
|
||||||
poster?: string,
|
// poster: ?string,
|
||||||
muted?: boolean,
|
// muted: ?boolean,
|
||||||
};
|
// };
|
||||||
|
|
||||||
const videoPlaybackRates = [0.25, 0.5, 0.75, 1, 1.1, 1.25, 1.5, 1.75, 2];
|
const videoPlaybackRates = [0.25, 0.5, 0.75, 1, 1.1, 1.25, 1.5, 1.75, 2];
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ const IS_IOS =
|
||||||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) &&
|
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) &&
|
||||||
!window.MSStream;
|
!window.MSStream;
|
||||||
|
|
||||||
const VIDEO_JS_OPTIONS: VideoJSOptions = {
|
const VIDEO_JS_OPTIONS = {
|
||||||
preload: 'auto',
|
preload: 'auto',
|
||||||
playbackRates: videoPlaybackRates,
|
playbackRates: videoPlaybackRates,
|
||||||
responsive: true,
|
responsive: true,
|
||||||
|
@ -172,9 +172,7 @@ properties for this component should be kept to ONLY those that if changed shoul
|
||||||
*/
|
*/
|
||||||
export default React.memo<Props>(function VideoJs(props: Props) {
|
export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
const { autoplay, startMuted, source, sourceType, poster, isAudio, onPlayerReady, toggleVideoTheaterMode } = props;
|
const { autoplay, startMuted, source, sourceType, poster, isAudio, onPlayerReady, toggleVideoTheaterMode } = props;
|
||||||
|
|
||||||
const [reload, setReload] = useState('initial');
|
const [reload, setReload] = useState('initial');
|
||||||
|
|
||||||
const playerRef = useRef();
|
const playerRef = useRef();
|
||||||
const containerRef = useRef();
|
const containerRef = useRef();
|
||||||
const videoJsOptions = {
|
const videoJsOptions = {
|
||||||
|
|
|
@ -73,6 +73,8 @@ function VideoViewer(props: Props) {
|
||||||
const claimId = claim && claim.claim_id;
|
const claimId = claim && claim.claim_id;
|
||||||
const isAudio = contentType.includes('audio');
|
const isAudio = contentType.includes('audio');
|
||||||
const forcePlayer = FORCE_CONTENT_TYPE_PLAYER.includes(contentType);
|
const forcePlayer = FORCE_CONTENT_TYPE_PLAYER.includes(contentType);
|
||||||
|
const previousUri = usePrevious(uri);
|
||||||
|
const embedded = useContext(EmbedContext);
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
const [showAutoplayCountdown, setShowAutoplayCountdown] = useState(false);
|
const [showAutoplayCountdown, setShowAutoplayCountdown] = useState(false);
|
||||||
const [isEndededEmbed, setIsEndededEmbed] = useState(false);
|
const [isEndededEmbed, setIsEndededEmbed] = useState(false);
|
||||||
|
@ -82,9 +84,6 @@ function VideoViewer(props: Props) {
|
||||||
breaks because some browsers (e.g. Firefox) block autoplay but leave the player.play Promise pending */
|
breaks because some browsers (e.g. Firefox) block autoplay but leave the player.play Promise pending */
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const previousUri = usePrevious(uri);
|
|
||||||
const embedded = useContext(EmbedContext);
|
|
||||||
|
|
||||||
// force everything to recent when URI changes, can cause weird corner cases otherwise (e.g. navigate while autoplay is true)
|
// force everything to recent when URI changes, can cause weird corner cases otherwise (e.g. navigate while autoplay is true)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (uri && previousUri && uri !== previousUri) {
|
if (uri && previousUri && uri !== previousUri) {
|
||||||
|
|
|
@ -21,7 +21,9 @@ export default function WebUploadList(props: Props) {
|
||||||
!!uploadCount && (
|
!!uploadCount && (
|
||||||
<Card
|
<Card
|
||||||
title={__('Currently uploading')}
|
title={__('Currently uploading')}
|
||||||
subtitle={uploadCount > 1 ? __('You files are currently uploading.') : __('Your file is currently uploading.')}
|
subtitle={
|
||||||
|
uploadCount > 1 ? __('You videos are currently uploading.') : __('Your video is currently uploading.')
|
||||||
|
}
|
||||||
body={
|
body={
|
||||||
<section>
|
<section>
|
||||||
{/* $FlowFixMe */}
|
{/* $FlowFixMe */}
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
import * as MODALS from 'constants/modal_types';
|
import * as MODALS from 'constants/modal_types';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectLanguage, makeSelectClientSetting } from 'redux/selectors/settings';
|
import { selectLanguage, selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
import { doToast } from 'redux/actions/notifications';
|
import { doToast } from 'redux/actions/notifications';
|
||||||
import { doSearch } from 'redux/actions/search';
|
import { doSearch } from 'redux/actions/search';
|
||||||
import { doOpenModal, doHideModal } from 'redux/actions/app';
|
import { doOpenModal, doHideModal } from 'redux/actions/app';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { doResolveUris, SETTINGS } from 'lbry-redux';
|
import { doResolveUris } from 'lbry-redux';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import Wunderbar from './view';
|
import Wunderbar from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
language: selectLanguage(state),
|
language: selectLanguage(state),
|
||||||
showMature: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
showMature: selectShowMatureContent(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = (dispatch, ownProps) => ({
|
const perform = (dispatch, ownProps) => ({
|
||||||
doResolveUris: uris => dispatch(doResolveUris(uris)),
|
doResolveUris: (uris) => dispatch(doResolveUris(uris)),
|
||||||
doSearch: (query, options) => dispatch(doSearch(query, options)),
|
doSearch: (query, options) => dispatch(doSearch(query, options)),
|
||||||
navigateToSearchPage: query => {
|
navigateToSearchPage: (query) => {
|
||||||
let encodedQuery = encodeURIComponent(query);
|
let encodedQuery = encodeURIComponent(query);
|
||||||
ownProps.history.push({ pathname: `/$/search`, search: `?q=${encodedQuery}` });
|
ownProps.history.push({ pathname: `/$/search`, search: `?q=${encodedQuery}` });
|
||||||
analytics.apiLogSearch();
|
analytics.apiLogSearch();
|
||||||
},
|
},
|
||||||
doShowSnackBar: message => dispatch(doToast({ isError: true, message })),
|
doShowSnackBar: (message) => dispatch(doToast({ isError: true, message })),
|
||||||
doOpenMobileSearch: () => dispatch(doOpenModal(MODALS.MOBILE_SEARCH)),
|
doOpenMobileSearch: () => dispatch(doOpenModal(MODALS.MOBILE_SEARCH)),
|
||||||
doCloseMobileSearch: () => dispatch(doHideModal()),
|
doCloseMobileSearch: () => dispatch(doHideModal()),
|
||||||
});
|
});
|
||||||
|
|
|
@ -119,7 +119,7 @@ export default function YoutubeTransferStatus(props: Props) {
|
||||||
<I18nMessage
|
<I18nMessage
|
||||||
tokens={{ here: <Button button="link" href="https://lbry.com/faq/youtube" label={__('here')} /> }}
|
tokens={{ here: <Button button="link" href="https://lbry.com/faq/youtube" label={__('here')} /> }}
|
||||||
>
|
>
|
||||||
Email help@lbry.com if you think there has been a mistake. Make sure your channel qualifies %here%.
|
Email help@odysee.com if you think there has been a mistake. Make sure your channel qualifies %here%.
|
||||||
</I18nMessage>
|
</I18nMessage>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -3,3 +3,22 @@ export const LINKED_COMMENT_QUERY_PARAM = 'lc';
|
||||||
export const SORT_COMMENTS_NEW = 'new';
|
export const SORT_COMMENTS_NEW = 'new';
|
||||||
export const SORT_COMMENTS_BEST = 'best';
|
export const SORT_COMMENTS_BEST = 'best';
|
||||||
export const SORT_COMMENTS_CONTROVERSIAL = 'controversial';
|
export const SORT_COMMENTS_CONTROVERSIAL = 'controversial';
|
||||||
|
|
||||||
|
export const BANNED_LIVESTREAM_WORDS = [
|
||||||
|
'n1gga',
|
||||||
|
'f4ggot',
|
||||||
|
'faggot',
|
||||||
|
'nigga',
|
||||||
|
'nigger',
|
||||||
|
'F4G',
|
||||||
|
'fag',
|
||||||
|
'n1gger',
|
||||||
|
'faget',
|
||||||
|
'niggah',
|
||||||
|
'n1ggah',
|
||||||
|
'jew',
|
||||||
|
'j3w',
|
||||||
|
'goy',
|
||||||
|
'goyim',
|
||||||
|
'fagot',
|
||||||
|
];
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
export const FF_MAX_CHARS_DEFAULT = 2000;
|
export const FF_MAX_CHARS_DEFAULT = 2000;
|
||||||
export const FF_MAX_CHARS_IN_COMMENT = 2000;
|
export const FF_MAX_CHARS_IN_COMMENT = 2000;
|
||||||
|
export const FF_MAX_CHARS_IN_LIVESTREAM_COMMENT = 300;
|
||||||
export const FF_MAX_CHARS_IN_DESCRIPTION = 5000;
|
export const FF_MAX_CHARS_IN_DESCRIPTION = 5000;
|
||||||
|
|
|
@ -61,3 +61,4 @@ exports.CHANNEL_NEW = 'channel/new';
|
||||||
exports.NOTIFICATIONS = 'notifications';
|
exports.NOTIFICATIONS = 'notifications';
|
||||||
exports.YOUTUBE_SYNC = 'youtube';
|
exports.YOUTUBE_SYNC = 'youtube';
|
||||||
exports.LIVESTREAM = 'livestream';
|
exports.LIVESTREAM = 'livestream';
|
||||||
|
exports.GENERAL = 'general';
|
||||||
|
|
|
@ -40,18 +40,18 @@ class ModalPublishSuccess extends React.PureComponent<Props> {
|
||||||
'Your livestream is now pending. You will be able to start shortly at the streaming dashboard.'
|
'Your livestream is now pending. You will be able to start shortly at the streaming dashboard.'
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
publishMessage = __('Your file is now pending on LBRY. It will take a few minutes to appear for other users.');
|
publishMessage = __('Your video will appear on Odysee shortly.');
|
||||||
}
|
}
|
||||||
|
clearPublish();
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
clearPublish();
|
|
||||||
closeModal();
|
closeModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen type="card" contentLabel={__(contentLabel)} onAborted={handleClose}>
|
<Modal isOpen type="card" contentLabel={__(contentLabel)} onAborted={handleClose}>
|
||||||
<Card
|
<Card
|
||||||
title={__('Success')}
|
title={livestream ? __('Livestream Created') : __('Upload Complete')}
|
||||||
subtitle={publishMessage}
|
subtitle={publishMessage}
|
||||||
body={
|
body={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
selectIsStillEditing,
|
selectIsStillEditing,
|
||||||
selectMyChannelClaims,
|
selectMyChannelClaims,
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
|
doClearPublish,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { selectFfmpegStatus, makeSelectClientSetting } from 'redux/selectors/settings';
|
import { selectFfmpegStatus, makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import { doPublishDesktop } from 'redux/actions/publish';
|
import { doPublishDesktop } from 'redux/actions/publish';
|
||||||
|
@ -16,6 +17,8 @@ const select = (state) => ({
|
||||||
...selectPublishFormValues(state),
|
...selectPublishFormValues(state),
|
||||||
myChannels: selectMyChannelClaims(state),
|
myChannels: selectMyChannelClaims(state),
|
||||||
isVid: makeSelectPublishFormValue('fileVid')(state),
|
isVid: makeSelectPublishFormValue('fileVid')(state),
|
||||||
|
publishSuccess: makeSelectPublishFormValue('publishSuccess')(state),
|
||||||
|
publishing: makeSelectPublishFormValue('publishing')(state),
|
||||||
isStillEditing: selectIsStillEditing(state),
|
isStillEditing: selectIsStillEditing(state),
|
||||||
ffmpegStatus: selectFfmpegStatus(state),
|
ffmpegStatus: selectFfmpegStatus(state),
|
||||||
enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state),
|
enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state),
|
||||||
|
@ -23,6 +26,7 @@ const select = (state) => ({
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
publish: (filePath, preview) => dispatch(doPublishDesktop(filePath, preview)),
|
publish: (filePath, preview) => dispatch(doPublishDesktop(filePath, preview)),
|
||||||
|
clearPublish: () => dispatch(doClearPublish()),
|
||||||
closeModal: () => dispatch(doHideModal()),
|
closeModal: () => dispatch(doHideModal()),
|
||||||
setEnablePublishPreview: (value) => dispatch(doSetClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW, value)),
|
setEnablePublishPreview: (value) => dispatch(doSetClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW, value)),
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,17 +41,63 @@ type Props = {
|
||||||
setEnablePublishPreview: (boolean) => void,
|
setEnablePublishPreview: (boolean) => void,
|
||||||
isStillEditing: boolean,
|
isStillEditing: boolean,
|
||||||
myChannels: ?Array<ChannelClaim>,
|
myChannels: ?Array<ChannelClaim>,
|
||||||
|
publishSuccess: boolean,
|
||||||
|
publishing: boolean,
|
||||||
|
clearPublish: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModalPublishPreview extends React.PureComponent<Props> {
|
// class ModalPublishPreview extends React.PureComponent<Props> {
|
||||||
onConfirmed() {
|
const ModalPublishPreview = (props: Props) => {
|
||||||
const { filePath, publish, closeModal } = this.props;
|
const {
|
||||||
|
filePath,
|
||||||
|
isMarkdownPost,
|
||||||
|
optimize,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
channel,
|
||||||
|
bid,
|
||||||
|
uri,
|
||||||
|
contentIsFree,
|
||||||
|
fee,
|
||||||
|
language,
|
||||||
|
licenseType,
|
||||||
|
otherLicenseDescription,
|
||||||
|
licenseUrl,
|
||||||
|
tags,
|
||||||
|
isVid,
|
||||||
|
ffmpegStatus = {},
|
||||||
|
previewResponse,
|
||||||
|
enablePublishPreview,
|
||||||
|
setEnablePublishPreview,
|
||||||
|
isStillEditing,
|
||||||
|
myChannels,
|
||||||
|
publishSuccess,
|
||||||
|
publishing,
|
||||||
|
publish,
|
||||||
|
closeModal,
|
||||||
|
clearPublish,
|
||||||
|
} = props;
|
||||||
|
const livestream =
|
||||||
|
// $FlowFixMe
|
||||||
|
previewResponse.outputs[0] && previewResponse.outputs[0].value && !previewResponse.outputs[0].value.source;
|
||||||
|
// @if TARGET='web'
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (publishing && !livestream) {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
if (publishSuccess && livestream) {
|
||||||
|
clearPublish();
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
}, [publishSuccess, publishing, livestream]);
|
||||||
|
// @endif
|
||||||
|
// const waitForSuccess = false;
|
||||||
|
function onConfirmed() {
|
||||||
// Publish for real:
|
// Publish for real:
|
||||||
publish(this.resolveFilePathName(filePath), false);
|
publish(getFilePathName(filePath), false);
|
||||||
closeModal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveFilePathName(filePath: string | WebFile) {
|
function getFilePathName(filePath: string | WebFile) {
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
return NO_FILE;
|
return NO_FILE;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +109,7 @@ class ModalPublishPreview extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createRow(label: string, value: any) {
|
function createRow(label: string, value: any) {
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{label}</td>
|
<td>{label}</td>
|
||||||
|
@ -72,54 +118,20 @@ class ModalPublishPreview extends React.PureComponent<Props> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
togglePreviewEnabled() {
|
const txFee = previewResponse ? previewResponse['total_fee'] : null;
|
||||||
const { enablePublishPreview, setEnablePublishPreview } = this.props;
|
// $FlowFixMe add outputs[0] etc to PublishResponse type
|
||||||
setEnablePublishPreview(!enablePublishPreview);
|
const isOptimizeAvail = filePath && filePath !== '' && isVid && ffmpegStatus.available;
|
||||||
|
let modalTitle;
|
||||||
|
if (isStillEditing) {
|
||||||
|
modalTitle = __('Confirm Edit');
|
||||||
|
} else if (livestream) {
|
||||||
|
modalTitle = __('Create Livestream');
|
||||||
|
} else {
|
||||||
|
modalTitle = __('Confirm Upload');
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
let confirmBtnText;
|
||||||
const {
|
if (!publishing) {
|
||||||
filePath,
|
|
||||||
isMarkdownPost,
|
|
||||||
optimize,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
channel,
|
|
||||||
bid,
|
|
||||||
uri,
|
|
||||||
contentIsFree,
|
|
||||||
fee,
|
|
||||||
language,
|
|
||||||
licenseType,
|
|
||||||
otherLicenseDescription,
|
|
||||||
licenseUrl,
|
|
||||||
tags,
|
|
||||||
isVid,
|
|
||||||
ffmpegStatus = {},
|
|
||||||
previewResponse,
|
|
||||||
closeModal,
|
|
||||||
enablePublishPreview,
|
|
||||||
setEnablePublishPreview,
|
|
||||||
isStillEditing,
|
|
||||||
myChannels,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const txFee = previewResponse ? previewResponse['total_fee'] : null;
|
|
||||||
// $FlowFixMe add outputs[0] etc to PublishResponse type
|
|
||||||
const livestream =
|
|
||||||
// $FlowFixMe
|
|
||||||
previewResponse.outputs[0] && previewResponse.outputs[0].value && !previewResponse.outputs[0].value.source;
|
|
||||||
const isOptimizeAvail = filePath && filePath !== '' && isVid && ffmpegStatus.available;
|
|
||||||
let modalTitle;
|
|
||||||
if (isStillEditing) {
|
|
||||||
modalTitle = __('Confirm Edit');
|
|
||||||
} else if (livestream) {
|
|
||||||
modalTitle = __('Create Livestream');
|
|
||||||
} else {
|
|
||||||
modalTitle = __('Confirm Upload');
|
|
||||||
}
|
|
||||||
|
|
||||||
let confirmBtnText;
|
|
||||||
if (isStillEditing) {
|
if (isStillEditing) {
|
||||||
confirmBtnText = __('Save');
|
confirmBtnText = __('Save');
|
||||||
} else if (livestream) {
|
} else if (livestream) {
|
||||||
|
@ -127,110 +139,119 @@ class ModalPublishPreview extends React.PureComponent<Props> {
|
||||||
} else {
|
} else {
|
||||||
confirmBtnText = __('Upload');
|
confirmBtnText = __('Upload');
|
||||||
}
|
}
|
||||||
const descriptionValue = description ? (
|
} else {
|
||||||
<div className="media__info-text-preview">
|
if (isStillEditing) {
|
||||||
<MarkdownPreview content={description} simpleLinks />
|
confirmBtnText = __('Saving');
|
||||||
</div>
|
} else if (livestream) {
|
||||||
) : null;
|
confirmBtnText = __('Creating');
|
||||||
|
} else {
|
||||||
const licenseValue =
|
confirmBtnText = __('Uploading');
|
||||||
licenseType === COPYRIGHT ? (
|
|
||||||
<p>© {otherLicenseDescription}</p>
|
|
||||||
) : licenseType === OTHER ? (
|
|
||||||
<p>
|
|
||||||
{otherLicenseDescription}
|
|
||||||
<br />
|
|
||||||
{licenseUrl}
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<p>{licenseType}</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
const tagsValue =
|
|
||||||
// Do nothing for onClick(). Setting to 'null' results in "View Tag" action -- we don't want to leave the modal.
|
|
||||||
tags.map((tag) => <Tag key={tag.name} title={tag.name} name={tag.name} type={'flow'} onClick={() => {}} />);
|
|
||||||
|
|
||||||
const depositValue = bid ? <LbcSymbol postfix={`${bid}`} size={14} /> : <p>---</p>;
|
|
||||||
|
|
||||||
let priceValue = __('Free');
|
|
||||||
if (!contentIsFree) {
|
|
||||||
if (fee.currency === 'LBC') {
|
|
||||||
priceValue = <LbcSymbol postfix={fee.amount} />;
|
|
||||||
} else {
|
|
||||||
priceValue = `${fee.amount} ${fee.currency}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const channelValue = (channel) => {
|
|
||||||
const channelClaim = myChannels && myChannels.find((x) => x.name === channel);
|
|
||||||
return channel ? (
|
|
||||||
<div className="channel-value">
|
|
||||||
{channelClaim && <ChannelThumbnail uri={channelClaim.permanent_url} />}
|
|
||||||
{channel}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="channel-value">
|
|
||||||
<Icon sectionIcon icon={ICONS.ANONYMOUS} />
|
|
||||||
<i>{__('Anonymous')}</i>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal isOpen contentLabel={modalTitle} type="card" onAborted={closeModal}>
|
|
||||||
<Form onSubmit={() => this.onConfirmed()}>
|
|
||||||
<Card
|
|
||||||
title={modalTitle}
|
|
||||||
body={
|
|
||||||
<>
|
|
||||||
<div className="section">
|
|
||||||
<table className="table table--condensed table--publish-preview">
|
|
||||||
<tbody>
|
|
||||||
{!livestream && !isMarkdownPost && this.createRow(__('File'), this.resolveFilePathName(filePath))}
|
|
||||||
{isOptimizeAvail && this.createRow(__('Transcode'), optimize ? __('Yes') : __('No'))}
|
|
||||||
{this.createRow(__('Title'), title)}
|
|
||||||
{this.createRow(__('Description'), descriptionValue)}
|
|
||||||
{this.createRow(__('Channel'), channelValue(channel))}
|
|
||||||
{this.createRow(__('URL'), uri)}
|
|
||||||
{this.createRow(__('Deposit'), depositValue)}
|
|
||||||
{this.createRow(__('Price'), priceValue)}
|
|
||||||
{this.createRow(__('Language'), language)}
|
|
||||||
{this.createRow(__('License'), licenseValue)}
|
|
||||||
{this.createRow(__('Tags'), tagsValue)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{txFee && (
|
|
||||||
<div className="section" aria-label={__('Estimated transaction fee:')}>
|
|
||||||
<b>{__('Est. transaction fee:')}</b>
|
|
||||||
<em>
|
|
||||||
<LbcSymbol postfix={txFee} />
|
|
||||||
</em>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
actions={
|
|
||||||
<>
|
|
||||||
<div className="section__actions">
|
|
||||||
<Button autoFocus button="primary" label={confirmBtnText} onClick={() => this.onConfirmed()} />
|
|
||||||
<Button button="link" label={__('Cancel')} onClick={closeModal} />
|
|
||||||
</div>
|
|
||||||
<p className="help">{__('Once the transaction is sent, it cannot be reversed.')}</p>
|
|
||||||
<FormField
|
|
||||||
type="checkbox"
|
|
||||||
name="sync_toggle"
|
|
||||||
label={__('Skip preview and confirmation')}
|
|
||||||
checked={!enablePublishPreview}
|
|
||||||
onChange={() => setEnablePublishPreview(!enablePublishPreview)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
const descriptionValue = description ? (
|
||||||
|
<div className="media__info-text-preview">
|
||||||
|
<MarkdownPreview content={description} simpleLinks />
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const licenseValue =
|
||||||
|
licenseType === COPYRIGHT ? (
|
||||||
|
<p>© {otherLicenseDescription}</p>
|
||||||
|
) : licenseType === OTHER ? (
|
||||||
|
<p>
|
||||||
|
{otherLicenseDescription}
|
||||||
|
<br />
|
||||||
|
{licenseUrl}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p>{licenseType}</p>
|
||||||
|
);
|
||||||
|
|
||||||
|
const tagsValue =
|
||||||
|
// Do nothing for onClick(). Setting to 'null' results in "View Tag" action -- we don't want to leave the modal.
|
||||||
|
tags.map((tag) => <Tag key={tag.name} title={tag.name} name={tag.name} type={'flow'} onClick={() => {}} />);
|
||||||
|
|
||||||
|
const depositValue = bid ? <LbcSymbol postfix={`${bid}`} size={14} /> : <p>---</p>;
|
||||||
|
|
||||||
|
let priceValue = __('Free');
|
||||||
|
if (!contentIsFree) {
|
||||||
|
if (fee.currency === 'LBC') {
|
||||||
|
priceValue = <LbcSymbol postfix={fee.amount} />;
|
||||||
|
} else {
|
||||||
|
priceValue = `${fee.amount} ${fee.currency}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelValue = (channel) => {
|
||||||
|
const channelClaim = myChannels && myChannels.find((x) => x.name === channel);
|
||||||
|
return channel ? (
|
||||||
|
<div className="channel-value">
|
||||||
|
{channelClaim && <ChannelThumbnail uri={channelClaim.permanent_url} />}
|
||||||
|
{channel}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="channel-value">
|
||||||
|
<Icon sectionIcon icon={ICONS.ANONYMOUS} />
|
||||||
|
<i>{__('Anonymous')}</i>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen contentLabel={modalTitle} type="card" onAborted={closeModal}>
|
||||||
|
<Form onSubmit={onConfirmed}>
|
||||||
|
<Card
|
||||||
|
title={modalTitle}
|
||||||
|
body={
|
||||||
|
<>
|
||||||
|
<div className="section">
|
||||||
|
<table className="table table--condensed table--publish-preview">
|
||||||
|
<tbody>
|
||||||
|
{!livestream && !isMarkdownPost && createRow(__('File'), getFilePathName(filePath))}
|
||||||
|
{isOptimizeAvail && createRow(__('Transcode'), optimize ? __('Yes') : __('No'))}
|
||||||
|
{createRow(__('Title'), title)}
|
||||||
|
{createRow(__('Description'), descriptionValue)}
|
||||||
|
{createRow(__('Channel'), channelValue(channel))}
|
||||||
|
{createRow(__('URL'), uri)}
|
||||||
|
{createRow(__('Deposit'), depositValue)}
|
||||||
|
{createRow(__('Price'), priceValue)}
|
||||||
|
{createRow(__('Language'), language)}
|
||||||
|
{createRow(__('License'), licenseValue)}
|
||||||
|
{createRow(__('Tags'), tagsValue)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{txFee && (
|
||||||
|
<div className="section" aria-label={__('Estimated transaction fee:')}>
|
||||||
|
<b>{__('Est. transaction fee:')}</b>
|
||||||
|
<em>
|
||||||
|
<LbcSymbol postfix={txFee} />
|
||||||
|
</em>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
<>
|
||||||
|
<div className="section__actions">
|
||||||
|
<Button autoFocus button="primary" disabled={publishing} label={confirmBtnText} onClick={onConfirmed} />
|
||||||
|
<Button button="link" label={__('Cancel')} onClick={closeModal} />
|
||||||
|
</div>
|
||||||
|
<p className="help">{__('Once the transaction is sent, it cannot be reversed.')}</p>
|
||||||
|
<FormField
|
||||||
|
type="checkbox"
|
||||||
|
name="sync_toggle"
|
||||||
|
label={__('Skip preview and confirmation')}
|
||||||
|
checked={!enablePublishPreview}
|
||||||
|
onChange={() => setEnablePublishPreview(!enablePublishPreview)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default ModalPublishPreview;
|
export default ModalPublishPreview;
|
||||||
|
|
|
@ -52,10 +52,8 @@ function ModalRemoveFile(props: Props) {
|
||||||
<FormField
|
<FormField
|
||||||
name="claim_abandon"
|
name="claim_abandon"
|
||||||
label={
|
label={
|
||||||
<I18nMessage
|
<I18nMessage tokens={{ lbc: <LbcSymbol postfix={claim.amount} /> }}>
|
||||||
tokens={{ lbc: <LbcSymbol prefix={__('reclaim %amount%', { amount: claim.amount })} /> }}
|
Abandon on blockchain (reclaim %lbc%)
|
||||||
>
|
|
||||||
Abandon on blockchain (%lbc%)
|
|
||||||
</I18nMessage>
|
</I18nMessage>
|
||||||
}
|
}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
|
|
@ -12,7 +12,7 @@ class ModalTransactionFailed extends React.PureComponent<Props> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen contentLabel={__('Transaction failed')} title={__('Transaction failed')} onConfirmed={closeModal}>
|
<Modal isOpen contentLabel={__('Transaction failed')} title={__('Transaction failed')} onConfirmed={closeModal}>
|
||||||
<p>{__('Sorry about that. Contact help@lbry.com if you continue to have issues.')}</p>
|
<p>{__('Sorry about that. Contact help@odysee.com if you continue to have issues.')}</p>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import { SIMPLE_SITE } from 'config';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Modal } from 'modal/modal';
|
import { Modal } from 'modal/modal';
|
||||||
|
@ -15,18 +16,27 @@ const YoutubeWelcome = (props: Props) => {
|
||||||
<Modal isOpen type="card" onAborted={doHideModal}>
|
<Modal isOpen type="card" onAborted={doHideModal}>
|
||||||
<Confetti recycle={false} style={{ position: 'fixed' }} numberOfPieces={100} />
|
<Confetti recycle={false} style={{ position: 'fixed' }} numberOfPieces={100} />
|
||||||
<Card
|
<Card
|
||||||
title={__("You're free!")}
|
title={!SIMPLE_SITE ? __("You're free!") : __('Welcome to Odysee')}
|
||||||
subtitle={
|
subtitle={
|
||||||
<React.Fragment>
|
!SIMPLE_SITE ? (
|
||||||
<p>
|
<React.Fragment>
|
||||||
{__("You've escaped the land of spying, censorship, and exploitation.")}
|
<p>
|
||||||
<span className="emoji"> 💩</span>
|
{__("You've escaped the land of spying, censorship, and exploitation.")}
|
||||||
</p>
|
<span className="emoji"> 💩</span>
|
||||||
<p>
|
</p>
|
||||||
{__('Welcome to the land of content freedom.')}
|
<p>
|
||||||
<span className="emoji"> 🌈</span>
|
{__('Welcome to the land of content freedom.')}
|
||||||
</p>
|
<span className="emoji"> 🌈</span>
|
||||||
</React.Fragment>
|
</p>
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<React.Fragment>
|
||||||
|
<p>
|
||||||
|
{__('You make the party extra special!')}
|
||||||
|
<span className="emoji"> 💖</span>
|
||||||
|
</p>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
|
|
12
ui/page/adsTest/index.js
Normal file
12
ui/page/adsTest/index.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { makeSelectClaimForUri, doResolveUri } from 'lbry-redux';
|
||||||
|
import AdsTestPage from './view';
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
(state) => ({
|
||||||
|
claim: makeSelectClaimForUri('lbry://fullscreenrelease#7')(state),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
doResolveUri,
|
||||||
|
}
|
||||||
|
)(AdsTestPage);
|
20
ui/page/adsTest/view.jsx
Normal file
20
ui/page/adsTest/view.jsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// @flow
|
||||||
|
// import React from 'react';
|
||||||
|
// import FilePage from 'page/file';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
doResolveUri: (string) => void,
|
||||||
|
claim: ?StreamClaim,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AdsTestPage(props: Props) {
|
||||||
|
return null;
|
||||||
|
// const { doResolveUri, claim } = props;
|
||||||
|
// const hasClaim = claim !== undefined;
|
||||||
|
// React.useEffect(() => {
|
||||||
|
// if (!hasClaim) {
|
||||||
|
// doResolveUri('lbry://fullscreenrelease#7');
|
||||||
|
// }
|
||||||
|
// }, [hasClaim, doResolveUri]);
|
||||||
|
// return <div>{hasClaim && <FilePage uri="lbry://fullscreenrelease#7" />}</div>;
|
||||||
|
}
|
|
@ -18,7 +18,8 @@ const MOONPAY_KEY = process.env.MOONPAY_SECRET_KEY;
|
||||||
const COUNTRIES = Array.from(
|
const COUNTRIES = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
countryData.all
|
countryData.all
|
||||||
.map(country => country.name)
|
.filter((country) => country.status !== 'deleted')
|
||||||
|
.map((country) => country.name)
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (a > b) {
|
if (a > b) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -39,7 +40,7 @@ type Props = {
|
||||||
email: string,
|
email: string,
|
||||||
user: ?User,
|
user: ?User,
|
||||||
doGetNewAddress: () => void,
|
doGetNewAddress: () => void,
|
||||||
doUserSetCountry: string => void,
|
doUserSetCountry: (string) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function BuyPage(props: Props) {
|
export default function BuyPage(props: Props) {
|
||||||
|
@ -65,7 +66,7 @@ export default function BuyPage(props: Props) {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (MOONPAY_KEY && !url && receiveAddress) {
|
if (MOONPAY_KEY && !url && receiveAddress) {
|
||||||
let url = `https://buy.moonpay.io?apiKey=pk_live_xNFffrN5NWKy6fu0ggbV8VQIwRieRzy&colorCode=%23257761¤cyCode=lbc&showWalletAddressForm=true&walletAddress=${receiveAddress}`;
|
let url = `https://buy.moonpay.io?apiKey=pk_live_xNFffrN5NWKy6fu0ggbV8VQIwRieRzy&colorCode=%23fa6165¤cyCode=lbc&showWalletAddressForm=true&walletAddress=${receiveAddress}`;
|
||||||
if (email) {
|
if (email) {
|
||||||
url += `&email=${encodeURIComponent(email)}`;
|
url += `&email=${encodeURIComponent(email)}`;
|
||||||
}
|
}
|
||||||
|
@ -148,7 +149,7 @@ export default function BuyPage(props: Props) {
|
||||||
'Only some countries are eligible at this time. We are working to make this available to everyone.'
|
'Only some countries are eligible at this time. We are working to make this available to everyone.'
|
||||||
)}
|
)}
|
||||||
value={country}
|
value={country}
|
||||||
onChange={e => setCountry(e.target.value)}
|
onChange={(e) => setCountry(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="" disabled defaultValue>
|
<option value="" disabled defaultValue>
|
||||||
{__('Select your country')}
|
{__('Select your country')}
|
||||||
|
|
|
@ -109,7 +109,7 @@ export default function ChannelsPage(props: Props) {
|
||||||
subtitle={__("You haven't created a channel yet. All of your beautiful channels will be listed here!")}
|
subtitle={__("You haven't created a channel yet. All of your beautiful channels will be listed here!")}
|
||||||
actions={
|
actions={
|
||||||
<div className="section__actions">
|
<div className="section__actions">
|
||||||
<Button button="primary" label={__('New Channel')} navigate={`/$/${PAGES.CHANNEL_NEW}`} />
|
<Button button="primary" label={__('Create Channel')} navigate={`/$/${PAGES.CHANNEL_NEW}`} />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -23,6 +23,7 @@ function ChannelsFollowingPage(props: Props) {
|
||||||
) : (
|
) : (
|
||||||
<Page noFooter fullWidthPage={tileLayout}>
|
<Page noFooter fullWidthPage={tileLayout}>
|
||||||
<ClaimListDiscover
|
<ClaimListDiscover
|
||||||
|
hideAdvancedFilter
|
||||||
tileLayout={tileLayout}
|
tileLayout={tileLayout}
|
||||||
headerLabel={
|
headerLabel={
|
||||||
<span>
|
<span>
|
||||||
|
@ -31,7 +32,7 @@ function ChannelsFollowingPage(props: Props) {
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
defaultOrderBy={ORDER_BY_NEW}
|
defaultOrderBy={ORDER_BY_NEW}
|
||||||
channelIds={subscribedChannels.map(sub => sub.uri.split('#')[1])}
|
channelIds={subscribedChannels.map((sub) => sub.uri.split('#')[1])}
|
||||||
meta={
|
meta={
|
||||||
<Button
|
<Button
|
||||||
icon={ICONS.SEARCH}
|
icon={ICONS.SEARCH}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as ICONS from 'constants/icons';
|
// import * as ICONS from 'constants/icons';
|
||||||
import * as PAGES from 'constants/pages';
|
// import * as PAGES from 'constants/pages';
|
||||||
|
import * as CS from 'constants/claim_search';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import Button from 'component/button';
|
// import Button from 'component/button';
|
||||||
import ClaimTilesDiscover from 'component/claimTilesDiscover';
|
|
||||||
import ClaimListDiscover from 'component/claimListDiscover';
|
import ClaimListDiscover from 'component/claimListDiscover';
|
||||||
import * as CS from 'constants/claim_search';
|
// import { toCapitalCase } from 'util/string';
|
||||||
import { toCapitalCase } from 'util/string';
|
|
||||||
import { SIMPLE_SITE } from 'config';
|
import { SIMPLE_SITE } from 'config';
|
||||||
|
|
||||||
const MORE_CHANNELS_ANCHOR = 'MoreChannels';
|
const MORE_CHANNELS_ANCHOR = 'MoreChannels';
|
||||||
|
@ -19,89 +18,87 @@ type Props = {
|
||||||
homepageData: any,
|
homepageData: any,
|
||||||
};
|
};
|
||||||
|
|
||||||
type ChannelsFollowingItem = {
|
|
||||||
title: string,
|
|
||||||
link?: string,
|
|
||||||
help?: any,
|
|
||||||
options?: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
function ChannelsFollowingDiscover(props: Props) {
|
function ChannelsFollowingDiscover(props: Props) {
|
||||||
const { followedTags, subscribedChannels, blockedChannels, homepageData } = props;
|
const {
|
||||||
|
// followedTags,
|
||||||
|
// subscribedChannels,
|
||||||
|
// blockedChannels,
|
||||||
|
homepageData,
|
||||||
|
} = props;
|
||||||
const { PRIMARY_CONTENT_CHANNEL_IDS } = homepageData;
|
const { PRIMARY_CONTENT_CHANNEL_IDS } = homepageData;
|
||||||
let rowData: Array<ChannelsFollowingItem> = [];
|
// let rowData: Array<ChannelsFollowingItem> = [];
|
||||||
const notChannels = subscribedChannels
|
// const notChannels = subscribedChannels
|
||||||
.map(({ uri }) => uri)
|
// .map(({ uri }) => uri)
|
||||||
.concat(blockedChannels)
|
// .concat(blockedChannels)
|
||||||
.map(uri => uri.split('#')[1]);
|
// .map(uri => uri.split('#')[1]);
|
||||||
|
|
||||||
rowData.push({
|
// rowData.push({
|
||||||
title: 'Top Channels Of All Time',
|
// title: 'Top Channels Of All Time',
|
||||||
link: `/$/${PAGES.DISCOVER}?claim_type=channel&${CS.ORDER_BY_KEY}=${CS.ORDER_BY_TOP}&${CS.FRESH_KEY}=${CS.FRESH_ALL}`,
|
// link: `/$/${PAGES.DISCOVER}?claim_type=channel&${CS.ORDER_BY_KEY}=${CS.ORDER_BY_TOP}&${CS.FRESH_KEY}=${CS.FRESH_ALL}`,
|
||||||
options: {
|
// options: {
|
||||||
pageSize: 12,
|
// pageSize: 12,
|
||||||
claimType: 'channel',
|
// claimType: 'channel',
|
||||||
orderBy: ['effective_amount'],
|
// orderBy: ['effective_amount'],
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
rowData.push({
|
// rowData.push({
|
||||||
title: 'Latest From @lbrycast',
|
// title: 'Latest From @lbrycast',
|
||||||
link: `/@lbrycast:4`,
|
// link: `/@lbrycast:4`,
|
||||||
options: {
|
// options: {
|
||||||
orderBy: ['release_time'],
|
// orderBy: ['release_time'],
|
||||||
pageSize: 8,
|
// pageSize: 8,
|
||||||
channelIds: ['4c29f8b013adea4d5cca1861fb2161d5089613ea'],
|
// channelIds: ['4c29f8b013adea4d5cca1861fb2161d5089613ea'],
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
rowData.push({
|
// rowData.push({
|
||||||
title: 'Trending Channels',
|
// title: 'Trending Channels',
|
||||||
link: `/$/${PAGES.DISCOVER}?claim_type=channel`,
|
// link: `/$/${PAGES.DISCOVER}?claim_type=channel`,
|
||||||
options: {
|
// options: {
|
||||||
pageSize: 8,
|
// pageSize: 8,
|
||||||
claimType: 'channel',
|
// claimType: 'channel',
|
||||||
orderBy: ['trending_group', 'trending_mixed'],
|
// orderBy: ['trending_group', 'trending_mixed'],
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (followedTags.length > 0 && followedTags.length < 5) {
|
// if (followedTags.length > 0 && followedTags.length < 5) {
|
||||||
const followedRows = followedTags.map((tag: Tag) => ({
|
// const followedRows = followedTags.map((tag: Tag) => ({
|
||||||
title: `Trending Channels for #${toCapitalCase(tag.name)}`,
|
// title: `Trending Channels for #${toCapitalCase(tag.name)}`,
|
||||||
link: `/$/${PAGES.DISCOVER}?t=${tag.name}&claim_type=channel`,
|
// link: `/$/${PAGES.DISCOVER}?t=${tag.name}&claim_type=channel`,
|
||||||
options: {
|
// options: {
|
||||||
claimType: 'channel',
|
// claimType: 'channel',
|
||||||
pageSize: 4,
|
// pageSize: 4,
|
||||||
tags: [tag.name],
|
// tags: [tag.name],
|
||||||
},
|
// },
|
||||||
}));
|
// }));
|
||||||
rowData.push(...followedRows);
|
// rowData.push(...followedRows);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (followedTags.length > 4) {
|
// if (followedTags.length > 4) {
|
||||||
rowData.push({
|
// rowData.push({
|
||||||
title: 'Trending For Your Tags',
|
// title: 'Trending For Your Tags',
|
||||||
link: `/$/${PAGES.TAGS_FOLLOWING}?claim_type=channel`,
|
// link: `/$/${PAGES.TAGS_FOLLOWING}?claim_type=channel`,
|
||||||
options: {
|
// options: {
|
||||||
claimType: 'channel',
|
// claimType: 'channel',
|
||||||
tags: followedTags.map(tag => tag.name),
|
// tags: followedTags.map(tag => tag.name),
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
const rowDataWithGenericOptions = rowData.map(row => {
|
// const rowDataWithGenericOptions = rowData.map(row => {
|
||||||
return {
|
// return {
|
||||||
...row,
|
// ...row,
|
||||||
options: {
|
// options: {
|
||||||
...row.options,
|
// ...row.options,
|
||||||
notChannels,
|
// notChannels,
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
});
|
// });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
{rowDataWithGenericOptions.map(({ title, link, help, options = {} }) => (
|
{/* {rowDataWithGenericOptions.map(({ title, link, help, options = {} }) => (
|
||||||
<div key={title} className="claim-grid__wrapper">
|
<div key={title} className="claim-grid__wrapper">
|
||||||
<h1 className="section__actions">
|
<h1 className="section__actions">
|
||||||
{link ? (
|
{link ? (
|
||||||
|
@ -120,17 +117,17 @@ function ChannelsFollowingDiscover(props: Props) {
|
||||||
|
|
||||||
<ClaimTilesDiscover {...options} />
|
<ClaimTilesDiscover {...options} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))} */}
|
||||||
<h1 id={MORE_CHANNELS_ANCHOR} className="claim-grid__title">
|
|
||||||
{__('More Channels')}
|
|
||||||
</h1>
|
|
||||||
{/* odysee: claimIds = PRIMARY_CONTENT_CHANNEL_IDS if simplesite CLD */}
|
|
||||||
<ClaimListDiscover
|
<ClaimListDiscover
|
||||||
defaultOrderBy={CS.ORDER_BY_TRENDING}
|
defaultOrderBy={CS.ORDER_BY_TRENDING}
|
||||||
defaultFreshness={CS.FRESH_ALL}
|
defaultFreshness={CS.FRESH_ALL}
|
||||||
claimType={CS.CLAIM_CHANNEL}
|
claimType={CS.CLAIM_CHANNEL}
|
||||||
claimIds={SIMPLE_SITE ? PRIMARY_CONTENT_CHANNEL_IDS : undefined}
|
claimIds={SIMPLE_SITE ? PRIMARY_CONTENT_CHANNEL_IDS : undefined}
|
||||||
scrollAnchor={MORE_CHANNELS_ANCHOR}
|
scrollAnchor={MORE_CHANNELS_ANCHOR}
|
||||||
|
maxPages={3}
|
||||||
|
hideFilters
|
||||||
|
header={<h1 className="section__title">{__('Moon cheese is an acquired taste')}</h1>}
|
||||||
/>
|
/>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { SHOW_ADS, DOMAIN, SIMPLE_SITE } from 'config';
|
import { SHOW_ADS, DOMAIN, SIMPLE_SITE } from 'config';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
|
import * as CS from 'constants/claim_search';
|
||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import ClaimListDiscover from 'component/claimListDiscover';
|
import ClaimListDiscover from 'component/claimListDiscover';
|
||||||
|
@ -10,18 +11,18 @@ import { useIsMobile } from 'effects/use-screensize';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import HiddenNsfw from 'component/common/hidden-nsfw';
|
import HiddenNsfw from 'component/common/hidden-nsfw';
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
import * as CS from 'constants/claim_search';
|
|
||||||
import Ads from 'web/component/ads';
|
import Ads from 'web/component/ads';
|
||||||
import LbcSymbol from 'component/common/lbc-symbol';
|
import LbcSymbol from 'component/common/lbc-symbol';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
location: { search: string },
|
location: { search: string },
|
||||||
followedTags: Array<Tag>,
|
followedTags: Array<Tag>,
|
||||||
repostedUri: string,
|
repostedUri: string,
|
||||||
repostedClaim: ?GenericClaim,
|
repostedClaim: ?GenericClaim,
|
||||||
doToggleTagFollowDesktop: string => void,
|
doToggleTagFollowDesktop: (string) => void,
|
||||||
doResolveUri: string => void,
|
doResolveUri: (string) => void,
|
||||||
isAuthenticated: boolean,
|
isAuthenticated: boolean,
|
||||||
dynamicRouteProps: RowDataItem,
|
dynamicRouteProps: RowDataItem,
|
||||||
tileLayout: boolean,
|
tileLayout: boolean,
|
||||||
|
@ -87,8 +88,8 @@ function DiscoverPage(props: Props) {
|
||||||
} else {
|
} else {
|
||||||
headerLabel = (
|
headerLabel = (
|
||||||
<span>
|
<span>
|
||||||
<Icon icon={(dynamicRouteProps && dynamicRouteProps.icon) || ICONS.DISCOVER} size={10} />
|
<Icon icon={(dynamicRouteProps && dynamicRouteProps.icon) || ICONS.WILD_WEST} size={10} />
|
||||||
{(dynamicRouteProps && dynamicRouteProps.title) || __('All Content')}
|
{(dynamicRouteProps && dynamicRouteProps.title) || __('Wild West')}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -96,9 +97,11 @@ function DiscoverPage(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Page noFooter fullWidthPage={tileLayout}>
|
<Page noFooter fullWidthPage={tileLayout}>
|
||||||
<ClaimListDiscover
|
<ClaimListDiscover
|
||||||
limitClaimsPerChannel={3}
|
hideAdvancedFilter
|
||||||
|
hideFilters={!dynamicRouteProps}
|
||||||
header={repostedUri ? <span /> : undefined}
|
header={repostedUri ? <span /> : undefined}
|
||||||
tileLayout={repostedUri ? false : tileLayout}
|
tileLayout={repostedUri ? false : tileLayout}
|
||||||
|
defaultOrderBy={dynamicRouteProps ? undefined : CS.ORDER_BY_TRENDING}
|
||||||
claimType={claimType ? [claimType] : undefined}
|
claimType={claimType ? [claimType] : undefined}
|
||||||
headerLabel={headerLabel}
|
headerLabel={headerLabel}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
|
@ -107,9 +110,18 @@ function DiscoverPage(props: Props) {
|
||||||
injectedItem={
|
injectedItem={
|
||||||
SHOW_ADS && IS_WEB ? (SIMPLE_SITE ? false : !isAuthenticated && <Ads small type={'video'} />) : false
|
SHOW_ADS && IS_WEB ? (SIMPLE_SITE ? false : !isAuthenticated && <Ads small type={'video'} />) : false
|
||||||
}
|
}
|
||||||
|
// Assume wild west page if no dynamicRouteProps
|
||||||
|
// Not a very good solution, but just doing it for now
|
||||||
|
// until we are sure this page will stay around
|
||||||
|
releaseTime={!dynamicRouteProps && `>${Math.floor(moment().subtract(1, 'day').startOf('week').unix())}`}
|
||||||
|
feeAmount={!dynamicRouteProps && CS.FEE_AMOUNT_ANY}
|
||||||
channelIds={
|
channelIds={
|
||||||
(dynamicRouteProps && dynamicRouteProps.options && dynamicRouteProps.options.channelIds) || undefined
|
(dynamicRouteProps && dynamicRouteProps.options && dynamicRouteProps.options.channelIds) || undefined
|
||||||
}
|
}
|
||||||
|
limitClaimsPerChannel={
|
||||||
|
(dynamicRouteProps && dynamicRouteProps.options && dynamicRouteProps.options.limitClaimsPerChannel) ||
|
||||||
|
undefined
|
||||||
|
}
|
||||||
meta={
|
meta={
|
||||||
!dynamicRouteProps ? (
|
!dynamicRouteProps ? (
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -8,6 +8,8 @@ import {
|
||||||
makeSelectClaimIsNsfw,
|
makeSelectClaimIsNsfw,
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
makeSelectTagInClaimOrChannelForUri,
|
makeSelectTagInClaimOrChannelForUri,
|
||||||
|
makeSelectClaimIsMine,
|
||||||
|
makeSelectClaimIsStreamPlaceholder,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { makeSelectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc';
|
import { makeSelectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc';
|
||||||
import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings';
|
import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
|
@ -32,14 +34,16 @@ const select = (state, props) => {
|
||||||
renderMode: makeSelectFileRenderModeForUri(props.uri)(state),
|
renderMode: makeSelectFileRenderModeForUri(props.uri)(state),
|
||||||
videoTheaterMode: makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)(state),
|
videoTheaterMode: makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)(state),
|
||||||
commentsDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state),
|
commentsDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state),
|
||||||
|
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||||
|
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = (dispatch) => ({
|
||||||
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
|
fetchFileInfo: (uri) => dispatch(doFetchFileInfo(uri)),
|
||||||
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
fetchCostInfo: (uri) => dispatch(doFetchCostInfoForUri(uri)),
|
||||||
setViewed: uri => dispatch(doSetContentHistoryItem(uri)),
|
setViewed: (uri) => dispatch(doSetContentHistoryItem(uri)),
|
||||||
setPrimaryUri: uri => dispatch(doSetPrimaryUri(uri)),
|
setPrimaryUri: (uri) => dispatch(doSetPrimaryUri(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withRouter(connect(select, perform)(FilePage));
|
export default withRouter(connect(select, perform)(FilePage));
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import * as PAGES from 'constants/pages';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
|
@ -10,6 +11,9 @@ import FileRenderDownload from 'component/fileRenderDownload';
|
||||||
import RecommendedContent from 'component/recommendedContent';
|
import RecommendedContent from 'component/recommendedContent';
|
||||||
import CommentsList from 'component/commentsList';
|
import CommentsList from 'component/commentsList';
|
||||||
import PostViewer from 'component/postViewer';
|
import PostViewer from 'component/postViewer';
|
||||||
|
import { Redirect } from 'react-router';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import Empty from 'component/common/empty';
|
import Empty from 'component/common/empty';
|
||||||
|
|
||||||
export const PRIMARY_PLAYER_WRAPPER_CLASS = 'file-page__video-container';
|
export const PRIMARY_PLAYER_WRAPPER_CLASS = 'file-page__video-container';
|
||||||
|
@ -27,7 +31,10 @@ type Props = {
|
||||||
linkedComment: any,
|
linkedComment: any,
|
||||||
setPrimaryUri: (?string) => void,
|
setPrimaryUri: (?string) => void,
|
||||||
videoTheaterMode: boolean,
|
videoTheaterMode: boolean,
|
||||||
|
claim: ?Claim,
|
||||||
|
claimIsMine: boolean,
|
||||||
commentsDisabled: boolean,
|
commentsDisabled: boolean,
|
||||||
|
isLivestream: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function FilePage(props: Props) {
|
function FilePage(props: Props) {
|
||||||
|
@ -44,7 +51,10 @@ function FilePage(props: Props) {
|
||||||
linkedComment,
|
linkedComment,
|
||||||
setPrimaryUri,
|
setPrimaryUri,
|
||||||
videoTheaterMode,
|
videoTheaterMode,
|
||||||
|
|
||||||
|
claimIsMine,
|
||||||
commentsDisabled,
|
commentsDisabled,
|
||||||
|
isLivestream,
|
||||||
} = props;
|
} = props;
|
||||||
const cost = costInfo ? costInfo.cost : null;
|
const cost = costInfo ? costInfo.cost : null;
|
||||||
const hasFileInfo = fileInfo !== undefined;
|
const hasFileInfo = fileInfo !== undefined;
|
||||||
|
@ -113,6 +123,10 @@ function FilePage(props: Props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!claimIsMine && isLivestream) {
|
||||||
|
return <Redirect to={`/$/${PAGES.LIVESTREAM}`} />;
|
||||||
|
}
|
||||||
|
|
||||||
if (obscureNsfw && isMature) {
|
if (obscureNsfw && isMature) {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
|
@ -129,6 +143,18 @@ function FilePage(props: Props) {
|
||||||
{!isMarkdown && (
|
{!isMarkdown && (
|
||||||
<div className="file-page__secondary-content">
|
<div className="file-page__secondary-content">
|
||||||
<div>
|
<div>
|
||||||
|
{claimIsMine && isLivestream && (
|
||||||
|
<div className="livestream__creator-message">
|
||||||
|
<h4>{__('Only visible to you')}</h4>
|
||||||
|
<I18nMessage>
|
||||||
|
People who view this link will be redirected to your livestream. Make sure to use this for sharing
|
||||||
|
so your title and thumbnail are displayed properly.
|
||||||
|
</I18nMessage>
|
||||||
|
<div className="section__actions">
|
||||||
|
<Button button="primary" navigate={`/$/${PAGES.LIVESTREAM}`} label={__('View livestream')} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{RENDER_MODES.FLOATING_MODES.includes(renderMode) && <FileTitleSection uri={uri} />}
|
{RENDER_MODES.FLOATING_MODES.includes(renderMode) && <FileTitleSection uri={uri} />}
|
||||||
{commentsDisabled && <Empty text={__('The creator of this content has disabled comments.')} />}
|
{commentsDisabled && <Empty text={__('The creator of this content has disabled comments.')} />}
|
||||||
{!commentsDisabled && <CommentsList uri={uri} linkedComment={linkedComment} />}
|
{!commentsDisabled && <CommentsList uri={uri} linkedComment={linkedComment} />}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import { SITE_NAME } from 'config';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
@ -129,19 +130,13 @@ class HelpPage extends React.PureComponent<Props, State> {
|
||||||
return (
|
return (
|
||||||
<Page className="card-stack">
|
<Page className="card-stack">
|
||||||
<Card
|
<Card
|
||||||
title={__('Read the FAQ')}
|
title={__('Visit the %SITE_NAME% Help Hub', { SITE_NAME })}
|
||||||
subtitle={__('Our FAQ answers many common questions.')}
|
subtitle={__('Our support posts answer many common questions.')}
|
||||||
actions={
|
actions={
|
||||||
<div className="section__actions">
|
<div className="section__actions">
|
||||||
<Button
|
<Button
|
||||||
href="https://lbry.com/faq/lbry-basics"
|
href="https://odysee.com/@OdyseeHelp:b"
|
||||||
label={__('Read the App Basics FAQ')}
|
label={__('View %SITE_NAME% Help Hub', { SITE_NAME })}
|
||||||
icon={ICONS.HELP}
|
|
||||||
button="secondary"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
href="https://lbry.com/faq"
|
|
||||||
label={__('View all LBRY FAQs')}
|
|
||||||
icon={ICONS.HELP}
|
icon={ICONS.HELP}
|
||||||
button="secondary"
|
button="secondary"
|
||||||
/>
|
/>
|
||||||
|
@ -160,7 +155,7 @@ class HelpPage extends React.PureComponent<Props, State> {
|
||||||
actions={
|
actions={
|
||||||
<div className="section__actions">
|
<div className="section__actions">
|
||||||
<Button button="secondary" label={__('Join Our Chat')} icon={ICONS.CHAT} href="https://chat.lbry.com" />
|
<Button button="secondary" label={__('Join Our Chat')} icon={ICONS.CHAT} href="https://chat.lbry.com" />
|
||||||
<Button button="secondary" label={__('Email Us')} icon={ICONS.WEB} href="mailto:help@lbry.com" />
|
<Button button="secondary" label={__('Email Us')} icon={ICONS.WEB} href="mailto:help@odysee.com" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -210,7 +205,6 @@ class HelpPage extends React.PureComponent<Props, State> {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<WalletBackup />
|
<WalletBackup />
|
||||||
{/* @endif */}
|
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
title={__('About --[About section in Help Page]--')}
|
title={__('About --[About section in Help Page]--')}
|
||||||
|
@ -295,6 +289,7 @@ class HelpPage extends React.PureComponent<Props, State> {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
{/* @endif */}
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import * as SETTINGS from 'constants/settings';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
import { makeSelectClientSetting, selectHomepageData } from 'redux/selectors/settings';
|
import { selectShowMatureContent, selectHomepageData } from 'redux/selectors/settings';
|
||||||
|
|
||||||
import DiscoverPage from './view';
|
import DiscoverPage from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = (state) => ({
|
||||||
followedTags: selectFollowedTags(state),
|
followedTags: selectFollowedTags(state),
|
||||||
subscribedChannels: selectSubscriptions(state),
|
subscribedChannels: selectSubscriptions(state),
|
||||||
authenticated: selectUserVerifiedEmail(state),
|
authenticated: selectUserVerifiedEmail(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
showNsfw: selectShowMatureContent(state),
|
||||||
homepageData: selectHomepageData(state),
|
homepageData: selectHomepageData(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -79,9 +79,14 @@ function HomePage(props: Props) {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<h1 className="home__meme">
|
||||||
|
<Button button="link" label={'HelpLBRYSaveCrypto.com'} href="https://helplbrysavecrypto.com" />
|
||||||
|
</h1>
|
||||||
|
|
||||||
{rowData.map(({ title, route, link, icon, help, options = {} }, index) => (
|
{rowData.map(({ title, route, link, icon, help, options = {} }, index) => (
|
||||||
<div key={title} className="claim-grid__wrapper">
|
<div key={title} className="claim-grid__wrapper">
|
||||||
{title && (
|
{index !== 0 && title && typeof title === 'string' && (
|
||||||
<h1 className="claim-grid__header">
|
<h1 className="claim-grid__header">
|
||||||
<Button navigate={route || link} button="link">
|
<Button navigate={route || link} button="link">
|
||||||
{icon && <Icon className="claim-grid__header-icon" sectionIcon icon={icon} size={20} />}
|
{icon && <Icon className="claim-grid__header-icon" sectionIcon icon={icon} size={20} />}
|
||||||
|
@ -91,8 +96,8 @@ function HomePage(props: Props) {
|
||||||
</h1>
|
</h1>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ClaimTilesDiscover {...options} />
|
<ClaimTilesDiscover {...options} pin={route === `/$/${PAGES.GENERAL}`} />
|
||||||
{link && (
|
{(route || link) && (
|
||||||
<Button
|
<Button
|
||||||
className="claim-grid__title--secondary"
|
className="claim-grid__title--secondary"
|
||||||
button="link"
|
button="link"
|
||||||
|
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import LivestreamLayout from 'component/livestreamLayout';
|
import LivestreamLayout from 'component/livestreamLayout';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
|
import { Lbry } from 'lbry-redux';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
|
@ -19,8 +20,41 @@ export default function LivestreamPage(props: Props) {
|
||||||
const [activeViewers, setActiveViewers] = React.useState(0);
|
const [activeViewers, setActiveViewers] = React.useState(0);
|
||||||
const [isLive, setIsLive] = React.useState(false);
|
const [isLive, setIsLive] = React.useState(false);
|
||||||
const livestreamChannelId = channelClaim && channelClaim.signing_channel && channelClaim.signing_channel.claim_id;
|
const livestreamChannelId = channelClaim && channelClaim.signing_channel && channelClaim.signing_channel.claim_id;
|
||||||
|
const [hasLivestreamClaim, setHasLivestreamClaim] = React.useState(false);
|
||||||
|
|
||||||
|
const STREAMING_POLL_INTERVAL_IN_MS = 10000;
|
||||||
|
const LIVESTREAM_CLAIM_POLL_IN_MS = 60000;
|
||||||
|
|
||||||
|
// the component needs to check if the channel has published a new livestream, so we know if it should check
|
||||||
|
React.useEffect(() => {
|
||||||
|
let checkClaimsInterval;
|
||||||
|
function checkHasLivestreamClaim() {
|
||||||
|
Lbry.claim_search({
|
||||||
|
channel_ids: [livestreamChannelId],
|
||||||
|
has_no_source: true,
|
||||||
|
claim_type: ['stream'],
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res && res.items && res.items.length > 0) {
|
||||||
|
setHasLivestreamClaim(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
}
|
||||||
|
if (livestreamChannelId && !isLive) {
|
||||||
|
if (!checkClaimsInterval) checkHasLivestreamClaim();
|
||||||
|
checkClaimsInterval = setInterval(checkHasLivestreamClaim, LIVESTREAM_CLAIM_POLL_IN_MS);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (checkClaimsInterval) {
|
||||||
|
clearInterval(checkClaimsInterval);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [livestreamChannelId, isLive]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
let interval;
|
||||||
function checkIsLive() {
|
function checkIsLive() {
|
||||||
// $FlowFixMe Bitwave's API can handle garbage
|
// $FlowFixMe Bitwave's API can handle garbage
|
||||||
fetch(`${BITWAVE_API}/${livestreamChannelId}`)
|
fetch(`${BITWAVE_API}/${livestreamChannelId}`)
|
||||||
|
@ -38,19 +72,17 @@ export default function LivestreamPage(props: Props) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (livestreamChannelId && hasLivestreamClaim) {
|
||||||
let interval;
|
|
||||||
if (livestreamChannelId) {
|
|
||||||
if (!interval) checkIsLive();
|
if (!interval) checkIsLive();
|
||||||
interval = setInterval(checkIsLive, 10 * 1000);
|
interval = setInterval(checkIsLive, STREAMING_POLL_INTERVAL_IN_MS);
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (interval) {
|
if (interval) {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [livestreamChannelId]);
|
}
|
||||||
|
}, [livestreamChannelId, hasLivestreamClaim]);
|
||||||
|
|
||||||
const stringifiedClaim = JSON.stringify(claim);
|
const stringifiedClaim = JSON.stringify(claim);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import CopyableText from 'component/copyableText';
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
import ClaimList from 'component/claimList';
|
import ClaimList from 'component/claimList';
|
||||||
import usePersistedState from 'effects/use-persisted-state';
|
import usePersistedState from 'effects/use-persisted-state';
|
||||||
|
import usePrevious from 'effects/use-previous';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
channels: Array<ChannelClaim>,
|
channels: Array<ChannelClaim>,
|
||||||
|
@ -24,15 +25,141 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function LivestreamSetupPage(props: Props) {
|
export default function LivestreamSetupPage(props: Props) {
|
||||||
|
const LIVESTREAM_CLAIM_POLL_IN_MS = 60000;
|
||||||
const { channels, fetchingChannels, activeChannelClaim, pendingClaims } = props;
|
const { channels, fetchingChannels, activeChannelClaim, pendingClaims } = props;
|
||||||
|
|
||||||
const [sigData, setSigData] = React.useState({ signature: undefined, signing_ts: undefined });
|
const [sigData, setSigData] = React.useState({ signature: undefined, signing_ts: undefined });
|
||||||
const [showHelpTest, setShowHelpTest] = usePersistedState('livestream-help-seen', true);
|
const [showHelpTest, setShowHelpTest] = usePersistedState('livestream-help-seen', true);
|
||||||
const [spin, setSpin] = React.useState(true);
|
const [spin, setSpin] = React.useState(true);
|
||||||
|
const [livestreamClaims, setLivestreamClaims] = React.useState([]);
|
||||||
|
|
||||||
const hasChannels = channels && channels.length > 0;
|
const hasChannels = channels && channels.length > 0;
|
||||||
const activeChannelClaimStr = JSON.stringify(activeChannelClaim);
|
const activeChannelClaimStr = JSON.stringify(activeChannelClaim);
|
||||||
|
|
||||||
|
function createStreamKey() {
|
||||||
|
if (!activeChannelClaim || !sigData.signature || !sigData.signing_ts) return null;
|
||||||
|
return `${activeChannelClaim.claim_id}?d=${toHex(activeChannelClaim.name)}&s=${sigData.signature}&t=${
|
||||||
|
sigData.signing_ts
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
const streamKey = createStreamKey();
|
const streamKey = createStreamKey();
|
||||||
|
const pendingLiveStreamClaims = pendingClaims
|
||||||
|
? pendingClaims.filter(
|
||||||
|
(claim) =>
|
||||||
|
// $FlowFixMe
|
||||||
|
claim.value_type === 'stream' && !(claim.value && claim.value.source)
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
const [localPending, setLocalPending] = React.useState([]); //
|
||||||
|
const localPendingStr = JSON.stringify(localPending);
|
||||||
|
const pendingLivestreamClaimsStr = JSON.stringify(pendingLiveStreamClaims);
|
||||||
|
const prevPendingLiveStreamClaimStr = usePrevious(pendingLivestreamClaimsStr);
|
||||||
|
const liveStreamClaimsStr = JSON.stringify(livestreamClaims);
|
||||||
|
const prevLiveStreamClaimsStr = JSON.stringify(liveStreamClaimsStr);
|
||||||
|
const pendingLength = pendingLiveStreamClaims.length;
|
||||||
|
|
||||||
|
// maintain a pendingClaims list by channelId that locally that things are removed from only when they show up in claim_search results.
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (activeChannelClaimStr) {
|
||||||
|
const channelClaim = JSON.parse(activeChannelClaimStr);
|
||||||
|
|
||||||
|
// ensure we have a channel
|
||||||
|
if (channelClaim.claim_id) {
|
||||||
|
Lbry.channel_sign({
|
||||||
|
channel_id: channelClaim.claim_id,
|
||||||
|
hexdata: toHex(channelClaim.name),
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
setSigData(data);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setSigData({ signature: null, signing_ts: null });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [activeChannelClaimStr, setSigData]);
|
||||||
|
|
||||||
|
// The following 2 effects handle the time between pending disappearing and claim_search being able to find it.
|
||||||
|
// We'll maintain our own pending list:
|
||||||
|
// add to it when there are new things in pending
|
||||||
|
// remove items only when our claim_search finds it
|
||||||
|
React.useEffect(() => {
|
||||||
|
// add to localPending when pending changes
|
||||||
|
const localPending = JSON.parse(localPendingStr);
|
||||||
|
const pendingLivestreamClaims = JSON.parse(pendingLivestreamClaimsStr);
|
||||||
|
if (
|
||||||
|
pendingLiveStreamClaims !== prevPendingLiveStreamClaimStr ||
|
||||||
|
(pendingLivestreamClaims.length && !localPending.length)
|
||||||
|
) {
|
||||||
|
const prevPendingLivestreamClaims = prevPendingLiveStreamClaimStr
|
||||||
|
? JSON.parse(prevPendingLiveStreamClaimStr)
|
||||||
|
: [];
|
||||||
|
const pendingClaimIds = pendingLivestreamClaims.map((claim) => claim.claim_id);
|
||||||
|
const prevPendingClaimIds = prevPendingLivestreamClaims.map((claim) => claim.claim_id);
|
||||||
|
const newLocalPending = [];
|
||||||
|
if (pendingClaimIds.length > prevPendingClaimIds.length) {
|
||||||
|
pendingLivestreamClaims.forEach((pendingClaim) => {
|
||||||
|
if (!localPending.some((lClaim) => lClaim.claim_id === pendingClaim.claim_id)) {
|
||||||
|
newLocalPending.push(pendingClaim);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setLocalPending(localPending.concat(newLocalPending));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [pendingLivestreamClaimsStr, prevPendingLiveStreamClaimStr, localPendingStr, setLocalPending]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
// remove from localPending when livestreamClaims found
|
||||||
|
const localPending = JSON.parse(localPendingStr);
|
||||||
|
if (liveStreamClaimsStr !== prevLiveStreamClaimsStr && localPending.length) {
|
||||||
|
const livestreamClaims = JSON.parse(liveStreamClaimsStr);
|
||||||
|
setLocalPending(
|
||||||
|
localPending.filter((pending) => !livestreamClaims.some((claim) => claim.claim_id === pending.claim_id))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [liveStreamClaimsStr, prevLiveStreamClaimsStr, localPendingStr, setLocalPending]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
let checkClaimsInterval;
|
||||||
|
if (!activeChannelClaimStr) return;
|
||||||
|
const channelClaim = JSON.parse(activeChannelClaimStr);
|
||||||
|
|
||||||
|
function checkLivestreamClaims() {
|
||||||
|
Lbry.claim_search({
|
||||||
|
channel_ids: [channelClaim.claim_id],
|
||||||
|
has_no_source: true,
|
||||||
|
claim_type: ['stream'],
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res && res.items && res.items.length > 0) {
|
||||||
|
setLivestreamClaims(res.items.reverse());
|
||||||
|
} else {
|
||||||
|
setLivestreamClaims([]);
|
||||||
|
}
|
||||||
|
setSpin(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setLivestreamClaims([]);
|
||||||
|
setSpin(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!checkClaimsInterval) {
|
||||||
|
checkLivestreamClaims();
|
||||||
|
checkClaimsInterval = setInterval(checkLivestreamClaims, LIVESTREAM_CLAIM_POLL_IN_MS);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (checkClaimsInterval) {
|
||||||
|
clearInterval(checkClaimsInterval);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [activeChannelClaimStr, pendingLength, setSpin]);
|
||||||
|
const activeChannelId = activeChannelClaim && activeChannelClaim.claim_id;
|
||||||
|
const localPendingForChannel = localPending.filter(
|
||||||
|
(claim) => claim.signing_channel && claim.signing_channel.claim_id === activeChannelId
|
||||||
|
);
|
||||||
|
const totalLivestreamClaims = localPendingForChannel.concat(livestreamClaims);
|
||||||
|
|
||||||
const helpText = (
|
const helpText = (
|
||||||
<div className="section__subtitle">
|
<div className="section__subtitle">
|
||||||
|
@ -76,67 +203,10 @@ export default function LivestreamSetupPage(props: Props) {
|
||||||
<p>{__(`Click Save and you are done!`)}</p>
|
<p>{__(`Click Save and you are done!`)}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
React.useEffect(() => {
|
|
||||||
if (activeChannelClaimStr) {
|
|
||||||
const channelClaim = JSON.parse(activeChannelClaimStr);
|
|
||||||
|
|
||||||
// ensure we have a channel
|
|
||||||
if (channelClaim.claim_id) {
|
|
||||||
Lbry.channel_sign({
|
|
||||||
channel_id: channelClaim.claim_id,
|
|
||||||
hexdata: toHex(channelClaim.name),
|
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
setSigData(data);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
setSigData({ signature: null, signing_ts: null });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [activeChannelClaimStr, setSigData]);
|
|
||||||
|
|
||||||
function createStreamKey() {
|
|
||||||
if (!activeChannelClaim || !sigData.signature || !sigData.signing_ts) return null;
|
|
||||||
return `${activeChannelClaim.claim_id}?d=${toHex(activeChannelClaim.name)}&s=${sigData.signature}&t=${
|
|
||||||
sigData.signing_ts
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [livestreamClaims, setLivestreamClaims] = React.useState([]);
|
|
||||||
const pendingLiveStreamClaims =
|
|
||||||
// $FlowFixMe
|
|
||||||
pendingClaims ? pendingClaims.filter((claim) => !(claim && claim.value && claim.value.source)) : [];
|
|
||||||
const pendingLength = pendingLiveStreamClaims.length;
|
|
||||||
const totalLivestreamClaims = pendingLiveStreamClaims.concat(livestreamClaims);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (!activeChannelClaimStr) return;
|
|
||||||
|
|
||||||
const channelClaim = JSON.parse(activeChannelClaimStr);
|
|
||||||
|
|
||||||
Lbry.claim_search({
|
|
||||||
channel_ids: [channelClaim.claim_id],
|
|
||||||
has_no_source: true,
|
|
||||||
claim_type: ['stream'],
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res && res.items && res.items.length > 0) {
|
|
||||||
setLivestreamClaims(res.items.reverse());
|
|
||||||
} else {
|
|
||||||
setLivestreamClaims([]);
|
|
||||||
}
|
|
||||||
setSpin(false);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setLivestreamClaims([]);
|
|
||||||
setSpin(false);
|
|
||||||
});
|
|
||||||
}, [activeChannelClaimStr, pendingLength, setSpin]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
{fetchingChannels && (
|
{(fetchingChannels || spin) && (
|
||||||
<div className="main--empty">
|
<div className="main--empty">
|
||||||
<Spinner delayed />
|
<Spinner delayed />
|
||||||
</div>
|
</div>
|
||||||
|
@ -178,6 +248,7 @@ export default function LivestreamSetupPage(props: Props) {
|
||||||
)}
|
)}
|
||||||
{streamKey && totalLivestreamClaims.length > 0 && (
|
{streamKey && totalLivestreamClaims.length > 0 && (
|
||||||
<Card
|
<Card
|
||||||
|
className="section"
|
||||||
title={__('Your stream key')}
|
title={__('Your stream key')}
|
||||||
actions={
|
actions={
|
||||||
<>
|
<>
|
||||||
|
@ -201,10 +272,26 @@ export default function LivestreamSetupPage(props: Props) {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{totalLivestreamClaims.length > 0 ? (
|
{totalLivestreamClaims.length > 0 ? (
|
||||||
<ClaimList
|
<>
|
||||||
header={__('Your livestream uploads')}
|
{Boolean(localPendingForChannel.length) && (
|
||||||
uris={totalLivestreamClaims.map((claim) => claim.permanent_url)}
|
<div className="section">
|
||||||
/>
|
<ClaimList
|
||||||
|
header={__('Your pending livestream uploads')}
|
||||||
|
uris={localPendingForChannel.map((claim) => claim.permanent_url)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{Boolean(livestreamClaims.length) && (
|
||||||
|
<div className="section">
|
||||||
|
<ClaimList
|
||||||
|
header={__('Your livestream uploads')}
|
||||||
|
uris={livestreamClaims
|
||||||
|
.filter((c) => !pendingLiveStreamClaims.some((p) => p.permanent_url === c.permanent_url))
|
||||||
|
.map((claim) => claim.permanent_url)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Yrbl
|
<Yrbl
|
||||||
className="livestream__publish-intro"
|
className="livestream__publish-intro"
|
||||||
|
|
|
@ -78,8 +78,8 @@ class RewardsPage extends PureComponent<Props> {
|
||||||
rewards_faq: <Button button="link" label={__('Rewards FAQ')} href="https://lbry.com/faq/support" />,
|
rewards_faq: <Button button="link" label={__('Rewards FAQ')} href="https://lbry.com/faq/support" />,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Please review the %rewards_faq% for eligibility, and send us an email to help@lbry.com if you continue
|
Please review the %rewards_faq% for eligibility, and send us an email to help@odysee.com if you
|
||||||
to see this message. You can continue to use LBRY without this feature.
|
continue to see this message. You can continue to use LBRY without this feature.
|
||||||
</I18nMessage>
|
</I18nMessage>
|
||||||
{`${__('Enjoy all the awesome free content in the meantime!')}`}
|
{`${__('Enjoy all the awesome free content in the meantime!')}`}
|
||||||
</p>
|
</p>
|
||||||
|
@ -156,7 +156,7 @@ class RewardsPage extends PureComponent<Props> {
|
||||||
'card--disabled': isNotEligible,
|
'card--disabled': isNotEligible,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{rewards.map(reward => (
|
{rewards.map((reward) => (
|
||||||
<RewardTile key={reward.claim_code} reward={reward} />
|
<RewardTile key={reward.claim_code} reward={reward} />
|
||||||
))}
|
))}
|
||||||
{this.renderCustomRewardCode()}
|
{this.renderCustomRewardCode()}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doToast, SETTINGS } from 'lbry-redux';
|
import { doToast } from 'lbry-redux';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { doSearch } from 'redux/actions/search';
|
import { doSearch } from 'redux/actions/search';
|
||||||
import {
|
import {
|
||||||
|
@ -8,37 +8,32 @@ import {
|
||||||
makeSelectQueryWithOptions,
|
makeSelectQueryWithOptions,
|
||||||
selectSearchOptions,
|
selectSearchOptions,
|
||||||
} from 'redux/selectors/search';
|
} from 'redux/selectors/search';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import SearchPage from './view';
|
import SearchPage from './view';
|
||||||
|
|
||||||
const select = (state, props) => {
|
const select = (state, props) => {
|
||||||
const showMature = makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state);
|
|
||||||
const urlParams = new URLSearchParams(props.location.search);
|
const urlParams = new URLSearchParams(props.location.search);
|
||||||
let urlQuery = urlParams.get('q') || null;
|
let urlQuery = urlParams.get('q') || null;
|
||||||
if (urlQuery) {
|
if (urlQuery) {
|
||||||
urlQuery = urlQuery.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
|
urlQuery = urlQuery.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
|
||||||
}
|
}
|
||||||
|
const query = makeSelectQueryWithOptions(urlQuery, { nsfw: false, isBackgroundSearch: false })(state);
|
||||||
const query = makeSelectQueryWithOptions(
|
|
||||||
urlQuery,
|
|
||||||
showMature === false ? { nsfw: false, isBackgroundSearch: false } : { isBackgroundSearch: false }
|
|
||||||
)(state);
|
|
||||||
const uris = makeSelectSearchUris(query)(state);
|
const uris = makeSelectSearchUris(query)(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isSearching: selectIsSearching(state),
|
isSearching: selectIsSearching(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
showNsfw: selectShowMatureContent(state),
|
||||||
uris: uris,
|
uris: uris,
|
||||||
isAuthenticated: selectUserVerifiedEmail(state),
|
isAuthenticated: selectUserVerifiedEmail(state),
|
||||||
searchOptions: selectSearchOptions(state),
|
searchOptions: selectSearchOptions(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = (dispatch) => ({
|
||||||
search: (query, options) => dispatch(doSearch(query, options)),
|
search: (query, options) => dispatch(doSearch(query, options)),
|
||||||
onFeedbackPositive: query => {
|
onFeedbackPositive: (query) => {
|
||||||
analytics.apiSearchFeedback(query, 1);
|
analytics.apiSearchFeedback(query, 1);
|
||||||
dispatch(
|
dispatch(
|
||||||
doToast({
|
doToast({
|
||||||
|
@ -46,7 +41,7 @@ const perform = dispatch => ({
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onFeedbackNegative: query => {
|
onFeedbackNegative: (query) => {
|
||||||
analytics.apiSearchFeedback(query, 0);
|
analytics.apiSearchFeedback(query, 0);
|
||||||
dispatch(
|
dispatch(
|
||||||
doToast({
|
doToast({
|
||||||
|
|
|
@ -24,8 +24,8 @@ type Props = {
|
||||||
isSearching: boolean,
|
isSearching: boolean,
|
||||||
location: UrlLocation,
|
location: UrlLocation,
|
||||||
uris: Array<string>,
|
uris: Array<string>,
|
||||||
onFeedbackNegative: string => void,
|
onFeedbackNegative: (string) => void,
|
||||||
onFeedbackPositive: string => void,
|
onFeedbackPositive: (string) => void,
|
||||||
showNsfw: boolean,
|
showNsfw: boolean,
|
||||||
isAuthenticated: boolean,
|
isAuthenticated: boolean,
|
||||||
};
|
};
|
||||||
|
@ -38,7 +38,7 @@ export default function SearchPage(props: Props) {
|
||||||
onFeedbackNegative,
|
onFeedbackNegative,
|
||||||
location,
|
location,
|
||||||
isSearching,
|
isSearching,
|
||||||
showNsfw,
|
// showNsfw,
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
searchOptions,
|
searchOptions,
|
||||||
} = props;
|
} = props;
|
||||||
|
@ -47,12 +47,9 @@ export default function SearchPage(props: Props) {
|
||||||
const urlQuery = urlParams.get('q') || '';
|
const urlQuery = urlParams.get('q') || '';
|
||||||
const additionalOptions: AdditionalOptions = { isBackgroundSearch: false };
|
const additionalOptions: AdditionalOptions = { isBackgroundSearch: false };
|
||||||
|
|
||||||
additionalOptions['nsfw'] = showNsfw;
|
additionalOptions['nsfw'] = false;
|
||||||
|
|
||||||
const modifiedUrlQuery = urlQuery
|
const modifiedUrlQuery = urlQuery.trim().replace(/\s+/g, '').replace(/:/g, '#');
|
||||||
.trim()
|
|
||||||
.replace(/\s+/g, '')
|
|
||||||
.replace(/:/g, '#');
|
|
||||||
const uriFromQuery = `lbry://${modifiedUrlQuery}`;
|
const uriFromQuery = `lbry://${modifiedUrlQuery}`;
|
||||||
|
|
||||||
let streamName;
|
let streamName;
|
||||||
|
@ -72,7 +69,7 @@ export default function SearchPage(props: Props) {
|
||||||
try {
|
try {
|
||||||
const dummyUrlForClaimId = `x#${urlQuery}`;
|
const dummyUrlForClaimId = `x#${urlQuery}`;
|
||||||
({ claimId } = parseURI(dummyUrlForClaimId));
|
({ claimId } = parseURI(dummyUrlForClaimId));
|
||||||
Lbry.claim_search({ claim_id: claimId }).then(res => {
|
Lbry.claim_search({ claim_id: claimId }).then((res) => {
|
||||||
if (res.items && res.items.length) {
|
if (res.items && res.items.length) {
|
||||||
const claim = res.items[0];
|
const claim = res.items[0];
|
||||||
const url = formatLbryUrlForWeb(claim.canonical_url);
|
const url = formatLbryUrlForWeb(claim.canonical_url);
|
||||||
|
@ -105,21 +102,23 @@ export default function SearchPage(props: Props) {
|
||||||
SHOW_ADS && IS_WEB ? (SIMPLE_SITE ? false : !isAuthenticated && <Ads small type={'video'} />) : false
|
SHOW_ADS && IS_WEB ? (SIMPLE_SITE ? false : !isAuthenticated && <Ads small type={'video'} />) : false
|
||||||
}
|
}
|
||||||
headerAltControls={
|
headerAltControls={
|
||||||
<>
|
!SIMPLE_SITE && (
|
||||||
<span>{__('Find what you were looking for?')}</span>
|
<>
|
||||||
<Button
|
<span>{__('Find what you were looking for?')}</span>
|
||||||
button="alt"
|
<Button
|
||||||
description={__('Yes')}
|
button="alt"
|
||||||
onClick={() => onFeedbackPositive(urlQuery)}
|
description={__('Yes')}
|
||||||
icon={ICONS.YES}
|
onClick={() => onFeedbackPositive(urlQuery)}
|
||||||
/>
|
icon={ICONS.YES}
|
||||||
<Button
|
/>
|
||||||
button="alt"
|
<Button
|
||||||
description={__('No')}
|
button="alt"
|
||||||
onClick={() => onFeedbackNegative(urlQuery)}
|
description={__('No')}
|
||||||
icon={ICONS.NO}
|
onClick={() => onFeedbackNegative(urlQuery)}
|
||||||
/>
|
icon={ICONS.NO}
|
||||||
</>
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{isSearching && new Array(5).fill(1).map((x, i) => <ClaimPreview key={i} placeholder="loading" />)}
|
{isSearching && new Array(5).fill(1).map((x, i) => <ClaimPreview key={i} placeholder="loading" />)}
|
||||||
|
|
|
@ -10,7 +10,12 @@ import {
|
||||||
doExitSettingsPage,
|
doExitSettingsPage,
|
||||||
} from 'redux/actions/settings';
|
} from 'redux/actions/settings';
|
||||||
import { doSetPlayingUri } from 'redux/actions/content';
|
import { doSetPlayingUri } from 'redux/actions/content';
|
||||||
import { makeSelectClientSetting, selectDaemonSettings, selectLanguage } from 'redux/selectors/settings';
|
import {
|
||||||
|
makeSelectClientSetting,
|
||||||
|
selectDaemonSettings,
|
||||||
|
selectLanguage,
|
||||||
|
selectShowMatureContent,
|
||||||
|
} from 'redux/selectors/settings';
|
||||||
import { doWalletStatus, selectWalletIsEncrypted, SETTINGS } from 'lbry-redux';
|
import { doWalletStatus, selectWalletIsEncrypted, SETTINGS } from 'lbry-redux';
|
||||||
import SettingsPage from './view';
|
import SettingsPage from './view';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
|
@ -19,7 +24,7 @@ const select = (state) => ({
|
||||||
daemonSettings: selectDaemonSettings(state),
|
daemonSettings: selectDaemonSettings(state),
|
||||||
allowAnalytics: selectAllowAnalytics(state),
|
allowAnalytics: selectAllowAnalytics(state),
|
||||||
isAuthenticated: selectUserVerifiedEmail(state),
|
isAuthenticated: selectUserVerifiedEmail(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
showNsfw: selectShowMatureContent(state),
|
||||||
currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state),
|
currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state),
|
||||||
themes: makeSelectClientSetting(SETTINGS.THEMES)(state),
|
themes: makeSelectClientSetting(SETTINGS.THEMES)(state),
|
||||||
automaticDarkModeEnabled: makeSelectClientSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED)(state),
|
automaticDarkModeEnabled: makeSelectClientSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED)(state),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import * as MODALS from 'constants/modal_types';
|
// import * as MODALS from 'constants/modal_types';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { SETTINGS } from 'lbry-redux';
|
import { SETTINGS } from 'lbry-redux';
|
||||||
|
@ -17,7 +17,7 @@ import classnames from 'classnames';
|
||||||
import { getPasswordFromCookie } from 'util/saved-passwords';
|
import { getPasswordFromCookie } from 'util/saved-passwords';
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
import homepages from 'homepages';
|
import homepages from 'homepages';
|
||||||
import { Lbryio } from 'lbryinc';
|
// import { Lbryio } from 'lbryinc';
|
||||||
import Yrbl from 'component/yrbl';
|
import Yrbl from 'component/yrbl';
|
||||||
|
|
||||||
type Price = {
|
type Price = {
|
||||||
|
@ -27,10 +27,10 @@ type Price = {
|
||||||
|
|
||||||
type SetDaemonSettingArg = boolean | string | number;
|
type SetDaemonSettingArg = boolean | string | number;
|
||||||
|
|
||||||
type DarkModeTimes = {
|
// type DarkModeTimes = {
|
||||||
from: { hour: string, min: string, formattedTime: string },
|
// from: { hour: string, min: string, formattedTime: string },
|
||||||
to: { hour: string, min: string, formattedTime: string },
|
// to: { hour: string, min: string, formattedTime: string },
|
||||||
};
|
// };
|
||||||
|
|
||||||
type OptionTimes = {
|
type OptionTimes = {
|
||||||
fromTo: string,
|
fromTo: string,
|
||||||
|
@ -64,7 +64,7 @@ type Props = {
|
||||||
floatingPlayer: boolean,
|
floatingPlayer: boolean,
|
||||||
hideReposts: ?boolean,
|
hideReposts: ?boolean,
|
||||||
clearPlayingUri: () => void,
|
clearPlayingUri: () => void,
|
||||||
darkModeTimes: DarkModeTimes,
|
// darkModeTimes: DarkModeTimes,
|
||||||
setDarkTime: (string, {}) => void,
|
setDarkTime: (string, {}) => void,
|
||||||
openModal: (string) => void,
|
openModal: (string) => void,
|
||||||
language?: string,
|
language?: string,
|
||||||
|
@ -160,11 +160,11 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
const {
|
const {
|
||||||
daemonSettings,
|
daemonSettings,
|
||||||
allowAnalytics,
|
allowAnalytics,
|
||||||
showNsfw,
|
// showNsfw,
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
currentTheme,
|
currentTheme,
|
||||||
themes,
|
themes,
|
||||||
automaticDarkModeEnabled,
|
// automaticDarkModeEnabled,
|
||||||
autoplay,
|
autoplay,
|
||||||
walletEncrypted,
|
walletEncrypted,
|
||||||
// autoDownload,
|
// autoDownload,
|
||||||
|
@ -172,16 +172,16 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
setClientSetting,
|
setClientSetting,
|
||||||
toggle3PAnalytics,
|
toggle3PAnalytics,
|
||||||
floatingPlayer,
|
floatingPlayer,
|
||||||
hideReposts,
|
// hideReposts,
|
||||||
clearPlayingUri,
|
clearPlayingUri,
|
||||||
darkModeTimes,
|
// darkModeTimes,
|
||||||
clearCache,
|
clearCache,
|
||||||
openModal,
|
// openModal,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { storedPassword } = this.state;
|
const { storedPassword } = this.state;
|
||||||
const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0;
|
const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0;
|
||||||
const startHours = ['18', '19', '20', '21'];
|
// const startHours = ['18', '19', '20', '21'];
|
||||||
const endHours = ['5', '6', '7', '8'];
|
// const endHours = ['5', '6', '7', '8'];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
|
@ -258,7 +258,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
label={__('Theme')}
|
label={__('Theme')}
|
||||||
onChange={this.onThemeChange}
|
onChange={this.onThemeChange}
|
||||||
value={currentTheme}
|
value={currentTheme}
|
||||||
disabled={automaticDarkModeEnabled}
|
// disabled={automaticDarkModeEnabled}
|
||||||
>
|
>
|
||||||
{themes.map((theme) => (
|
{themes.map((theme) => (
|
||||||
<option key={theme} value={theme}>
|
<option key={theme} value={theme}>
|
||||||
|
@ -267,7 +267,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
))}
|
))}
|
||||||
</FormField>
|
</FormField>
|
||||||
</fieldset-section>
|
</fieldset-section>
|
||||||
<fieldset-section>
|
{/* <fieldset-section>
|
||||||
<FormField
|
<FormField
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="automatic_dark_mode"
|
name="automatic_dark_mode"
|
||||||
|
@ -305,7 +305,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
</FormField>
|
</FormField>
|
||||||
</fieldset-group>
|
</fieldset-group>
|
||||||
)}
|
)}
|
||||||
</fieldset-section>
|
</fieldset-section> */}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -337,7 +337,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
{/* <FormField
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="hide_reposts"
|
name="hide_reposts"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
@ -351,7 +351,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
checked={hideReposts}
|
checked={hideReposts}
|
||||||
label={__('Hide reposts')}
|
label={__('Hide reposts')}
|
||||||
helper={__('You will not see reposts by people you follow or receive email notifying about them.')}
|
helper={__('You will not see reposts by people you follow or receive email notifying about them.')}
|
||||||
/>
|
/> */}
|
||||||
|
|
||||||
{/* <FormField
|
{/* <FormField
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
@ -362,7 +362,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
helper={__('Anonymous content is published without a channel.')}
|
helper={__('Anonymous content is published without a channel.')}
|
||||||
/> */}
|
/> */}
|
||||||
|
|
||||||
<FormField
|
{/* <FormField
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="show_nsfw"
|
name="show_nsfw"
|
||||||
onChange={() =>
|
onChange={() =>
|
||||||
|
@ -375,7 +375,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
helper={__(
|
helper={__(
|
||||||
'Mature content may include nudity, intense sexuality, profanity, or other adult content. By displaying mature content, you are affirming you are of legal age to view mature content in your country or jurisdiction. '
|
'Mature content may include nudity, intense sexuality, profanity, or other adult content. By displaying mature content, you are affirming you are of legal age to view mature content in your country or jurisdiction. '
|
||||||
)}
|
)}
|
||||||
/>
|
/> */}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import * as PAGES from 'constants/pages';
|
||||||
import { DOMAIN } from 'config';
|
import { DOMAIN } from 'config';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { PAGE_SIZE } from 'constants/claim';
|
import { PAGE_SIZE } from 'constants/claim';
|
||||||
|
@ -11,7 +12,10 @@ import {
|
||||||
makeSelectClaimIsMine,
|
makeSelectClaimIsMine,
|
||||||
makeSelectClaimIsPending,
|
makeSelectClaimIsPending,
|
||||||
makeSelectClaimIsStreamPlaceholder,
|
makeSelectClaimIsStreamPlaceholder,
|
||||||
|
doClearPublish,
|
||||||
|
doPrepareEdit,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
|
import { push } from 'connected-react-router';
|
||||||
import { makeSelectChannelInSubscriptions } from 'redux/selectors/subscriptions';
|
import { makeSelectChannelInSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
import { selectBlackListedOutpoints } from 'lbryinc';
|
import { selectBlackListedOutpoints } from 'lbryinc';
|
||||||
import ShowPage from './view';
|
import ShowPage from './view';
|
||||||
|
@ -67,6 +71,11 @@ const select = (state, props) => {
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
resolveUri: (uri) => dispatch(doResolveUri(uri)),
|
resolveUri: (uri) => dispatch(doResolveUri(uri)),
|
||||||
|
beginPublish: (name) => {
|
||||||
|
dispatch(doClearPublish());
|
||||||
|
dispatch(doPrepareEdit({ name }));
|
||||||
|
dispatch(push(`/$/${PAGES.PUBLISH}`));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(ShowPage);
|
export default connect(select, perform)(ShowPage);
|
||||||
|
|
|
@ -10,7 +10,9 @@ import Page from 'component/page';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
import AbandonedChannelPreview from 'component/abandonedChannelPreview';
|
import AbandonedChannelPreview from 'component/abandonedChannelPreview';
|
||||||
|
import Yrbl from 'component/yrbl';
|
||||||
import { formatLbryUrlForWeb } from 'util/url';
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
|
import { parseURI } from 'lbry-redux';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isResolvingUri: boolean,
|
isResolvingUri: boolean,
|
||||||
|
@ -27,6 +29,8 @@ type Props = {
|
||||||
claimIsMine: boolean,
|
claimIsMine: boolean,
|
||||||
claimIsPending: boolean,
|
claimIsPending: boolean,
|
||||||
isLivestream: boolean,
|
isLivestream: boolean,
|
||||||
|
claimIsMine: Boolean,
|
||||||
|
beginPublish: (string) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ShowPage(props: Props) {
|
function ShowPage(props: Props) {
|
||||||
|
@ -41,6 +45,7 @@ function ShowPage(props: Props) {
|
||||||
isSubscribed,
|
isSubscribed,
|
||||||
claimIsPending,
|
claimIsPending,
|
||||||
isLivestream,
|
isLivestream,
|
||||||
|
beginPublish,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const signingChannel = claim && claim.signing_channel;
|
const signingChannel = claim && claim.signing_channel;
|
||||||
|
@ -48,6 +53,7 @@ function ShowPage(props: Props) {
|
||||||
const claimExists = claim !== null && claim !== undefined;
|
const claimExists = claim !== null && claim !== undefined;
|
||||||
const haventFetchedYet = claim === undefined;
|
const haventFetchedYet = claim === undefined;
|
||||||
const isMine = claim && claim.is_my_output;
|
const isMine = claim && claim.is_my_output;
|
||||||
|
const { contentName } = parseURI(uri);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// @if TARGET='web'
|
// @if TARGET='web'
|
||||||
|
@ -84,11 +90,20 @@ function ShowPage(props: Props) {
|
||||||
<Page>
|
<Page>
|
||||||
{(claim === undefined || isResolvingUri) && (
|
{(claim === undefined || isResolvingUri) && (
|
||||||
<div className="main--empty">
|
<div className="main--empty">
|
||||||
<Spinner />
|
<Spinner delayed />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isResolvingUri && !isSubscribed && (
|
{!isResolvingUri && !isSubscribed && (
|
||||||
<span className="empty">{__("There's nothing available at this location.")}</span>
|
<div className="main--empty">
|
||||||
|
<Yrbl
|
||||||
|
title={__('No Content Found')}
|
||||||
|
subtitle={
|
||||||
|
<div className="section__actions">
|
||||||
|
<Button button="primary" label={__('Publish Something')} onClick={() => beginPublish(contentName)} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isResolvingUri && isSubscribed && claim === null && <AbandonedChannelPreview uri={uri} type={'large'} />}
|
{!isResolvingUri && isSubscribed && claim === null && <AbandonedChannelPreview uri={uri} type={'large'} />}
|
||||||
</Page>
|
</Page>
|
||||||
|
|
|
@ -5,14 +5,14 @@ import Page from 'component/page';
|
||||||
import ClaimListDiscover from 'component/claimListDiscover';
|
import ClaimListDiscover from 'component/claimListDiscover';
|
||||||
import ClaimEffectiveAmount from 'component/claimEffectiveAmount';
|
import ClaimEffectiveAmount from 'component/claimEffectiveAmount';
|
||||||
import SearchTopClaim from 'component/searchTopClaim';
|
import SearchTopClaim from 'component/searchTopClaim';
|
||||||
import { ORDER_BY_TOP, FRESH_ALL } from 'constants/claim_search';
|
import * as CS from 'constants/claim_search';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
name: string,
|
name: string,
|
||||||
beginPublish: string => void,
|
beginPublish: (string) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
function TopPage(props: Props) {
|
function TopPage(props: Props) {
|
||||||
|
@ -25,8 +25,9 @@ function TopPage(props: Props) {
|
||||||
<SearchTopClaim query={name} hideLink setChannelActive={setChannelActive} />
|
<SearchTopClaim query={name} hideLink setChannelActive={setChannelActive} />
|
||||||
<ClaimListDiscover
|
<ClaimListDiscover
|
||||||
name={channelActive ? `@${queryName}` : queryName}
|
name={channelActive ? `@${queryName}` : queryName}
|
||||||
defaultFreshness={FRESH_ALL}
|
defaultFreshness={CS.FRESH_ALL}
|
||||||
defaultOrderBy={ORDER_BY_TOP}
|
defaultOrderBy={CS.ORDER_BY_TOP}
|
||||||
|
streamType={CS.CONTENT_ALL}
|
||||||
meta={
|
meta={
|
||||||
<div className="search__top-links">
|
<div className="search__top-links">
|
||||||
<I18nMessage
|
<I18nMessage
|
||||||
|
@ -46,7 +47,7 @@ function TopPage(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
includeSupportAction
|
includeSupportAction
|
||||||
renderProperties={claim => (
|
renderProperties={(claim) => (
|
||||||
<span className="claim-preview__custom-properties">
|
<span className="claim-preview__custom-properties">
|
||||||
{claim.meta.is_controlling && <span className="help--inline">{__('Currently winning')}</span>}
|
{claim.meta.is_controlling && <span className="help--inline">{__('Currently winning')}</span>}
|
||||||
<ClaimEffectiveAmount uri={claim.repost_url || claim.canonical_url} />
|
<ClaimEffectiveAmount uri={claim.repost_url || claim.canonical_url} />
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
import * as REACTION_TYPES from 'constants/reactions';
|
import * as REACTION_TYPES from 'constants/reactions';
|
||||||
import { Lbry, parseURI, buildURI, selectClaimsByUri, selectMyChannelClaims } from 'lbry-redux';
|
import { Lbry, parseURI, buildURI, selectClaimsByUri, selectMyChannelClaims } from 'lbry-redux';
|
||||||
|
// import { BANNED_LIVESTREAM_WORDS } from 'constants/comment';
|
||||||
import { doToast, doSeeNotifications } from 'redux/actions/notifications';
|
import { doToast, doSeeNotifications } from 'redux/actions/notifications';
|
||||||
import {
|
import {
|
||||||
makeSelectCommentIdsForUri,
|
makeSelectCommentIdsForUri,
|
||||||
|
@ -216,6 +217,27 @@ export function doCommentCreate(
|
||||||
type: ACTIONS.COMMENT_CREATE_STARTED,
|
type: ACTIONS.COMMENT_CREATE_STARTED,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// if (livestream) {
|
||||||
|
// const strippedCommentText = comment.trim().toLowerCase().replace(/\s/g, '');
|
||||||
|
// for (var i = 0; i < BANNED_LIVESTREAM_WORDS.length; i++) {
|
||||||
|
// const bannedWord = BANNED_LIVESTREAM_WORDS[i];
|
||||||
|
// if (strippedCommentText.includes(bannedWord)) {
|
||||||
|
// dispatch({
|
||||||
|
// type: ACTIONS.COMMENT_CREATE_FAILED,
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// dispatch(
|
||||||
|
// doToast({
|
||||||
|
// message: 'Unable to create comment.',
|
||||||
|
// isError: true,
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (parent_id) {
|
if (parent_id) {
|
||||||
const notification = makeSelectNotificationForCommentId(parent_id)(state);
|
const notification = makeSelectNotificationForCommentId(parent_id)(state);
|
||||||
if (notification && !notification.is_seen) {
|
if (notification && !notification.is_seen) {
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {
|
||||||
doCheckPendingClaims,
|
doCheckPendingClaims,
|
||||||
doCheckReflectingFiles,
|
doCheckReflectingFiles,
|
||||||
ACTIONS as LBRY_REDUX_ACTIONS,
|
ACTIONS as LBRY_REDUX_ACTIONS,
|
||||||
|
makeSelectPublishFormValue,
|
||||||
|
makeSelectClaimForUri,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { doError } from 'redux/actions/notifications';
|
import { doError } from 'redux/actions/notifications';
|
||||||
import { push } from 'connected-react-router';
|
import { push } from 'connected-react-router';
|
||||||
|
@ -24,7 +26,12 @@ export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispat
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const noFile = !filePath || filePath === NO_FILE;
|
const noFileParam = !filePath || filePath === NO_FILE;
|
||||||
|
const state = getState();
|
||||||
|
const editingUri = makeSelectPublishFormValue('editingURI')(state) || '';
|
||||||
|
const claim = makeSelectClaimForUri(editingUri)(state) || {};
|
||||||
|
const hasSourceFile = claim.value && claim.value.source;
|
||||||
|
const redirectToLivestream = noFileParam && !hasSourceFile;
|
||||||
|
|
||||||
const publishSuccess = (successResponse, lbryFirstError) => {
|
const publishSuccess = (successResponse, lbryFirstError) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
@ -41,6 +48,7 @@ export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispat
|
||||||
actions.push({
|
actions.push({
|
||||||
type: ACTIONS.PUBLISH_SUCCESS,
|
type: ACTIONS.PUBLISH_SUCCESS,
|
||||||
});
|
});
|
||||||
|
|
||||||
// We have to fake a temp claim until the new pending one is returned by claim_list_mine
|
// We have to fake a temp claim until the new pending one is returned by claim_list_mine
|
||||||
// We can't rely on claim_list_mine because there might be some delay before the new claims are returned
|
// We can't rely on claim_list_mine because there might be some delay before the new claims are returned
|
||||||
// Doing this allows us to show the pending claim immediately, it will get overwritten by the real one
|
// Doing this allows us to show the pending claim immediately, it will get overwritten by the real one
|
||||||
|
@ -73,6 +81,9 @@ export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispat
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
dispatch(doCheckReflectingFiles());
|
dispatch(doCheckReflectingFiles());
|
||||||
// @endif
|
// @endif
|
||||||
|
if (redirectToLivestream) {
|
||||||
|
dispatch(push(`/$/${PAGES.LIVESTREAM}`));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const publishFail = (error) => {
|
const publishFail = (error) => {
|
||||||
|
@ -93,7 +104,9 @@ export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispat
|
||||||
// on the publishes page. This doesn't exist on desktop so wait until we get a response
|
// on the publishes page. This doesn't exist on desktop so wait until we get a response
|
||||||
// from the SDK
|
// from the SDK
|
||||||
// @if TARGET='web'
|
// @if TARGET='web'
|
||||||
dispatch(push(noFile ? `/$/${PAGES.LIVESTREAM}` : `/$/${PAGES.UPLOADS}`));
|
if (!redirectToLivestream) {
|
||||||
|
dispatch(push(`/$/${PAGES.UPLOADS}`));
|
||||||
|
}
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
dispatch(doPublish(publishSuccess, publishFail));
|
dispatch(doPublish(publishSuccess, publishFail));
|
||||||
|
|
|
@ -6,13 +6,13 @@ import { SEARCH_OPTIONS } from 'constants/search';
|
||||||
const defaultState: SearchState = {
|
const defaultState: SearchState = {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
options: {
|
options: {
|
||||||
[SEARCH_OPTIONS.RESULT_COUNT]: 30,
|
[SEARCH_OPTIONS.RESULT_COUNT]: 50,
|
||||||
[SEARCH_OPTIONS.CLAIM_TYPE]: SEARCH_OPTIONS.INCLUDE_FILES_AND_CHANNELS,
|
[SEARCH_OPTIONS.CLAIM_TYPE]: 'file,channel',
|
||||||
[SEARCH_OPTIONS.MEDIA_AUDIO]: true,
|
[SEARCH_OPTIONS.MEDIA_AUDIO]: false,
|
||||||
[SEARCH_OPTIONS.MEDIA_VIDEO]: true,
|
[SEARCH_OPTIONS.MEDIA_VIDEO]: true,
|
||||||
[SEARCH_OPTIONS.MEDIA_TEXT]: true,
|
[SEARCH_OPTIONS.MEDIA_TEXT]: false,
|
||||||
[SEARCH_OPTIONS.MEDIA_IMAGE]: true,
|
[SEARCH_OPTIONS.MEDIA_IMAGE]: false,
|
||||||
[SEARCH_OPTIONS.MEDIA_APPLICATION]: true,
|
[SEARCH_OPTIONS.MEDIA_APPLICATION]: false,
|
||||||
},
|
},
|
||||||
urisByQuery: {},
|
urisByQuery: {},
|
||||||
searching: false,
|
searching: false,
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { UNSYNCED_SETTINGS } from 'config';
|
||||||
const { CLIENT_SYNC_KEYS } = SHARED_PREFERENCES;
|
const { CLIENT_SYNC_KEYS } = SHARED_PREFERENCES;
|
||||||
const settingsToIgnore = (UNSYNCED_SETTINGS && UNSYNCED_SETTINGS.trim().split(' ')) || [];
|
const settingsToIgnore = (UNSYNCED_SETTINGS && UNSYNCED_SETTINGS.trim().split(' ')) || [];
|
||||||
const clientSyncKeys = settingsToIgnore.length
|
const clientSyncKeys = settingsToIgnore.length
|
||||||
? CLIENT_SYNC_KEYS.filter(k => !settingsToIgnore.includes(k))
|
? CLIENT_SYNC_KEYS.filter((k) => !settingsToIgnore.includes(k))
|
||||||
: CLIENT_SYNC_KEYS;
|
: CLIENT_SYNC_KEYS;
|
||||||
|
|
||||||
const reducers = {};
|
const reducers = {};
|
||||||
|
@ -69,7 +69,7 @@ const defaultState = {
|
||||||
[SETTINGS.AUTOPLAY_NEXT]: true,
|
[SETTINGS.AUTOPLAY_NEXT]: true,
|
||||||
[SETTINGS.FLOATING_PLAYER]: true,
|
[SETTINGS.FLOATING_PLAYER]: true,
|
||||||
[SETTINGS.AUTO_DOWNLOAD]: true,
|
[SETTINGS.AUTO_DOWNLOAD]: true,
|
||||||
[SETTINGS.HIDE_REPOSTS]: false,
|
[SETTINGS.HIDE_REPOSTS]: true,
|
||||||
|
|
||||||
// OS
|
// OS
|
||||||
[SETTINGS.AUTO_LAUNCH]: true,
|
[SETTINGS.AUTO_LAUNCH]: true,
|
||||||
|
@ -88,12 +88,12 @@ reducers[ACTIONS.REHYDRATE] = (state, action) => {
|
||||||
return Object.assign({}, state, { clientSettings });
|
return Object.assign({}, state, { clientSettings });
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.FINDING_FFMPEG_STARTED] = state =>
|
reducers[ACTIONS.FINDING_FFMPEG_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
findingFFmpeg: true,
|
findingFFmpeg: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.FINDING_FFMPEG_COMPLETED] = state =>
|
reducers[ACTIONS.FINDING_FFMPEG_COMPLETED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
findingFFmpeg: false,
|
findingFFmpeg: false,
|
||||||
});
|
});
|
||||||
|
@ -119,7 +119,7 @@ reducers[ACTIONS.CLIENT_SETTING_CHANGED] = (state, action) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.UPDATE_IS_NIGHT] = state => {
|
reducers[ACTIONS.UPDATE_IS_NIGHT] = (state) => {
|
||||||
const { from, to } = state.clientSettings[SETTINGS.DARK_MODE_TIMES];
|
const { from, to } = state.clientSettings[SETTINGS.DARK_MODE_TIMES];
|
||||||
const momentNow = moment();
|
const momentNow = moment();
|
||||||
const startNightMoment = moment(from.formattedTime, 'HH:mm');
|
const startNightMoment = moment(from.formattedTime, 'HH:mm');
|
||||||
|
@ -154,7 +154,7 @@ reducers[LBRY_REDUX_ACTIONS.SHARED_PREFERENCE_SET] = (state, action) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.SYNC_CLIENT_SETTINGS] = state => {
|
reducers[ACTIONS.SYNC_CLIENT_SETTINGS] = (state) => {
|
||||||
const { clientSettings } = state;
|
const { clientSettings } = state;
|
||||||
const sharedPreferences = Object.assign({}, state.sharedPreferences);
|
const sharedPreferences = Object.assign({}, state.sharedPreferences);
|
||||||
const selectedClientSettings = getSubsetFromKeysArray(clientSettings, clientSyncKeys);
|
const selectedClientSettings = getSubsetFromKeysArray(clientSettings, clientSyncKeys);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue