Compare commits

...

19 commits

Author SHA1 Message Date
zeppi
63bd8ba420 test api 2021-06-18 10:29:18 -04:00
drewhancock
3f5caebf77
changed pins
removed some orginary gamers
added adrian logan
2021-06-16 14:16:59 -05:00
zeppi
f63956a08b bump lighthouse throttle 2021-06-16 14:45:25 -04:00
zeppi
e5b0c85747 bump lightouse throttle 2021-06-16 14:12:58 -04:00
zeppi
48bc887b1f fix lists empty on channel page 2021-06-16 12:27:16 -04:00
zeppi
aa6c7c52a9 fix simplesite crash 2021-06-16 12:10:05 -04:00
zeppi
ef5752d827 help copy for lists 2021-06-16 11:41:18 -04:00
zeppi
58c5cc38ee remove sub heart on collections 2021-06-16 11:33:31 -04:00
zeppi
583d565093 put footer back 2021-06-16 11:28:28 -04:00
zeppi
b7cb060d7d spacing hack 2021-06-16 11:17:17 -04:00
zeppi
7cb3de26cf fix 2021-06-16 11:12:27 -04:00
zeppi
33ad7e026f fix popup 2021-06-16 10:49:10 -04:00
drewhancock
f77853a0be changed pins
removed game knights
added some ordinary gamers
2021-06-16 10:17:43 -04:00
drewhancock
c5a632106e change tagline
"this cave is not a natural formation"
2021-06-16 10:17:42 -04:00
drewhancock
6196f98556 changed pins
removed fna vanlife
added game knights
2021-06-16 10:17:40 -04:00
drewhancock
f0109ba9d7 changed pins
removed some ordinary gamers
removed arvie's cookbook
added fna van life
2021-06-16 10:17:39 -04:00
drewhancock
564881e723 changed pins
someordinarygamers
arvie's cookbook
2021-06-16 10:17:38 -04:00
zeppi
e3070044b8 fix channel tabs again again 2021-06-16 10:17:37 -04:00
Thomas Zarebczan
bf4b03a4ce odysee
fix replay select styling

make meme a link

Fix audio references

get newest livestream claim in livestreamLink

pin crackermilk

fix livestream banner placement

fix live page

fix rebase

fix rebase

fix error nag

fix darkmode blockquote style

break word on livestream comment text

fix dark mode snack

fix live badge

fix lint

small fixes - word wrap, live badge

wip

Fix invisible snack in Odysee Light Theme

Revert "wip"

This reverts commit d17e477fe0.

Revert "small fixes - word wrap, live badge"

This reverts commit 0e431d4038.

fix blank

pinned destiny

fix badges and homepage again

only get livestreams live for less than a day

pinned hammy and olivia

multi pin

pin destiny

updated pinned videos

update tagline

Update view.jsx

pins

updated destiny's video

updated pinned videos

removed destiny, added lie likes music

pinned destiny and mason's woodshop

removed hammy and olivia

unpinned mason's woodshop

removed pins

added hammy and olivia
pinned sam seder
unpinned destiny and hammy and olivia
Fix merge on ChannelThumbnails

- sam seder, + hammy & olivia and passion for food
update tagline (#6086)

removed everyone, added kona and suba
Theme color fixes (odysee) (#6089)

* Cherry-pick master's 'base-theme.scss'

* Non-functional cleanup (remove dups, re-order, etc.)

* Dark: update positive Toast to --color-primary as well.

This follows the intention of the refactoring, which I guess was (1) reduce the number of color names (2) reduce the number of customizations needed.

The only issue I have with this is that the current Odysee primary color is pink, which can be intepreted as an error.

The original (pre-refactoring color was green).

For now, I follow the refactoring path. We can tweak this later.

* Fix text color inside '--color-card-background-highlighted'

Light: use base-theme (it was the same value anyway).
Dark: use bright text.

* Dark: add some contrast between the components

The color for the background, header, card, placeholder, etc. is almost identical -- it looks like there are all in the same component. The almost-invisible border doesn't help. One would have to crank up the monitor's contrast setting to see separation of components.

Brighten up the components a bit, somewhat following the same scale as lbry.tv's dark theme.

Overall, I still think it's too dark. The Card's background color can still be brightened up further for better contrast, but I try not to make too drastic of a change for now.

The original lbry.tv's gray theme is the most pleasant theme I've seen so far, but this is all subjective.
changed pins

removed kona and suba
added destiny
changed pins

removed destiny
pinned sgtducky
changed pins

removed sgtducky
added hammy and olivia
added chrissie mayr
added the bite shot
changed pins

removed the bite shot
added heads of tech
changed pins

removed hammy and olivia
removed chrissie mayr
changed pins

removed heads of tech
added crackermilk
changed pins

removed crackermilk
added some ordinary gamer
added passion for food
changed pins

removed some ordinary gamers
removed passion for food
added emmy hucker
changed pins

added game knights
Update view.jsx

Force rebuild
changed pins

removed emmy hucker
changed pins

removed game knights
added crackermilk
changed pins

removed crackermilk
added some ordinary gamer
changed pins

removed some ordinary gamers
added passion for food
added green renaissance
changed pins

removed passion for food
removed green renaissance
added expand love
changed pins

removed expand love
added dr nora
change tagline (#6122)

there's so much room for activities

comment out music

changed pins

removed dr nora
added kona and suba

changed pins

removed kona and suba
added destiny
changed pins

removed destiny
added crackermilk
changed pins

removed crackermilk
added someordinarygamers
change tagline

Drake, where's the door hole?

changed pins

unpinned someordinarygamers
pinned kona and suba
Add message for mature content

changed pin
changed pins

removed creative model

changed pins

added bcpov
added krish mohan
added cigarvixen

changed pins

removed krish mohan
added adrian logan

bump

fix footer

change tagline

just like the simulations

changed pins

removed:
bcpov
cigarvixen
adrian logan

added:
someordinarygamers

quick fix for reposts

oops

fix channel tabs

changed pin

removed someordinarygamers
added kona and suba
changed pins

removed kona and suba
added dirtyworkz
added crackermilk
2021-06-16 10:17:35 -04:00
86 changed files with 2148 additions and 740 deletions

View file

@ -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

View file

@ -879,7 +879,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",
@ -1452,6 +1451,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% new user reward.", "You earned %lbc% new user reward.": "You earned %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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -38,6 +38,9 @@ type Analytics = {
playerLoadedEvent: (?boolean) => void, playerLoadedEvent: (?boolean) => void,
playerStartedEvent: (?boolean) => void, playerStartedEvent: (?boolean) => void,
videoStartEvent: (string, number) => void, videoStartEvent: (string, number) => void,
adsFetchedEvent: () => void,
adsReceivedEvent: (any) => void,
adsErrorEvent: (any) => void,
videoBufferEvent: ( videoBufferEvent: (
StreamClaim, StreamClaim,
{ {
@ -50,9 +53,6 @@ type Analytics = {
readyState: number, readyState: number,
} }
) => void, ) => void,
adsFetchedEvent: () => void,
adsReceivedEvent: (any) => void,
adsErrorEvent: (any) => void,
emailProvidedEvent: () => void, emailProvidedEvent: () => void,
emailVerifiedEvent: () => void, emailVerifiedEvent: () => void,
rewardEligibleEvent: () => void, rewardEligibleEvent: () => void,

View file

@ -136,13 +136,14 @@ function ChannelContent(props: Props) {
{!channelIsMine && claimsInChannel > 0 && <HiddenNsfwClaims uri={uri} />} {!channelIsMine && claimsInChannel > 0 && <HiddenNsfwClaims uri={uri} />}
<ClaimListDiscover <ClaimListDiscover
defaultFreshness={CS.FRESH_ALL} hideLivestreamClaims
showHiddenByUser={viewHiddenChannels} showHiddenByUser={viewHiddenChannels}
forceShowReposts forceShowReposts
hideFilters={!showFilters} hideFilters={!showFilters}
hideAdvancedFilter={!showFilters} hideAdvancedFilter={!showFilters}
tileLayout={tileLayout} tileLayout={tileLayout}
uris={searchResults} uris={searchResults}
streamType={CS.CONTENT_ALL}
channelIds={[claim.claim_id]} channelIds={[claim.claim_id]}
claimType={claimType} claimType={claimType}
feeAmount={CS.FEE_AMOUNT_ANY} feeAmount={CS.FEE_AMOUNT_ANY}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View file

@ -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 Transparent from './transparent_1x1.png'; import Transparent from './transparent_1x1.png';
import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper'; import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper';
import ChannelStakedIndicator from 'component/channelStakedIndicator'; import ChannelStakedIndicator from 'component/channelStakedIndicator';
@ -100,7 +100,7 @@ function ChannelThumbnail(props: Props) {
ref={thumbnailRef} ref={thumbnailRef}
alt={__('Channel profile picture')} alt={__('Channel profile picture')}
className="channel-thumbnail__default" className="channel-thumbnail__default"
data-src={!thumbError && url ? url : Gerbil} data-src={!thumbError && url ? url : Spaceman}
src={Transparent} src={Transparent}
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.
/> />
@ -114,7 +114,7 @@ function ChannelThumbnail(props: Props) {
ref={thumbnailRef} ref={thumbnailRef}
alt={__('Channel profile picture')} alt={__('Channel profile picture')}
className="channel-thumbnail__custom" className="channel-thumbnail__custom"
data-src={!thumbError && url ? url : Gerbil} data-src={!thumbError && url ? url : Spaceman}
src={Transparent} src={Transparent}
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.
/> />

View file

@ -45,6 +45,7 @@ type Props = {
livestreamMap?: { [string]: any }, livestreamMap?: { [string]: any },
searchOptions?: any, searchOptions?: any,
collectionId?: string, collectionId?: string,
hideLivestreamClaims?: boolean,
}; };
export default function ClaimList(props: Props) { export default function ClaimList(props: Props) {
@ -76,6 +77,7 @@ export default function ClaimList(props: Props) {
livestreamMap, livestreamMap,
searchOptions, searchOptions,
collectionId, collectionId,
hideLivestreamClaims,
} = props; } = props;
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW); const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
@ -135,6 +137,7 @@ export default function ClaimList(props: Props) {
properties={renderProperties} properties={renderProperties}
live={resolveLive(index)} live={resolveLive(index)}
collectionId={collectionId} collectionId={collectionId}
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>}
@ -195,6 +198,7 @@ export default function ClaimList(props: Props) {
showUserBlocked={showHiddenByUser} showUserBlocked={showHiddenByUser}
showHiddenByUser={showHiddenByUser} showHiddenByUser={showHiddenByUser}
collectionId={collectionId} collectionId={collectionId}
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:

View file

@ -72,6 +72,9 @@ type Props = {
liveLivestreamsFirst?: boolean, liveLivestreamsFirst?: boolean,
livestreamMap?: { [string]: any }, livestreamMap?: { [string]: any },
hasSource?: boolean, hasSource?: boolean,
limitClaimsPerChannel?: number,
releaseTime?: string,
hideLivestreamClaims?: boolean,
isChannel?: boolean, isChannel?: boolean,
empty?: string, empty?: string,
}; };
@ -103,8 +106,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,
@ -123,12 +126,15 @@ function ClaimListDiscover(props: Props) {
forceShowReposts = false, forceShowReposts = false,
languageSetting, languageSetting,
searchInLanguage, searchInLanguage,
limitClaimsPerChannel,
releaseTime,
scrollAnchor, scrollAnchor,
showHiddenByUser = false, showHiddenByUser = false,
liveLivestreamsFirst, liveLivestreamsFirst,
livestreamMap, livestreamMap,
hasSource, hasSource,
isChannel = false, isChannel = false,
hideLivestreamClaims,
empty, empty,
} = props; } = props;
const didNavigateForward = history.action === 'PUSH'; const didNavigateForward = history.action === 'PUSH';
@ -168,12 +174,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') {
@ -217,6 +223,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,
@ -239,6 +246,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;
} }
@ -268,7 +279,10 @@ function ClaimListDiscover(props: Props) {
options.reposted_claim_id = repostedClaimId; options.reposted_claim_id = repostedClaimId;
} }
if (claimType !== CS.CLAIM_CHANNEL) { // IF release time, set it, else set fallback release times using the hack below.
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) {
@ -346,9 +360,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)) {
@ -472,7 +502,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}
@ -512,6 +542,7 @@ function ClaimListDiscover(props: Props) {
liveLivestreamsFirst={liveLivestreamsFirst} liveLivestreamsFirst={liveLivestreamsFirst}
livestreamMap={livestreamMap} livestreamMap={livestreamMap}
searchOptions={options} searchOptions={options}
hideLivestreamClaims={hideLivestreamClaims}
empty={empty} empty={empty}
/> />
{loading && ( {loading && (
@ -546,6 +577,7 @@ function ClaimListDiscover(props: Props) {
liveLivestreamsFirst={liveLivestreamsFirst} liveLivestreamsFirst={liveLivestreamsFirst}
livestreamMap={livestreamMap} livestreamMap={livestreamMap}
searchOptions={options} searchOptions={options}
hideLivestreamClaims={hideLivestreamClaims}
empty={empty} empty={empty}
/> />
{loading && {loading &&

View file

@ -188,8 +188,8 @@ function ClaimMenuList(props: Props) {
</MenuButton> </MenuButton>
<MenuList className="menu__list"> <MenuList className="menu__list">
{/* WATCH LATER */} {/* WATCH LATER */}
{isPlayable && !collectionId && (
<> <>
{isPlayable && !collectionId && (
<MenuItem <MenuItem
className="comment__menu-option" className="comment__menu-option"
onSelect={() => { onSelect={() => {
@ -210,6 +210,7 @@ function ClaimMenuList(props: Props) {
{hasClaimInWatchLater ? __('In Watch Later') : __('Watch Later')} {hasClaimInWatchLater ? __('In Watch Later') : __('Watch Later')}
</div> </div>
</MenuItem> </MenuItem>
)}
{/* COLLECTION OPERATIONS */} {/* COLLECTION OPERATIONS */}
{collectionId && collectionName && isCollectionClaim && ( {collectionId && collectionName && isCollectionClaim && (
<> <>
@ -254,7 +255,6 @@ function ClaimMenuList(props: Props) {
</MenuItem> </MenuItem>
)} )}
</> </>
)}
{!isChannelPage && ( {!isChannelPage && (
<> <>
<hr className="menu__separator" /> <hr className="menu__separator" />

View file

@ -30,7 +30,7 @@ import * as ICONS from 'constants/icons';
type Props = { type Props = {
uri: string, uri: string,
claim: ?Claim, // maybe? claim: ?Claim,
obscureNsfw: boolean, obscureNsfw: boolean,
showUserBlocked: boolean, showUserBlocked: boolean,
claimIsMine: boolean, claimIsMine: boolean,

View file

@ -44,6 +44,8 @@ type Props = {
properties?: (Claim) => void, properties?: (Claim) => void,
live?: boolean, live?: boolean,
collectionId?: string, collectionId?: string,
hideLivestreamClaims?: boolean,
isLivestream: boolean,
}; };
function ClaimPreviewTile(props: Props) { function ClaimPreviewTile(props: Props) {
@ -66,6 +68,8 @@ function ClaimPreviewTile(props: Props) {
showHiddenByUser, showHiddenByUser,
properties, properties,
live, live,
hideLivestreamClaims,
isLivestream,
collectionId, collectionId,
} = props; } = props;
const isRepost = claim && claim.repost_channel_url; const isRepost = claim && claim.repost_channel_url;
@ -149,7 +153,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;
} }
@ -213,7 +217,6 @@ function ClaimPreviewTile(props: Props) {
<UriIndicator uri={uri} /> <UriIndicator uri={uri} />
</div> </div>
)} )}
{/* CHECK CLAIM MENU LIST PARAMS (IS REPOST?) */}
<ClaimMenuList uri={uri} collectionId={listId} channelUri={channelUri} isRepost={isRepost} /> <ClaimMenuList uri={uri} collectionId={listId} channelUri={channelUri} isRepost={isRepost} />
</h2> </h2>
</NavLink> </NavLink>

View file

@ -37,7 +37,7 @@ export function prioritizeActiveLivestreams(
// for that channel actually point to the same source). // for that channel actually point to the same source).
// 2. 'liveChannelIds' needs to be pruned after being accounted for, // 2. 'liveChannelIds' needs to be pruned after being accounted for,
// otherwise all livestream-claims will be "live" (we'll only take the // otherwise all livestream-claims will be "live" (we'll only take the
// latest one as "live"). // latest one as "live" ).
return ( return (
claim && claim &&
claim.value_type === 'stream' && claim.value_type === 'stream' &&
@ -145,14 +145,14 @@ function ClaimTilesDiscover(props: Props) {
mutedUris, mutedUris,
liveLivestreamsFirst, liveLivestreamsFirst,
livestreamMap, livestreamMap,
// pin, pin,
prefixUris, prefixUris,
} = 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 mutedAndBlockedChannelIds = Array.from(new Set(mutedUris.concat(blockedUris).map((uri) => uri.split('#')[1]))); const mutedAndBlockedChannelIds = Array.from(new Set(mutedUris.concat(blockedUris).map((uri) => uri.split('#')[1])));
const liveUris = []; const liveUris = [];
@ -239,8 +239,8 @@ function ClaimTilesDiscover(props: Props) {
let uris = (prefixUris || []).concat(claimSearchByQuery[mainSearchKey] || []); let uris = (prefixUris || []).concat(claimSearchByQuery[mainSearchKey] || []);
const isLoading = fetchingClaimSearchByQuery[mainSearchKey]; const isLoading = fetchingClaimSearchByQuery[mainSearchKey];
// && !isLoading isn't on master, doesn't seem to affect the functionality, don't recall why it was added.
if (liveLivestreamsFirst && livestreamMap) { if (liveLivestreamsFirst && livestreamMap && !isLoading) {
prioritizeActiveLivestreams(uris, liveUris, livestreamMap, claimsByUri, claimSearchByQuery, options); prioritizeActiveLivestreams(uris, liveUris, livestreamMap, claimsByUri, claimSearchByQuery, options);
} }
@ -284,10 +284,26 @@ function ClaimTilesDiscover(props: Props) {
return undefined; return undefined;
}; };
const modifiedUris = uris ? uris.slice() : [];
const fixUris = [
'lbry://@AdrianLogan#5/why-i-talk-to-myself#3',
];
if (pin && modifiedUris && modifiedUris.length > 2 && window.location.pathname === '/') {
fixUris.forEach((fixUri) => {
if (modifiedUris.indexOf(fixUri) !== -1) {
modifiedUris.splice(modifiedUris.indexOf(fixUri), 1);
} else {
modifiedUris.pop();
}
});
modifiedUris.splice(2, 0, ...fixUris);
}
return ( return (
<ul className="claim-grid"> <ul className="claim-grid">
{uris && uris.length {modifiedUris && modifiedUris.length
? uris.map((uri, index) => ( ? modifiedUris.map((uri, index) => (
<ClaimPreviewTile key={uri} uri={uri} properties={renderProperties} live={resolveLive(index)} /> <ClaimPreviewTile key={uri} uri={uri} properties={renderProperties} live={resolveLive(index)} />
)) ))
: new Array(pageSize).fill(1).map((x, i) => <ClaimPreviewTile key={i} placeholder />)} : new Array(pageSize).fill(1).map((x, i) => <ClaimPreviewTile key={i} placeholder />)}

View file

@ -10,6 +10,7 @@ import * as PAGES from 'constants/pages';
import Yrbl from 'component/yrbl'; import Yrbl from 'component/yrbl';
import usePersistedState from 'effects/use-persisted-state'; import usePersistedState from 'effects/use-persisted-state';
import Card from 'component/common/card'; import Card from 'component/common/card';
import classnames from 'classnames';
type Props = { type Props = {
builtinCollections: CollectionGroup, builtinCollections: CollectionGroup,
@ -39,14 +40,13 @@ export default function CollectionsListMine(props: Props) {
const helpText = ( const helpText = (
<div className="section__subtitle"> <div className="section__subtitle">
<p>{__(`Thanks for checking out our new lists feature!`)}</p> <p>{__(`Everyone starts with 2 private lists - Watch Later and Favorites.`)}</p>
<p>{__(`From content pages or content preview menus, you can add content to lists or add new lists.`)}</p> <p>{__(`Add content to existing lists or new lists from content pages or content previews.`)}</p>
<p> <p>
{__( {__(
`Right now lists are for playable content only, but soon you can make lists of lists of lists of lists! Listception!` `By default, lists are private. You can edit them and later publish them from the Lists page or the Publish context menu on this page. Similar to uploads, small blockchain fees apply.`
)} )}
</p> </p>
<p>{__(`Keep a list private, or publish it (transaction fees and bidding apply).`)}</p>
</div> </div>
); );
@ -104,7 +104,12 @@ export default function CollectionsListMine(props: Props) {
/> />
)} )}
{Boolean(hasCollections) && ( {Boolean(hasCollections) && (
<div className="section"> <div
className={classnames({
section: showHelp,
})}
>
{/* TODO: fix above spacing hack */}
<div className="claim-grid"> <div className="claim-grid">
{unpublishedCollectionsList && {unpublishedCollectionsList &&
unpublishedCollectionsList.length > 0 && unpublishedCollectionsList.length > 0 &&

View file

@ -31,11 +31,15 @@ type Props = {
isReply: boolean, isReply: boolean,
activeChannel: string, activeChannel: string,
activeChannelClaim: ?ChannelClaim, activeChannelClaim: ?ChannelClaim,
livestream?: boolean, bottom: boolean,
onSubmit: (string, string) => void,
livestream: boolean,
embed?: boolean,
toast: (string) => void, toast: (string) => void,
claimIsMine: boolean, claimIsMine: boolean,
sendTip: ({}, (any) => void, (any) => void) => void, sendTip: ({}, (any) => void, (any) => void) => void,
justCommented: Array<string>, justCommented: Array<string>,
commentingEnabled: boolean,
}; };
export function CommentCreate(props: Props) { export function CommentCreate(props: Props) {
@ -50,10 +54,14 @@ export function CommentCreate(props: Props) {
isReply, isReply,
parentId, parentId,
activeChannelClaim, activeChannelClaim,
onSubmit,
bottom,
livestream, livestream,
embed,
toast, toast,
claimIsMine, claimIsMine,
sendTip, sendTip,
commentingEnabled,
justCommented, justCommented,
} = props; } = props;
const buttonref: ElementRef<any> = React.useRef(); const buttonref: ElementRef<any> = React.useRef();
@ -88,7 +96,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();
} }
@ -166,6 +174,10 @@ export function CommentCreate(props: Props) {
setCommentFailure(false); setCommentFailure(false);
justCommented.push(res.comment_id); justCommented.push(res.comment_id);
if (onSubmit) {
onSubmit(commentValue, activeChannelClaim.name);
}
if (onDoneReplying) { if (onDoneReplying) {
onDoneReplying(); onDoneReplying();
} }
@ -181,11 +193,16 @@ export function CommentCreate(props: Props) {
setAdvancedEditor(!advancedEditor); setAdvancedEditor(!advancedEditor);
} }
if (!hasChannels) { if (!commentingEnabled || !hasChannels) {
return ( return (
<div <div
role="button" role="button"
onClick={() => { onClick={() => {
if (embed) {
window.open(`https://odysee.com/$/${PAGES.AUTH}?redirect=/$/${PAGES.LIVESTREAM}`);
return;
}
const pathPlusRedirect = `/$/${PAGES.CHANNEL_NEW}?redirect=${pathname}`; const pathPlusRedirect = `/$/${PAGES.CHANNEL_NEW}?redirect=${pathname}`;
if (livestream) { if (livestream) {
window.open(pathPlusRedirect); window.open(pathPlusRedirect);
@ -239,6 +256,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

View file

@ -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),
})} })}

View file

@ -1,5 +1,5 @@
// @flow // @flow
import { SITE_NAME, ENABLE_FILE_REACTIONS } from 'config'; import { SIMPLE_SITE, SITE_NAME, ENABLE_FILE_REACTIONS } from 'config';
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';
@ -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,
reactionsDisabled: boolean, reactionsDisabled: boolean,
}; };
@ -43,6 +44,7 @@ function FileActions(props: Props) {
myChannels, myChannels,
clearPlayingUri, clearPlayingUri,
doToast, doToast,
hideRepost,
isLivestreamClaim, isLivestreamClaim,
reactionsDisabled, reactionsDisabled,
} = props; } = props;
@ -88,24 +90,26 @@ function FileActions(props: Props) {
const lhsSection = ( const lhsSection = (
<> <>
{ENABLE_FILE_REACTIONS && !reactionsDisabled && <FileReactions uri={uri} />} {ENABLE_FILE_REACTIONS && !reactionsDisabled && <FileReactions uri={uri} livestream={isLivestreamClaim} />}
<ClaimSupportButton uri={uri} fileAction /> <ClaimSupportButton uri={uri} fileAction />
<ClaimCollectionAddButton uri={uri} fileAction /> <ClaimCollectionAddButton 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, collectionId })} onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, webShareable, collectionId })}
/> />
@ -114,7 +118,7 @@ function FileActions(props: Props) {
const rhsSection = ( const rhsSection = (
<> <>
<FileDownloadLink uri={uri} /> {!SIMPLE_SITE && <FileDownloadLink uri={uri} />}
{claimIsMine && ( {claimIsMine && (
<Button <Button

View file

@ -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';
@ -15,18 +16,44 @@ type Props = {
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;
const isCollection = claim && claim.value_type === 'collection'; // hack because nudge gets cut off by card on cols. const isCollection = claim && claim.value_type === 'collection'; // hack because nudge gets cut off by card on cols.
React.useEffect(() => { React.useEffect(() => {
if (claimId) { function fetchReactions() {
doFetchReactions(claimId); doFetchReactions(claimId);
} }
}, [claimId, doFetchReactions]);
let fetchInterval;
if (claimId) {
fetchReactions();
if (livestream) {
fetchInterval = setInterval(fetchReactions, 45000);
}
}
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)}
/> />
</> </>

View file

@ -21,8 +21,8 @@ function FileSubtitle(props: Props) {
<FileViewCount uri={uri} livestream={livestream} activeViewers={activeViewers} isLive={isLive} /> <FileViewCount uri={uri} livestream={livestream} activeViewers={activeViewers} isLive={isLive} />
</div> </div>
{/* did I need these params? */}
<FileActions uri={uri} /> <FileActions uri={uri} hideRepost={livestream} livestream={livestream} />
</div> </div>
); );
} }

View file

@ -10,7 +10,6 @@ import * as ICONS from 'constants/icons';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
import Button from 'component/button'; import Button from 'component/button';
import * as PAGES from 'constants/pages';
import FileDescription from 'component/fileDescription'; import FileDescription from 'component/fileDescription';
import usePersistedState from 'effects/use-persisted-state'; import usePersistedState from 'effects/use-persisted-state';
@ -65,17 +64,16 @@ function FileTitleSection(props: Props) {
<div className="main--empty"> <div className="main--empty">
<h2> <h2>
<Icon className="icon--hidden" icon={ICONS.EYE_OFF} /> <Icon className="icon--hidden" icon={ICONS.EYE_OFF} />
{__('Mature content blocked.')} {__('Mature content is not supported on Odysee.')}
</h2> </h2>
<div> <div>
<I18nMessage <I18nMessage
tokens={{ tokens={{
content_settings: ( download_url: <Button label={__('lbry.com')} button="link" href="https://lbry.com/get" />,
<Button button="link" label={__('content settings')} navigate={`/$/${PAGES.SETTINGS}`} />
),
}} }}
> >
Change this in your %content_settings%. You can download the LBRY Desktop or Android app on %download_url% and enable mature content in
Settings.
</I18nMessage> </I18nMessage>
</div> </div>
</div> </div>

View file

@ -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>

View file

@ -1,5 +1,5 @@
// @flow // @flow
import { LOGO_TITLE, ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM } from 'config'; import { ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM } from 'config';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import { SETTINGS } from 'lbry-redux'; import { SETTINGS } from 'lbry-redux';
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
@ -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,
@ -87,8 +89,8 @@ const Header = (props: Props) => {
sidebarOpen, sidebarOpen,
setSidebarOpen, setSidebarOpen,
isAbsoluteSideNavHidden, isAbsoluteSideNavHidden,
user,
hideCancel, hideCancel,
user,
activeChannelClaim, activeChannelClaim,
activeChannelStakedLevel, activeChannelStakedLevel,
} = props; } = props;
@ -100,7 +102,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;
const livestreamEnabled = Boolean( const livestreamEnabled = Boolean(
ENABLE_NO_SOURCE_CLAIMS && ENABLE_NO_SOURCE_CLAIMS &&
user && user &&
@ -240,19 +242,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();
}} }}
@ -262,7 +257,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">
@ -279,7 +279,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}
@ -432,6 +432,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}`)}>

View 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);

View 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} />
</>
);
}

View file

@ -17,7 +17,7 @@ export default function LivestreamLink(props: Props) {
const { push } = useHistory(); const { push } = useHistory();
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) {
@ -29,7 +29,7 @@ export default function LivestreamLink(props: Props) {
}) })
.then((res) => { .then((res) => {
if (res && res.items && res.items.length > 0) { if (res && res.items && res.items.length > 0) {
const claim = res.items[res.items.length - 1]; const claim = res.items[0];
setLivestreamClaim(claim); setLivestreamClaim(claim);
} }
}) })

View file

@ -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;
} }

View file

@ -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}

View file

@ -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,

View file

@ -65,7 +65,7 @@ export default function PreviewOverlayProperties(props: Props) {
{isCollection && claim && !iconOnly && <div>{claimCount}</div>} {isCollection && claim && !iconOnly && <div>{claimCount}</div>}
{!iconOnly && isStream && <VideoDuration uri={uri} />} {!iconOnly && isStream && <VideoDuration uri={uri} />}
{isStream && <FileType uri={uri} small={small} />} {isStream && <FileType uri={uri} small={small} />}
{isSubscribed && <Icon tooltip size={size} icon={ICONS.SUBSCRIBE} />} {isSubscribed && !isCollection && <Icon tooltip size={size} icon={ICONS.SUBSCRIBE} />}
{!claimIsMine && downloaded && <Icon size={size} tooltip icon={ICONS.LIBRARY} />} {!claimIsMine && downloaded && <Icon size={size} tooltip icon={ICONS.LIBRARY} />}
<FilePrice hideFree uri={uri} /> <FilePrice hideFree uri={uri} />
</> </>

View file

@ -612,7 +612,7 @@ function PublishFile(props: Props) {
{/* @if TARGET='app' */} {/* @if TARGET='app' */}
{showFileUpload && ( {showFileUpload && (
<FileSelector <FileSelector
label={__('File')} label={__('Video file')}
disabled={disabled} disabled={disabled}
currentPath={currentFile} currentPath={currentFile}
onFileChosen={handleFileChange} onFileChosen={handleFileChange}

View file

@ -182,9 +182,9 @@ function PublishForm(props: Props) {
} }
} else { } else {
if (editingURI) { if (editingURI) {
customSubtitle = __('Update your video'); customSubtitle = __('Update your video/audio');
} else { } else {
customSubtitle = __('Upload that unlabeled video you found behind the TV in 1991'); customSubtitle = __('Upload that unlabeled video or cassette you found behind the TV in 1991');
} }
} }
@ -566,6 +566,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={() => {

View file

@ -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) {

View file

@ -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://${incognito || !activeChannelClaim ? '' : `${activeChannelClaim.name}/`}`; const repostUrlName = `lbry://${incognito || !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>

View file

@ -123,7 +123,7 @@ class SelectThumbnail extends React.PureComponent<Props> {
<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 })}
/> />

View file

@ -212,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> = [];
@ -306,6 +306,11 @@ 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">
<Button label={__('Community Guidelines')} href="https://odysee.com/@OdyseeHelp:b/Community-Guidelines:c" />
</li>
<li className="navigation-link"> <li className="navigation-link">
<Button label={__('Support --[used in footer; general help/support]--')} href="https://lbry.com/support" /> <Button label={__('Support --[used in footer; general help/support]--')} href="https://lbry.com/support" />
</li> </li>

View file

@ -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, {

View file

@ -43,7 +43,6 @@ export default function SubscribeButton(props: Props) {
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: rawChannelName } = parseURI(uri); const { channelName: rawChannelName } = parseURI(uri);
const { channelName } = parseURI(permanentUrl); const { channelName } = parseURI(permanentUrl);
@ -109,7 +108,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}

View file

@ -11,7 +11,6 @@ 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,
prefsReady: boolean, prefsReady: boolean,
@ -45,25 +44,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

View file

@ -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' }} />

View file

@ -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

View file

@ -52,14 +52,14 @@ type Props = {
adUrl: ?string, adUrl: ?string,
}; };
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];
@ -68,7 +68,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,

View file

@ -114,12 +114,12 @@ function VideoViewer(props: Props) {
const { const {
location: { pathname }, location: { pathname },
} = useHistory(); } = useHistory();
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);
const vjsCallbackDataRef: any = React.useRef(); const vjsCallbackDataRef: any = React.useRef();
const previousUri = usePrevious(uri);
const embedded = useContext(EmbedContext);
const approvedVideo = Boolean(channelClaimId) && adApprovedChannelIds.includes(channelClaimId); const approvedVideo = Boolean(channelClaimId) && adApprovedChannelIds.includes(channelClaimId);
const adsEnabled = ENABLE_PREROLL_ADS && !authenticated && !embedded && approvedVideo; const adsEnabled = ENABLE_PREROLL_ADS && !authenticated && !embedded && approvedVideo;
const [adUrl, setAdUrl, isFetchingAd] = useGetAds(approvedVideo, adsEnabled); const [adUrl, setAdUrl, isFetchingAd] = useGetAds(approvedVideo, adsEnabled);

View file

@ -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 */}

View file

@ -46,6 +46,7 @@ export default function WunderBarSuggestions(props: Props) {
const inputRef: ElementRef<any> = React.useRef(); const inputRef: ElementRef<any> = React.useRef();
const isFocused = inputRef && inputRef.current && inputRef.current === document.activeElement; const isFocused = inputRef && inputRef.current && inputRef.current === document.activeElement;
const THROTTLE_MS = 1000;
const { const {
push, push,
location: { search }, location: { search },
@ -53,7 +54,7 @@ export default function WunderBarSuggestions(props: Props) {
const urlParams = new URLSearchParams(search); const urlParams = new URLSearchParams(search);
const queryFromUrl = urlParams.get('q') || ''; const queryFromUrl = urlParams.get('q') || '';
const [term, setTerm] = React.useState(queryFromUrl); const [term, setTerm] = React.useState(queryFromUrl);
const throttledTerm = useThrottle(term, 500) || ''; const throttledTerm = useThrottle(term, THROTTLE_MS) || '';
const searchSize = isMobile ? 20 : 5; const searchSize = isMobile ? 20 : 5;
const { results, loading } = useLighthouse(throttledTerm, showMature, searchSize); const { results, loading } = useLighthouse(throttledTerm, showMature, searchSize);
const noResults = throttledTerm && !loading && results && results.length === 0; const noResults = throttledTerm && !loading && results && results.length === 0;

View file

@ -118,9 +118,7 @@ export default function YoutubeTransferStatus(props: Props) {
{isNotElligible && ( {isNotElligible && (
<I18nMessage <I18nMessage
tokens={{ tokens={{
here: ( here: <Button button="link" href="https://lbry.com/faq/youtube" label={__('here')} />,
<Button button="link" href="https://lbry.com/faq/youtube" label={__('here')} />
),
email: SITE_HELP_EMAIL, email: SITE_HELP_EMAIL,
}} }}
> >

View file

@ -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',
];

View file

@ -1,6 +1,6 @@
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 = 500; 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;
export const FF_MAX_CHARS_REPORT_CONTENT_DETAILS = 500; export const FF_MAX_CHARS_REPORT_CONTENT_DETAILS = 500;
export const FF_MAX_CHARS_REPORT_CONTENT_ADDRESS = 255; export const FF_MAX_CHARS_REPORT_CONTENT_ADDRESS = 255;

View file

@ -1,3 +1,3 @@
export const BITWAVE_EMBED_URL = 'https://bitwave.tv/odysee'; export const BITWAVE_EMBED_URL = 'https://bitwave.tv/odysee';
export const BITWAVE_LIVE_API = 'https://api.bitwave.tv/v1/odysee/live'; export const BITWAVE_LIVE_API = 'https://api.live.odysee.com/v1/odysee/live';
export const BITWAVE_REPLAY_API = 'https://api.bitwave.tv/v1/replays/odysee'; export const BITWAVE_REPLAY_API = 'https://api.bitwave.tv/v1/replays/odysee';

View file

@ -6,10 +6,11 @@ import { isURIValid } from 'lbry-redux';
import useThrottle from './use-throttle'; import useThrottle from './use-throttle';
export default function useLighthouse(query: string, showMature?: boolean, size?: number = 5) { export default function useLighthouse(query: string, showMature?: boolean, size?: number = 5) {
const THROTTLE_MS = 1000;
const [results, setResults] = React.useState(); const [results, setResults] = React.useState();
const [loading, setLoading] = React.useState(); const [loading, setLoading] = React.useState();
const queryString = query ? getSearchQueryString(query, { nsfw: showMature, size }) : ''; const queryString = query ? getSearchQueryString(query, { nsfw: showMature, size }) : '';
const throttledQuery = useThrottle(queryString, 500); const throttledQuery = useThrottle(queryString, THROTTLE_MS);
React.useEffect(() => { React.useEffect(() => {
if (throttledQuery) { if (throttledQuery) {
@ -19,9 +20,11 @@ export default function useLighthouse(query: string, showMature?: boolean, size?
let isSubscribed = true; let isSubscribed = true;
lighthouse lighthouse
.search(throttledQuery) .search(throttledQuery)
.then(results => { .then((results) => {
if (isSubscribed) { if (isSubscribed) {
setResults(results.map(result => `lbry://${result.name}#${result.claimId}`).filter(uri => isURIValid(uri))); setResults(
results.map((result) => `lbry://${result.name}#${result.claimId}`).filter((uri) => isURIValid(uri))
);
setLoading(false); setLoading(false);
} }
}) })

View file

@ -44,8 +44,9 @@ 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() {
closeModal(); closeModal();
@ -54,7 +55,7 @@ class ModalPublishSuccess extends React.PureComponent<Props> {
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>

View file

@ -8,6 +8,7 @@ import {
selectMyChannelClaims, selectMyChannelClaims,
makeSelectClaimIsStreamPlaceholder, makeSelectClaimIsStreamPlaceholder,
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';
@ -32,6 +33,7 @@ const select = (state, props) => {
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)),
}); });

View file

@ -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"

View file

@ -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>
); );
} }

View file

@ -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
View 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
View 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>;
}

View file

@ -22,6 +22,7 @@ const MOONPAY_KEY = process.env.MOONPAY_SECRET_KEY;
const COUNTRIES = Array.from( const COUNTRIES = Array.from(
new Set( new Set(
countryData.all countryData.all
.filter((country) => country.status !== 'deleted')
.map((country) => country.name) .map((country) => country.name)
.sort((a, b) => { .sort((a, b) => {
if (a > b) { if (a > b) {

View file

@ -125,7 +125,7 @@ function ChannelPage(props: Props) {
<section className="main--empty">{__('You have no lists! Create one from any playable content.')}</section> <section className="main--empty">{__('You have no lists! Create one from any playable content.')}</section>
); );
} else { } else {
collectionEmpty = <section className="main--empty">{__('You have unpublished lists')}</section>; collectionEmpty = <section className="main--empty">{__('No Lists Found')}</section>;
} }
let channelIsBlackListed = false; let channelIsBlackListed = false;

View file

@ -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>
); );

View file

@ -2,6 +2,7 @@
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 PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
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';
@ -11,11 +12,11 @@ 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 useGetLivestreams from 'effects/use-get-livestreams'; import useGetLivestreams from 'effects/use-get-livestreams';
import moment from 'moment';
type Props = { type Props = {
location: { search: string }, location: { search: string },
@ -97,8 +98,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>
); );
} }
@ -106,9 +107,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}
@ -117,9 +120,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

View file

@ -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';
@ -11,6 +12,9 @@ import RecommendedContent from 'component/recommendedContent';
import CollectionContent from 'component/collectionContentSidebar'; import CollectionContent from 'component/collectionContentSidebar';
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';
@ -30,7 +34,9 @@ type Props = {
collection?: Collection, collection?: Collection,
collectionId: string, collectionId: string,
videoTheaterMode: boolean, videoTheaterMode: boolean,
claimIsMine: boolean,
commentsDisabled: boolean, commentsDisabled: boolean,
isLivestream: boolean,
}; };
function FilePage(props: Props) { function FilePage(props: Props) {
@ -47,9 +53,12 @@ function FilePage(props: Props) {
linkedComment, linkedComment,
setPrimaryUri, setPrimaryUri,
videoTheaterMode, videoTheaterMode,
claimIsMine,
commentsDisabled, commentsDisabled,
collection, collection,
collectionId, collectionId,
isLivestream,
} = props; } = props;
const cost = costInfo ? costInfo.cost : null; const cost = costInfo ? costInfo.cost : null;
const hasFileInfo = fileInfo !== undefined; const hasFileInfo = fileInfo !== undefined;
@ -118,6 +127,10 @@ function FilePage(props: Props) {
); );
} }
if (!claimIsMine && isLivestream) {
return <Redirect to={`/$/${PAGES.LIVESTREAM}`} />;
}
if (obscureNsfw && isMature) { if (obscureNsfw && isMature) {
return ( return (
<Page className="file-page" filePage isMarkdown={isMarkdown}> <Page className="file-page" filePage isMarkdown={isMarkdown}>
@ -138,6 +151,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} />}

View file

@ -81,6 +81,13 @@ function HomePage(props: Props) {
</p> </p>
</div> </div>
)} )}
<h1 className="home__meme">
<Button button="link" href="https://odysee.com/@Odysee:8?view=discussion">
{__("this cave is not a natural formation")}
</Button>
</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">
{index !== 0 && title && typeof title === 'string' && ( {index !== 0 && title && typeof title === 'string' && (
@ -93,7 +100,13 @@ function HomePage(props: Props) {
</h1> </h1>
)} )}
<ClaimTilesDiscover {...options} liveLivestreamsFirst livestreamMap={livestreamMap} hasSource /> <ClaimTilesDiscover
{...options}
liveLivestreamsFirst
livestreamMap={livestreamMap}
hasSource
pin={route === `/$/${PAGES.GENERAL}`}
/>
{(route || link) && ( {(route || link) && (
<Button <Button
className="claim-grid__title--secondary" className="claim-grid__title--secondary"

View file

@ -14,12 +14,12 @@ import { selectUserVerifiedEmail } from 'redux/selectors/user';
import SearchPage from './view'; import SearchPage from './view';
const select = (state, props) => { const select = (state, props) => {
const showMature = selectShowMatureContent(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 showMature = selectShowMatureContent(state);
const query = makeSelectQueryWithOptions( const query = makeSelectQueryWithOptions(
urlQuery, urlQuery,
SIMPLE_SITE SIMPLE_SITE

View file

@ -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 = {
@ -170,7 +170,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
const { const {
daemonSettings, daemonSettings,
allowAnalytics, allowAnalytics,
showNsfw, // showNsfw,
isAuthenticated, isAuthenticated,
currentTheme, currentTheme,
themes, themes,
@ -183,12 +183,12 @@ class SettingsPage extends React.PureComponent<Props, State> {
setClientSetting, setClientSetting,
toggle3PAnalytics, toggle3PAnalytics,
floatingPlayer, floatingPlayer,
hideReposts, // hideReposts,
clearPlayingUri, clearPlayingUri,
darkModeTimes, darkModeTimes,
clearCache, clearCache,
openModal,
myChannelUrls, myChannelUrls,
// 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;
@ -358,7 +358,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
)} )}
/> />
<FormField {/* <FormField
type="checkbox" type="checkbox"
name="hide_reposts" name="hide_reposts"
onChange={(e) => { onChange={(e) => {
@ -372,7 +372,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"
@ -383,7 +383,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={() =>
@ -396,7 +396,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>
} }
/> />

View file

@ -30,6 +30,7 @@ type Props = {
claimIsMine: boolean, claimIsMine: boolean,
claimIsPending: boolean, claimIsPending: boolean,
isLivestream: boolean, isLivestream: boolean,
claimIsMine: Boolean,
beginPublish: (string) => void, beginPublish: (string) => void,
collectionId: string, collectionId: string,
collection: Collection, collection: Collection,

View file

@ -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,

View file

@ -1,5 +1,6 @@
// @flow // @flow
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import { SEARCH_OPTIONS } from 'constants/search';
import { buildURI, doResolveUris, batchActions, isURIValid, makeSelectClaimForUri } from 'lbry-redux'; import { buildURI, doResolveUris, batchActions, isURIValid, makeSelectClaimForUri } from 'lbry-redux';
import { import {
makeSelectSearchUris, makeSelectSearchUris,
@ -135,6 +136,10 @@ export const doFetchRecommendedContent = (uri: string, mature: boolean) => (disp
if (!mature) { if (!mature) {
options['nsfw'] = false; options['nsfw'] = false;
} }
options[SEARCH_OPTIONS.CLAIM_TYPE] = SEARCH_OPTIONS.INCLUDE_FILES;
options[SEARCH_OPTIONS.MEDIA_VIDEO] = true;
const { title } = claim.value; const { title } = claim.value;
if (title && options) { if (title && options) {
dispatch(doSearch(title, options)); dispatch(doSearch(title, options));

View file

@ -11,9 +11,9 @@ const defaultState: SearchState = {
[SEARCH_OPTIONS.CLAIM_TYPE]: SEARCH_OPTIONS.INCLUDE_FILES_AND_CHANNELS, [SEARCH_OPTIONS.CLAIM_TYPE]: SEARCH_OPTIONS.INCLUDE_FILES_AND_CHANNELS,
[SEARCH_OPTIONS.MEDIA_AUDIO]: true, [SEARCH_OPTIONS.MEDIA_AUDIO]: true,
[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: {},
hasReachedMaxResultsLength: {}, hasReachedMaxResultsLength: {},

View file

@ -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 = {};
@ -70,7 +70,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,
@ -89,12 +89,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,
}); });
@ -120,7 +120,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');
@ -155,7 +155,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);

View file

@ -620,6 +620,12 @@ svg + .button__label {
border-top-left-radius: 0; border-top-left-radius: 0;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
.button--file-action {
&:first-child {
margin-right: var(--spacing-s);
}
}
} }
.button--file-action { .button--file-action {

View file

@ -211,13 +211,28 @@
} }
.file-viewer__overlay-logo { .file-viewer__overlay-logo {
color: var(--color-white); height: 3.5rem;
font-weight: bold; width: 12rem;
display: flex;
align-items: center;
.icon { &:hover {
height: 30px; filter: drop-shadow(1px 2px 10px var(--color-gray-3));
stroke-width: 5px;
} }
@media (max-width: $breakpoint-small) {
margin-right: var(--spacing-m);
width: 2.5rem;
.button__label {
display: none;
}
}
}
.file-viewer__overlay-logo--videoend {
height: 3.5rem;
width: 12rem;
} }
.file-viewer--is-playing:not(:hover) .file-viewer__embedded-header { .file-viewer--is-playing:not(:hover) .file-viewer__embedded-header {
@ -227,11 +242,15 @@
.file-viewer__embedded-header { .file-viewer__embedded-header {
position: absolute; position: absolute;
display: flex; display: flex;
align-items: center;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
top: 0; top: 0;
opacity: 1; opacity: 1;
z-index: 2; z-index: 2;
height: 4rem;
padding-left: var(--spacing-m);
padding-right: var(--spacing-s);
font-size: var(--font-large); font-size: var(--font-large);
overflow-x: hidden; overflow-x: hidden;
overflow-y: hidden; overflow-y: hidden;
@ -241,7 +260,6 @@
border-top-right-radius: var(--border-radius); border-top-right-radius: var(--border-radius);
.button { .button {
padding: var(--spacing-s);
color: var(--color-white); color: var(--color-white);
z-index: 2; z-index: 2;

View file

@ -203,7 +203,7 @@ input-submit {
height: var(--height-checkbox); height: var(--height-checkbox);
width: var(--height-checkbox); width: var(--height-checkbox);
border: 1px solid var(--color-input-border); border: 1px solid var(--color-input-border);
border-radius: var(--border-radius); border-radius: 3px;
left: 0px; left: 0px;
top: -1px; top: -1px;
} }

View file

@ -58,7 +58,6 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
border-radius: var(--border-radius); border-radius: var(--border-radius);
color: var(--color-text);
position: relative; position: relative;
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);

View file

@ -281,8 +281,8 @@ $discussion-header__height: 3rem;
} }
.livestream-superchat__banner { .livestream-superchat__banner {
border-top-right-radius: var(--border-radius); border-top-left-radius: 0;
border-bottom-right-radius: var(--border-radius); border-bottom-left-radius: 0;
padding: 0.25rem var(--spacing-s); padding: 0.25rem var(--spacing-s);
display: inline-block; display: inline-block;
position: relative; position: relative;
@ -298,7 +298,7 @@ $discussion-header__height: 3rem;
background-color: var(--color-superchat); background-color: var(--color-superchat);
bottom: 0; bottom: 0;
left: 0; left: 0;
transform: translateX(25%) translateY(50%); transform: translateX(0) translateY(50%);
&::after { &::after {
content: ''; content: '';
@ -316,6 +316,11 @@ $discussion-header__height: 3rem;
.livestream-comment__text { .livestream-comment__text {
padding-right: var(--spacing-xxs); padding-right: var(--spacing-xxs);
padding-bottom: var(--spacing-xxs); padding-bottom: var(--spacing-xxs);
.markdown-preview {
p {
word-break: break-all;
}
}
} }
.livestream-superchat__tooltip-amount { .livestream-superchat__tooltip-amount {

View file

@ -39,6 +39,10 @@
overflow-y: auto; overflow-y: auto;
} }
} }
@media (min-width: $breakpoint-small) {
justify-content: space-between;
}
} }
.navigation--mac { .navigation--mac {

View file

@ -58,7 +58,7 @@
.section__title { .section__title {
text-align: left; text-align: left;
font-size: var(--font-title); font-size: var(--font-large);
font-weight: var(--font-weight-light); font-weight: var(--font-weight-light);
} }

View file

@ -1,57 +0,0 @@
:root {
// Generic colors
--color-primary: #257761;
--color-primary-alt: #e4f4ef;
--color-primary-alt-2: #4b8576;
--color-secondary: #295284;
--color-secondary-alt: #d9eaff;
--color-tertiary: #552470;
--color-tertiary-alt: #f7e8ff;
--color-danger: #9b2023;
--color-danger-alt: #fccdce;
--color-warning: #fff58c;
--color-cost: #ffd580;
--color-focus: #93cff2;
--color-notification: #f02849;
--color-live: #cc190f;
--color-black: #111;
--color-white: #fdfdfd;
--color-white-alt: #fafafa;
--color-gray-1: #eff1f4;
--color-gray-2: #d8dde1;
--color-gray-3: #ced4da;
--color-gray-4: #abb1b7;
--color-gray-5: #666a6d;
// Text
--color-text: var(--color-black);
--color-text-subtitle: var(--color-gray-5);
--color-text-inverse: #fdfdfd;
// Components
// Button
--color-button-primary-bg: var(--color-primary);
--color-button-primary-text: var(--color-primary-alt);
--color-button-primary-bg-hover: var(--color-primary-alt-2);
--color-button-primary-hover-text: var(--color-primary-alt);
--color-button-secondary-bg: var(--color-secondary-alt);
--color-button-secondary-border: var(--color-secondary-alt);
--color-button-secondary-text: var(--color-secondary);
--color-button-secondary-bg-hover: #b9d0e9;
--color-button-alt-bg: var(--color-gray-1);
--color-button-alt-text: var(--color-text);
--color-button-alt-bg-hover: var(--color-gray-2);
--color-link: var(--color-primary);
--color-link-hover: var(--color-black);
// Table
--color-table-highlight: var(--color-white-alt);
// Tag
--color-tag: var(--color-gray-5);
--color-tag-bg: var(--color-button-alt-bg);
--color-tag-hover: var(--color-button-alt-text);
--color-tag-bg-hover: var(--color-button-alt-bg-hover);
}

View file

@ -60,8 +60,12 @@ ol {
position: relative; position: relative;
list-style-position: outside; list-style-position: outside;
margin: var(--spacing-xs) 0; margin: var(--spacing-xs) 0;
margin-left: var(--spacing-xl); margin-left: var(--spacing-s);
margin-bottom: 0; margin-bottom: 0;
@media (min-width: $breakpoint-small) {
margin-left: var(--spacing-xl);
}
} }
} }
@ -267,10 +271,10 @@ textarea {
margin-top: var(--spacing-s); margin-top: var(--spacing-s);
&:not(:last-child) { &:not(:last-child) {
margin-bottom: var(--spacing-s); margin-bottom: var(--spacing-m);
} }
.button--link ~ .button--link { .button--link + .button--link {
margin-left: var(--spacing-s); margin-left: var(--spacing-s);
} }
@ -499,3 +503,16 @@ textarea {
height: 4rem; height: 4rem;
} }
} }
.home__meme {
text-align: center;
font-weight: bold;
line-height: 1;
font-size: 1rem;
margin-bottom: var(--spacing-m);
@media (min-width: $breakpoint-small) {
font-size: 1.2rem;
margin-bottom: var(--spacing-l);
}
}

View file

@ -9,7 +9,7 @@ $breakpoint-medium: 1150px;
$breakpoint-large: 1600px; $breakpoint-large: 1600px;
:root { :root {
--border-radius: 5px; --border-radius: 10px;
--height-input: 2.5rem; --height-input: 2.5rem;
--height-button: 2.5rem; --height-button: 2.5rem;
--height-checkbox: 24px; --height-checkbox: 24px;

View file

@ -1,30 +1,29 @@
[theme='dark'] { [theme='dark'] {
// Color overrides // Color overrides
--color-primary: #2bbb90; --color-primary: #e50054;
--color-primary-alt: #3e675d; --color-primary-alt: #66001880;
--color-primary-alt-2: #065f46; --color-fire: #ff6635;
--color-primary-alt-3: #34e5b0; --color-fire-outside: #ff9b20;
--color-secondary: #204166;
--color-secondary-alt: #dbeafe;
--color-secondary-alt-2: #bfdbfe;
--color-secondary-alt-3: #2c5c8c;
// Structure // Structure
--color-background: var(--color-gray-9); --color-background: #140e1b;
--color-background-overlay: #21252999; --color-background-overlay: #0c0d0e95;
--color-border: #333338; --color-border: #30243d;
--color-card-background: var(--color-gray-8); --color-card-background: #181021;
--color-card-background-highlighted: var(--color-gray-7); --color-card-background-highlighted: #241c30;
// Text // Text
--color-text: var(--color-white); --color-text: var(--color-gray-1);
--color-text-subtitle: var(--color-gray-4); --color-text-subtitle: var(--color-gray-4);
--color-text-empty: var(--color-text-subtitle); --color-text-empty: var(--color-text-subtitle);
--color-text-help: #bbbbbb; --color-text-help: #bbbbbb;
--color-text-warning: #212529; --color-text-warning: #212529;
--color-text-warning--background: var(--lbry-yellow-1); --color-text-warning--background: var(--lbry-yellow-1);
--color-text-error: #f87171; --color-text-error: var(--color-danger);
--color-error: #61373f; --color-error: var(--color-danger-alt);
--color-blockquote: var(--color-gray-5);
--color-blockquote-bg: var(--color-card-background-highlighted);
--color-help-warning-text: var(--color-white-alt);
// Tags (words) // Tags (words)
--color-tag-words: var(--color-text); --color-tag-words: var(--color-text);
@ -33,87 +32,73 @@
--color-tag-words-bg-hover: var(--color-gray-4); --color-tag-words-bg-hover: var(--color-gray-4);
// Header // Header
--color-header-background: var(--color-gray-8); --color-header-button: #38274c;
--color-header-button: var(--color-gray-6); --color-header-background: #231830;
--color-header-button-hover: var(--color-gray-6);
--color-header-button-active: var(--color-gray-6);
// Button // Button
--color-button-primary-bg: var(--color-primary-alt); --color-button-primary-text: white;
--color-button-primary-bg-hover: var(--color-primary-alt-2);
--color-button-primary-text: var(--color-gray-2);
--color-button-primary-hover-text: var(--color-primary-alt); --color-button-primary-hover-text: var(--color-primary-alt);
--color-button-secondary-bg: var(--color-secondary); --color-button-secondary-bg: #2c1543;
--color-button-secondary-border: var(--color-secondary); --color-button-secondary-border: #4f1c82;
--color-button-secondary-bg-hover: var(--color-secondary-alt-3); --color-button-secondary-bg-hover: #3b1c5b;
--color-button-secondary-text: var(--color-gray-2); --color-button-secondary-text: #efefef;
--color-button-alt-bg: var(--color-gray-7); --color-button-alt-bg: var(--color-header-button);
--color-button-alt-bg-hover: var(--color-gray-6); --color-button-alt-bg-hover: #2b2037;
--color-button-alt-text: var(--color-gray-1); --color-button-toggle-text: var(--color-text);
--color-button-border: var(--color-gray-5); --color-button-toggle-bg: var(--color-primary-alt);
--color-button-toggle-text: var(--color-gray-1); --color-button-toggle-bg-hover: var(--color-primary-alt);
--color-link: var(--color-primary-alt-3); --color-button-alt-text: #e2e9f0;
--color-link-hover: var(--color-text); --color-button-border: #5b4475;
--color-link-focus-bg: var(--color-gray-7); --color-link: var(--color-primary);
--color-link-hover: #d75673;
--color-link-active: #ec1d4c;
--color-link-focus-bg: #3d2d4e;
// Input // Input
--color-input: var(--color-white); --color-input: #f4f4f5;
--color-input-label: var(--color-gray-3); --color-input-label: #a7a7a7;
--color-input-placeholder: var(--color-gray-1); --color-input-placeholder: #f4f4f5;
--color-input-bg: var(--color-header-button); --color-input-bg: var(--color-header-button);
--color-input-bg-copyable: var(--color-gray-6); --color-input-bg-copyable: #4c3861;
--color-input-border: var(--color-border); --color-input-border: var(--color-border);
--color-input-border-active: var(--color-secondary); --color-input-border-active: var(--color-secondary);
--color-input-toggle: var(--color-primary-alt-3); --color-input-toggle: var(--color-primary-alt-3);
--color-input-toggle-bg: var(--color-input-bg); --color-input-toggle-bg: var(--color-input-bg);
--color-input-toggle-bg-hover: var(--color-secondary); --color-input-toggle-bg-hover: var(--color-secondary);
--color-input-bg-selected: var(--color-primary-alt); --color-input-bg-selected: var(--color-primary-alt);
--color-input-prefix-bg: var(--color-gray-5); --color-input-prefix-bg: var(--color-input-bg-copyable);
--color-input-prefix-border: var(--color-gray-4); --color-input-prefix-border: var(--color-gray-4);
--select-toggle-background: url("data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg' fill='%23ffffff'%3E%3Cpath d='M17.172, 31.172c1.562, -1.562 4.095, -1.562 5.656, 0l25.172, 25.171l25.172, -25.171c1.562, -1.562 4.095, -1.562 5.656, 0c1.562, 1.562 1.562, 4.095 0, 5.656l-28, 28c-1.562, 1.562 -4.095, 1.562 -5.656, 0l-28, -28c-0.781, -0.781 -1.172, -1.805 -1.172, -2.828c0, -1.023 0.391, -2.047 1.172, -2.828Z'/%3E%3C/svg%3E%0A"); --select-toggle-background: url("data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg' fill='%23ffffff'%3E%3Cpath d='M17.172, 31.172c1.562, -1.562 4.095, -1.562 5.656, 0l25.172, 25.171l25.172, -25.171c1.562, -1.562 4.095, -1.562 5.656, 0c1.562, 1.562 1.562, 4.095 0, 5.656l-28, 28c-1.562, 1.562 -4.095, 1.562 -5.656, 0l-28, -28c-0.781, -0.781 -1.172, -1.805 -1.172, -2.828c0, -1.023 0.391, -2.047 1.172, -2.828Z'/%3E%3C/svg%3E%0A");
// Navigation // Navigation
--color-navigation-icon: var(--color-gray-4); --color-navigation-icon: #76808a;
--color-navigation-link: var(--color-gray-4); --color-navigation-link: #b9c3ce;
--color-navigation-active: var(--color-gray-7); --color-navigation-active: #2b2037;
--color-navigation-active-text: var(--color-gray-3); --color-navigation-active-text: #c6bcd2;
--color-navigation-hover: var(--color-gray-6); --color-navigation-hover: #21182a;
--color-navigation-hover-text: var(--color-gray-3); --color-navigation-hover-text: #c6bcd2;
// Tags // Tags
--color-tag: var(--color-primary-alt-3); --color-tag: #ff85b1;
--color-tag-bg: var(--color-gray-7); --color-tag-bg: var(--color-navigation-hover);
--color-tag-hover: var(--color-white); --color-tag-hover: var(--color-white);
--color-tag-bg-hover: var(--color-primary-alt); --color-tag-bg-hover: var(--color-primary-alt-2);
--color-tag-mature-bg: var(--color-primary-alt-2);
// Menu // Menu
--color-menu-background: var(--color-header-background); --color-menu-background: var(--color-header-background);
--color-menu-background--active: var(--color-gray-7); --color-menu-background--active: var(--color-primary-alt);
--color-menu-icon: var(--color-gray-4); --color-menu-icon: #928b9b;
--color-menu-icon-active: #d6d6d6;
// Comments // Comments
--color-comment-menu: var(--color-gray-5); --color-comment-menu: #6a6a6a;
--color-comment-menu-hovering: var(--color-gray-2); --color-comment-menu-hovering: #e0e0e0;
--color-comment-threadline: #434b54;
--color-comment-threadline-hover: var(--color-gray-4);
--color-comment-highlighted: #484734; --color-comment-highlighted: #484734;
--color-comment-threadline: #24192f;
// Snack --color-comment-threadline-hover: var(--color-gray-4);
--color-snack-bg: var(--color-secondary);
// Superchat
--color-superchat-text: var(--color-black);
--color-superchat-text__light: var(--color-text);
--color-superchat: #fcd34d;
--color-superchat__light: #ef4e1647;
--color-superchat-2: #fde68a;
--color-superchat-3: #fef3c7;
--color-superchat-3__light: #58066087;
--color-superchat-4: #fffbeb;
// Other // Other
--color-focus: #93c5fd50;
--color-nag: var(--color-orange);
--color-tab-text: var(--color-white); --color-tab-text: var(--color-white);
--color-tabs-background: var(--color-card-background); --color-tabs-background: var(--color-card-background);
--color-tab-divider: var(--color-white); --color-tab-divider: var(--color-white);
@ -121,14 +106,12 @@
--color-notice: #58563b; --color-notice: #58563b;
--color-purchased: #ffd580; --color-purchased: #ffd580;
--color-purchased-alt: var(--color-purchased); --color-purchased-alt: var(--color-purchased);
--color-purchased-text: var(--color-gray-5); --color-purchased-text: black;
--color-thumbnail-background: var(--color-gray-5); --color-thumbnail-background: var(--color-gray-5);
--color-tooltip-bg: #2f3337; --color-tooltip-bg: #2f3337;
--color-help-warning-bg: #d97706; --color-focus: #e91e6329;
--color-help-warning-text: white; --color-placeholder-background: #261a35;
--color-blockquote: var(--color-gray-5); --color-spinner-light: white;
--color-placeholder-background: #4e5862;
--color-spinner-light: #5a6570;
--color-spinner-dark: #212529; --color-spinner-dark: #212529;
--color-login-graphic-background: var(--color-background); --color-login-graphic-background: var(--color-background);

View file

@ -1,2 +1,155 @@
:root { :root {
// Color overrides
--color-primary: #fa6165;
--color-primary-alt: #fef1f6;
--color-primary-alt-2: #fb7e82;
--color-primary-alt-3: #fbcbdd;
--color-secondary: #f9902a;
--color-secondary-alt: #fee8d2;
--color-secondary-alt-2: #fefcf6;
// Structure
--color-border: #ededed;
--color-background: #fafafa;
--color-background-overlay: #21252980;
--color-card-background: #ffffff;
--color-card-background-highlighted: #fff5f5;
// Text
--color-text-selection-bg: var(--color-primary-alt);
--color-text-selection: var(--color-primary);
--color-text-error: var(--color-danger);
--color-text-empty: #999999;
--color-text-help: #999999;
--color-text-subtitle: #767676;
--color-text-warning: #212529;
--color-help-warning-bg: #fef3c7;
--color-text-warning--background: var(--lbry-yellow-1);
--color-blockquote: var(--color-gray-3);
--color-blockquote-bg: var(--color-gray-1);
--color-tooltip-bg: #222;
--color-tooltip-text: #fafafa;
// Header
--color-header-button: var(--color-button-alt-bg);
--color-header-background: #ffffff;
// Button
--color-button-alt-bg: var(--color-gray-1);
--color-button-alt-bg-hover: var(--color-gray-2);
--color-button-alt-text: black;
--color-button-primary-bg: var(--color-primary);
--color-button-primary-bg-hover: var(--color-primary-alt-2);
--color-button-primary-text: var(--color-primary-alt);
--color-button-primary-hover-text: var(--color-white);
--color-button-secondary-bg: var(--color-primary-alt);
--color-button-secondary-border: var(--color-primary-alt-3);
--color-button-secondary-text: var(--color-primary);
--color-button-secondary-bg-hover: var(--color-primary-alt-3);
--color-button-toggle-text: var(--color-primary);
--color-button-toggle-bg: var(--color-primary-alt);
--color-button-toggle-bg-hover: var(--color-primary-alt);
--color-button-border: var(--color-gray-3);
--color-link-active: var(--color-primary);
--color-link-focus-bg: #f1f1f1;
--color-link: var(--color-primary);
// Input
--color-input-bg-selected: var(--color-primary-alt);
--color-input-color: #111111;
--color-input-label: var(--color-gray-5);
--color-input-placeholder: #212529;
--color-input-bg: var(--color-gray-1);
--color-input-border: var(--color-border);
--color-input-border-active: var(--color-secondary);
--color-input-toggle: var(--color-secondary);
--color-input-toggle-bg: var(--color-gray-1);
--color-input-toggle-bg-hover: var(--color-secondary-alt);
--color-input-prefix-bg: var(--color-gray-2);
--color-input-prefix-border: var(--color-gray-5);
--select-toggle-background: url("data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg' fill='%23212529'%3E%3Cpath d='M17.172, 31.172c1.562, -1.562 4.095, -1.562 5.656, 0l25.172, 25.171l25.172, -25.171c1.562, -1.562 4.095, -1.562 5.656, 0c1.562, 1.562 1.562, 4.095 0, 5.656l-28, 28c-1.562, 1.562 -4.095, 1.562 -5.656, 0l-28, -28c-0.781, -0.781 -1.172, -1.805 -1.172, -2.828c0, -1.023 0.391, -2.047 1.172, -2.828Z'/%3E%3C/svg%3E%0A");
// Navigation
--color-navigation-icon: var(--color-gray-5);
--color-navigation-link: var(--color-gray-5);
--color-navigation-active: var(--color-primary-alt);
--color-navigation-active-text: var(--color-primary);
--color-navigation-hover: var(--color-gray-1);
--color-navigation-hover-text: #3f3f3f;
// Tags
--color-tag: var(--color-primary-alt-2);
--color-tag-bg: #f9f6f7;
--color-tag-hover: var(--color-button-alt-text);
--color-tag-bg-hover: var(--color-button-alt-bg-hover);
// Menu
--color-menu-background: var(--color-header-background);
--color-menu-icon: var(--color-navigation-link);
--color-menu-icon-active: var(--color-navigation-link);
--color-menu-background--selected: var(--color-secondary-alt);
--color-menu-background--active: var(--color-primary-alt);
// Comments
--color-comment-menu: #e0e0e0;
--color-comment-menu-hovering: #6a6a6a;
--color-comment-highlighted: #fff2d9;
--color-comment-threadline: var(--color-gray-1);
--color-comment-threadline-hover: var(--color-gray-4);
// Superchat
--color-superchat-text: var(--color-black);
--color-superchat: #fcd34d;
--color-superchat__light: #fcd34d50;
--color-superchat-2: #fde68a;
--color-superchat-3: #fef3c7;
--color-superchat-3__light: #fef3c750;
--color-superchat-4: #fffbeb;
// Color
--color-focus: #8dbff0;
--color-nag: #fa8700;
--color-error: #fcafca;
--color-notice: #fef3ca;
--color-purchased: var(--color-cost);
--color-purchased-alt: #ffebc2;
--color-purchased-text: black;
--color-thumbnail-background: var(--color-gray-1);
--color-spinner-light: #ffffff;
--color-spinner-dark: #212529;
--color-placeholder-background: #f0f0f0;
--color-file-viewer-background: var(--color-card-background);
--color-tabs-background: var(--color-card-background);
--color-tab-divider: var(--color-primary);
--color-modal-background: var(--color-card-background);
// Icons
--color-follow-bg: #ffd4da;
--color-follow-icon: #e2495e;
--color-view-bg: var(--color-secondary-alt);
--color-view-icon: var(--color-secondary);
// Editor
--color-editor-cursor: var(--color-text);
--color-editor-quote: #707070;
--color-editor-tag: #ea9400;
--color-editor-attr: #04b0f4;
--color-editor-string: #ff7451;
--color-editor-inline-code-fg: var(--color-text);
--color-editor-inline-code-fg-preview: #2e3439;
--color-editor-inline-code-bg: rgba(157, 161, 165, 0.3);
--color-editor-inline-code-bg-preview: #d0e8ff;
--color-editor-selected: #add6ff;
--color-editor-link: var(--color-link);
--color-editor-url: var(--color-editor-string);
--color-editor-hr: var(--color-editor-tag);
--color-editor-hr-preview: #cccccc;
// Ads
--color-ads-background: #fae5ff;
--color-ads-link: var(--color-link);
// Scrollbar
--color-scrollbar-thumb-bg: rgba(0, 0, 0, 0.2);
--color-scrollbar-track-bg: transparent;
} }

View file

@ -38,9 +38,13 @@ export const getSearchQueryString = (query: string, options: any = {}) => {
const encodedQuery = encodeURIComponent(query); const encodedQuery = encodeURIComponent(query);
const queryParams = [ const queryParams = [
options.exact && !isSurroundedByQuotes(encodedQuery) ? `s="${encodedQuery}"` : `s=${encodedQuery}`, options.exact && !isSurroundedByQuotes(encodedQuery) ? `s="${encodedQuery}"` : `s=${encodedQuery}`,
`free_only=true`,
`size=${options.size || DEFAULT_SEARCH_SIZE}`, `size=${options.size || DEFAULT_SEARCH_SIZE}`,
`from=${options.from || DEFAULT_SEARCH_RESULT_FROM}`, `from=${options.from || DEFAULT_SEARCH_RESULT_FROM}`,
// `mediaType=${SEARCH_OPTIONS.MEDIA_VIDEO}`,
// `claimType=${SEARCH_OPTIONS.INCLUDE_FILES}`,
]; ];
const { isBackgroundSearch } = options; const { isBackgroundSearch } = options;
const includeUserOptions = typeof isBackgroundSearch === 'undefined' ? false : !isBackgroundSearch; const includeUserOptions = typeof isBackgroundSearch === 'undefined' ? false : !isBackgroundSearch;

View file

@ -4,7 +4,7 @@ import Button from 'component/button';
import { formatLbryUrlForWeb } from 'util/url'; import { formatLbryUrlForWeb } from 'util/url';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import { URL, SITE_NAME } from 'config'; import { URL, SITE_NAME } from 'config';
import * as ICONS from 'constants/icons'; import OdyseeLogoWithText from 'component/header/odysee_white.png';
type Props = { type Props = {
uri: string, uri: string,
@ -37,7 +37,9 @@ function FileViewerEmbeddedEnded(props: Props) {
return ( return (
<div className="file-viewer__overlay"> <div className="file-viewer__overlay">
<div className="file-viewer__overlay-secondary"> <div className="file-viewer__overlay-secondary">
<Button className="file-viewer__overlay-logo" label="LBRY" icon={ICONS.LBRY} href={URL} /> <Button className="file-viewer__overlay-logo--videoend" href={URL}>
<img src={OdyseeLogoWithText} />
</Button>
</div> </div>
<div className="file-viewer__overlay-title">{prompt}</div> <div className="file-viewer__overlay-title">{prompt}</div>
<div className="file-viewer__overlay-actions"> <div className="file-viewer__overlay-actions">

View file

@ -1,101 +1,34 @@
import * as PAGES from 'constants/pages';
import React from 'react'; import React from 'react';
import Button from 'component/button'; import Button from 'component/button';
import I18nMessage from 'component/i18nMessage';
const sections = [
{
name: 'Community',
links: [
{
label: 'Twitter',
link: 'https://twitter.com/lbrycom',
},
{
label: 'Reddit',
link: 'https://reddit.com/r/lbry',
},
{
label: 'Chat (Discord)',
link: 'https://chat.lbry.org/',
},
{
label: 'Telegram',
link: 'https://t.me/lbryofficial',
},
{
label: 'Facebook',
link: 'https://www.facebook.com/lbryio',
},
],
},
{
name: 'Resources',
links: [
{
label: 'FAQ',
link: 'https://lbry.com/faq',
},
{
label: 'Support --[used in footer; general help/support]--',
link: 'https://lbry.com/faq/support',
},
{
label: 'YouTube Partner Program',
link: 'https://lbry.com/youtube',
},
{
label: 'lbry.com',
link: 'https://lbry.com',
},
{
label: 'lbry.tech',
link: 'https://lbry.tech',
},
{
label: 'GitHub',
link: 'https://github.com/lbryio',
},
],
},
{
name: 'Policies',
links: [
{
label: 'Terms of Service',
link: 'https://www.lbry.com/termsofservice',
},
{
label: 'Privacy Policy',
link: 'https://lbry.com/privacypolicy',
},
{
label: '2257',
navigate: `/$/${PAGES.CODE_2257}`,
},
],
},
];
export default function Footer() { export default function Footer() {
return ( return (
<footer className="footer"> <footer className="footer">
<ul className="navigation__tertiary footer__links ul--no-style"> <span className="footer__section-title">
{sections.map(({ name, links }) => { <I18nMessage tokens={{ lbry_link: <Button button="link" label={'LBRY'} href="https://lbry.com" /> }}>
return ( POWERED BY %lbry_link%
<li key={name} className="footer__section"> </I18nMessage>
<ul className="ul--no-style"> </span>
<div className="footer__section-title">{__(name)}</div> <ul className="navigation__tertiary footer__links">
{links.map(({ label, link, navigate }) => { <li className="footer__link">
return ( <Button label={__('About --[link title in Sidebar or Footer]--')} href="https://lbry.com/about" />
<li key={label}> </li>
<Button className="footer__link" label={__(label)} href={link} navigate={navigate} /> <li className="footer__link">
</li> <Button label={__('Community Guidelines')} href="https://odysee.com/@OdyseeHelp:b/Community-Guidelines:c" />
); </li>
})} <li className="footer__link">
</ul> <Button label={__('FAQ')} href="https://odysee.com/@OdyseeHelp:b" />
</li> </li>
); <li className="footer__link">
})} <Button label={__('Support --[used in footer; general help/support]--')} href="https://lbry.com/support" />
</li>
<li className="footer__link">
<Button label={__('Terms')} href="https://lbry.com/termsofservice" />
</li>
<li className="footer__link">
<Button label={__('Privacy Policy')} href="https://lbry.com/privacy" />
</li>
</ul> </ul>
</footer> </footer>
); );