diff --git a/.env.defaults b/.env.defaults index 5da994a5e..3123bfaec 100644 --- a/.env.defaults +++ b/.env.defaults @@ -26,15 +26,14 @@ MATOMO_URL=https://analytics.lbry.com/ MATOMO_ID=4 # OG -OG_TITLE_SUFFIX=| LBRY -OG_HOMEPAGE_TITLE=LBRY +OG_TITLE_SUFFIX=| lbry.tv +OG_HOMEPAGE_TITLE=lbry.tv OG_IMAGE_URL= -#SITE_CANONICAL_URL= +SITE_CANONICAL_URL=https://lbry.tv # UI ## Custom Site info DOMAIN=lbry.tv -SHARE_DOMAIN_URL=odysee.com URL=https://lbry.tv SITE_TITLE=LBRY SITE_NAME=LBRY @@ -44,7 +43,7 @@ LOGO_TITLE=LBRY CLOUD_CONNECT_SITE_NAME=Odysee ## Social media TWITTER_ACCOUNT=LBRYcom -#BRANDED_SITE= +BRANDED_SITE=odysee ## IMAGE ASSETS YRBL_HAPPY_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-happy/7aa50a7e5adaf48691935d55e45d697547392929/839d9a diff --git a/config.js b/config.js index 1368a06cb..f7ab434b1 100644 --- a/config.js +++ b/config.js @@ -45,7 +45,7 @@ const config = { YRBL_HAPPY_IMG_URL: process.env.YRBL_HAPPY_IMG_URL, YRBL_SAD_IMG_URL: process.env.YRBL_SAD_IMG_URL, LOGIN_IMG_URL: process.env.LOGIN_IMG_URL, - // SITE_CANONICAL_URL: process.env.SITE_CANONICAL_URL, + SITE_CANONICAL_URL: process.env.SITE_CANONICAL_URL, DEFAULT_LANGUAGE: process.env.DEFAULT_LANGUAGE, AUTO_FOLLOW_CHANNELS: process.env.AUTO_FOLLOW_CHANNELS, UNSYNCED_SETTINGS: process.env.UNSYNCED_SETTINGS, diff --git a/electron/index.js b/electron/index.js index 56d2aa9fa..b73653280 100644 --- a/electron/index.js +++ b/electron/index.js @@ -7,6 +7,7 @@ import https from 'https'; import { app, dialog, ipcMain, session, shell, BrowserWindow } from 'electron'; import { autoUpdater } from 'electron-updater'; import Lbry from 'lbry'; +import LbryFirstInstance from './LbryFirstInstance'; import Daemon from './Daemon'; import isDev from 'electron-is-dev'; import createTray from './createTray'; @@ -99,51 +100,51 @@ const startDaemon = async () => { } }; -// let isLbryFirstRunning = false; -// const startLbryFirst = async () => { -// if (isLbryFirstRunning) { -// console.log('LbryFirst already running'); -// handleLbryFirstLaunched(); -// return; -// } -// -// console.log('LbryFirst: Starting...'); -// -// try { -// lbryFirst = new LbryFirstInstance(); -// lbryFirst.on('exit', e => { -// if (!isDev) { -// lbryFirst = null; -// isLbryFirstRunning = false; -// if (!appState.isQuitting) { -// dialog.showErrorBox( -// 'LbryFirst has Exited', -// 'The lbryFirst may have encountered an unexpected error, or another lbryFirst instance is already running. \n\n', -// e -// ); -// } -// app.quit(); -// } -// }); -// } catch (e) { -// console.log('LbryFirst: Failed to create new instance\n\n', e); -// } -// -// console.log('LbryFirst: Running...'); -// -// try { -// await lbryFirst.launch(); -// handleLbryFirstLaunched(); -// } catch (e) { -// isLbryFirstRunning = false; -// console.log('LbryFirst: Failed to start\n', e); -// } -// }; -// -// const handleLbryFirstLaunched = () => { -// isLbryFirstRunning = true; -// rendererWindow.webContents.send('lbry-first-launched'); -// }; +let isLbryFirstRunning = false; +const startLbryFirst = async () => { + if (isLbryFirstRunning) { + console.log('LbryFirst already running'); + handleLbryFirstLaunched(); + return; + } + + console.log('LbryFirst: Starting...'); + + try { + lbryFirst = new LbryFirstInstance(); + lbryFirst.on('exit', e => { + if (!isDev) { + lbryFirst = null; + isLbryFirstRunning = false; + if (!appState.isQuitting) { + dialog.showErrorBox( + 'LbryFirst has Exited', + 'The lbryFirst may have encountered an unexpected error, or another lbryFirst instance is already running. \n\n', + e + ); + } + app.quit(); + } + }); + } catch (e) { + console.log('LbryFirst: Failed to create new instance\n\n', e); + } + + console.log('LbryFirst: Running...'); + + try { + await lbryFirst.launch(); + handleLbryFirstLaunched(); + } catch (e) { + isLbryFirstRunning = false; + console.log('LbryFirst: Failed to start\n', e); + } +}; + +const handleLbryFirstLaunched = () => { + isLbryFirstRunning = true; + rendererWindow.webContents.send('lbry-first-launched'); +}; // When we are starting the app, ensure there are no other apps already running const gotSingleInstanceLock = app.requestSingleInstanceLock(); @@ -399,15 +400,15 @@ ipcMain.on('version-info-requested', () => { requestLatestRelease(); }); -// -// ipcMain.on('launch-lbry-first', async () => { -// try { -// await startLbryFirst(); -// } catch (e) { -// console.log('Failed to start LbryFirst'); -// console.log(e); -// } -// }); + +ipcMain.on('launch-lbry-first', async () => { + try { + await startLbryFirst(); + } catch (e) { + console.log('Failed to start LbryFirst'); + console.log(e); + } +}); process.on('uncaughtException', error => { console.log(error); diff --git a/extras/lbryinc/lbryio.js b/extras/lbryinc/lbryio.js index b1f687f8f..89c0536e9 100644 --- a/extras/lbryinc/lbryio.js +++ b/extras/lbryinc/lbryio.js @@ -111,7 +111,7 @@ Lbryio.getAuthToken = () => Lbryio.getCurrentUser = () => Lbryio.call('user', 'me'); -Lbryio.authenticate = (language) => { +Lbryio.authenticate = (domain, language) => { if (!Lbryio.enabled) { const params = { id: 1, @@ -155,7 +155,10 @@ Lbryio.authenticate = (language) => { .then( status => new Promise((res, rej) => { - const appId = status.installation_id; + const appId = + domain && domain !== 'lbry.tv' + ? (domain.replace(/[.]/gi, '') + status.installation_id).slice(0, 66) + : status.installation_id; Lbryio.call( 'user', 'new', diff --git a/static/app-strings.json b/static/app-strings.json index c9ee182ac..b3527b9b3 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -1661,6 +1661,7 @@ "Short (< 4 minutes)": "Short (< 4 minutes)", "Long (> 20 min)": "Long (> 20 min)", "POWERED BY %lbry_link%": "POWERED BY %lbry_link%", + "Learn more about LBRY Credits on %DOMAIN%": "Learn more about LBRY Credits on %DOMAIN%", "Results boosted by %lbc%": "Results boosted by %lbc%", "This will be visible in a few minutes.": "This will be visible in a few minutes.", "Turn on notifications": "Turn on notifications", @@ -2221,6 +2222,5 @@ "Your wallet is not currently using a cloud sync service. You are in control of backing up your wallet.": "Your wallet is not currently using a cloud sync service. You are in control of backing up your wallet.", "Sending": "Sending", "You sent %lbc%": "You sent %lbc%", - "Learn more about LBRY Credits": "Learn more about LBRY Credits", "--end--": "--end--" } diff --git a/ui/component/claimMenuList/view.jsx b/ui/component/claimMenuList/view.jsx index 3c0225ad0..eb7b2ab77 100644 --- a/ui/component/claimMenuList/view.jsx +++ b/ui/component/claimMenuList/view.jsx @@ -1,5 +1,5 @@ // @flow -import { SHARE_DOMAIN_URL } from 'config'; +import { URL, SHARE_DOMAIN_URL } from 'config'; import * as ICONS from 'constants/icons'; import * as PAGES from 'constants/pages'; import * as MODALS from 'constants/modal_types'; @@ -18,7 +18,7 @@ import { import { useHistory } from 'react-router'; import { buildURI, parseURI } from 'util/lbryURI'; -const SHARE_DOMAIN = SHARE_DOMAIN_URL; +const SHARE_DOMAIN = SHARE_DOMAIN_URL || URL; const PAGE_VIEW_QUERY = 'view'; const EDIT_PAGE = 'edit'; diff --git a/ui/component/collectionEdit/view.jsx b/ui/component/collectionEdit/view.jsx index 824a33878..108e21493 100644 --- a/ui/component/collectionEdit/view.jsx +++ b/ui/component/collectionEdit/view.jsx @@ -1,4 +1,5 @@ // @flow +import { DOMAIN } from 'config'; import React from 'react'; import classnames from 'classnames'; import Button from 'component/button'; @@ -89,7 +90,7 @@ function CollectionForm(props: Props) { onDone, } = props; const activeChannelName = activeChannelClaim && activeChannelClaim.name; - let prefix = 'lbry://'; + let prefix = IS_WEB ? `${DOMAIN}/` : 'lbry://'; if (activeChannelName && !incognito) { prefix += `${activeChannelName}/`; } diff --git a/ui/component/privacyAgreement/index.js b/ui/component/privacyAgreement/index.js index 046f866bc..f7b4e3082 100644 --- a/ui/component/privacyAgreement/index.js +++ b/ui/component/privacyAgreement/index.js @@ -1,3 +1,4 @@ +import { DOMAIN } from 'config'; import { connect } from 'react-redux'; import { doSetDaemonSetting } from 'redux/actions/settings'; import { doSetWelcomeVersion, doToggle3PAnalytics, doSignOut } from 'redux/actions/app'; @@ -18,7 +19,8 @@ const perform = (dispatch) => ({ setShareDataInternal: (share) => dispatch(doSetDaemonSetting(DAEMON_SETTINGS.SHARE_USAGE_DATA, share)), setShareDataThirdParty: (share) => dispatch(doToggle3PAnalytics(share)), signOut: () => dispatch(doSignOut()), - authenticateIfSharingData: () => dispatch(doAuthenticate(appVersion, undefined, undefined, true)), + authenticateIfSharingData: () => + dispatch(doAuthenticate(appVersion, undefined, undefined, true, undefined, undefined, DOMAIN)), }); export default connect(select, perform)(PrivacyAgreement); diff --git a/ui/component/publishName/view.jsx b/ui/component/publishName/view.jsx index fa882e067..3c5ff7f85 100644 --- a/ui/component/publishName/view.jsx +++ b/ui/component/publishName/view.jsx @@ -1,4 +1,5 @@ // @flow +import { DOMAIN } from 'config'; import { INVALID_NAME_ERROR } from 'constants/claim'; import React, { useState, useEffect } from 'react'; import { isNameValid } from 'util/lbryURI'; @@ -31,7 +32,7 @@ function PublishName(props: Props) { const [nameError, setNameError] = useState(undefined); const [blurred, setBlurred] = React.useState(false); const activeChannelName = activeChannelClaim && activeChannelClaim.name; - let prefix = 'lbry://'; + let prefix = IS_WEB ? `${DOMAIN}/` : 'lbry://'; if (activeChannelName && !incognito) { prefix += `${activeChannelName}/`; } diff --git a/ui/component/searchTopClaim/view.jsx b/ui/component/searchTopClaim/view.jsx index 139284883..ab884740f 100644 --- a/ui/component/searchTopClaim/view.jsx +++ b/ui/component/searchTopClaim/view.jsx @@ -10,6 +10,7 @@ import ClaimRepostAuthor from 'component/claimRepostAuthor'; import I18nMessage from 'component/i18nMessage'; import { useHistory } from 'react-router'; import LbcSymbol from 'component/common/lbc-symbol'; +import { DOMAIN } from 'config'; type Props = { query: string, @@ -81,7 +82,11 @@ export default function SearchTopClaim(props: Props) {
{winningUri && (
- +
diff --git a/ui/component/selectThumbnail/view.jsx b/ui/component/selectThumbnail/view.jsx index 341f131f8..c8090583b 100644 --- a/ui/component/selectThumbnail/view.jsx +++ b/ui/component/selectThumbnail/view.jsx @@ -2,6 +2,7 @@ import * as MODALS from 'constants/modal_types'; import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses'; import Lbry from 'lbry'; +import { DOMAIN } from 'config'; import * as React from 'react'; import { FormField } from 'component/common/form'; import FileSelector from 'component/common/file-selector'; @@ -183,7 +184,9 @@ function SelectThumbnail(props: Props) { {status === THUMBNAIL_STATUSES.IN_PROGRESS &&

{__('Uploading thumbnail')}...

} {!thumbUploaded && (

- {manualInput ? __('Enter a URL for your thumbnail.') : __('Upload your thumbnail. Recommended size is 16:9.')} + {manualInput + ? __('Enter a URL for your thumbnail.') + : __('Upload your thumbnail to %domain%. Recommended size is 16:9.', { domain: DOMAIN })}

)} diff --git a/ui/component/settingShareUrl/view.jsx b/ui/component/settingShareUrl/view.jsx index fb2687ab8..c58181ff1 100644 --- a/ui/component/settingShareUrl/view.jsx +++ b/ui/component/settingShareUrl/view.jsx @@ -1,5 +1,5 @@ // @flow -import { SHARE_DOMAIN_URL } from 'config'; +import { SHARE_DOMAIN_URL, URL } from 'config'; import React from 'react'; import { FormField } from 'component/common/form'; @@ -32,7 +32,7 @@ function SettingShareUrl(props: Props) { { if (e.target.checked) { diff --git a/ui/component/sideNavigation/view.jsx b/ui/component/sideNavigation/view.jsx index b8ac38599..b2c10b8c4 100644 --- a/ui/component/sideNavigation/view.jsx +++ b/ui/component/sideNavigation/view.jsx @@ -6,9 +6,11 @@ import * as KEYCODES from 'constants/keycodes'; import React from 'react'; import Button from 'component/button'; import classnames from 'classnames'; +import Icon from 'component/common/icon'; import NotificationBubble from 'component/notificationBubble'; +import I18nMessage from 'component/i18nMessage'; import ChannelThumbnail from 'component/channelThumbnail'; -import { ENABLE_UI_NOTIFICATIONS } from 'config'; +import { DOMAIN, ENABLE_UI_NOTIFICATIONS } from 'config'; import { IS_MAC } from 'component/app/view'; const HOME = { @@ -247,6 +249,23 @@ function SideNavigation(props: Props) { return () => window.removeEventListener('keydown', handleKeydown); }, [sidebarOpen, setSidebarOpen, isAbsolute]); + const unAuthNudge = + DOMAIN === 'lbry.tv' ? null : ( +
+ + }}> + Sign up to earn %lbc% for you and your favorite creators. + + +
+ ); + const helpLinks = ( )} + + {!isAuthenticated && sidebarOpen && unAuthNudge}
{sidebarOpen && helpLinks} @@ -400,6 +421,7 @@ function SideNavigation(props: Props) { ))} )} + {!isAuthenticated && unAuthNudge}
= limitSelect); const suggestedTags = Array.from(suggestedTagsSet).filter(doesTagMatch).slice(0, limitShow); diff --git a/ui/component/userEmailNew/view.jsx b/ui/component/userEmailNew/view.jsx index 1cb9b1f1b..13b0d0ca3 100644 --- a/ui/component/userEmailNew/view.jsx +++ b/ui/component/userEmailNew/view.jsx @@ -1,5 +1,6 @@ // @flow import * as PAGES from 'constants/pages'; +import { DOMAIN } from 'config'; import React, { useState } from 'react'; import { FormField, Form } from 'component/common/form'; import Button from 'component/button'; @@ -94,7 +95,7 @@ function UserEmailNew(props: Props) { title={__('Cloud Connect')} subtitle={__('Connect your wallet to Odysee')} actions={ -
+
+ , @@ -149,6 +154,7 @@ function DiscoverPage(props: Props) { limitClaimsPerChannel={LIMIT_CLAIMS_PER_CHANNEL} meta={getElemMeta()} hasSource + showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS} /> ); diff --git a/ui/redux/actions/app.js b/ui/redux/actions/app.js index e48419c18..84da06b75 100644 --- a/ui/redux/actions/app.js +++ b/ui/redux/actions/app.js @@ -6,6 +6,7 @@ import * as MODALS from 'constants/modal_types'; import * as SETTINGS from 'constants/settings'; import * as DAEMON_SETTINGS from 'constants/daemon_settings'; import * as SHARED_PREFERENCES from 'constants/shared_preferences'; +import { DOMAIN } from 'config'; import Lbry from 'lbry'; import { doFetchChannelListMine, doFetchCollectionListMine, doCheckPendingClaims } from 'redux/actions/claims'; import { makeSelectClaimForUri, makeSelectClaimIsMine, selectMyChannelClaims } from 'redux/selectors/claims'; @@ -332,7 +333,8 @@ export function doDaemonReady() { analytics.trendingAlgorithmEvent(trendingAlgorithm); } }, - undefined + undefined, + DOMAIN ) ); dispatch({ type: ACTIONS.DAEMON_READY }); diff --git a/ui/redux/actions/user.js b/ui/redux/actions/user.js index 9641b8185..9ff3932e5 100644 --- a/ui/redux/actions/user.js +++ b/ui/redux/actions/user.js @@ -10,6 +10,7 @@ import { selectEmailToVerify, selectPhoneToVerify, selectUserCountryCode, select import { doToast } from 'redux/actions/notifications'; import rewards from 'rewards'; import { Lbryio } from 'lbryinc'; +import { DOMAIN } from 'config'; import { getDefaultLanguage } from 'util/default-languages'; const AUTH_IN_PROGRESS = 'authInProgress'; export let sessionStorageAvailable = false; @@ -54,7 +55,10 @@ export function doInstallNew(appVersion, os = null, firebaseToken = null, callba } Lbry.status().then((status) => { - payload.app_id = status.installation_id; + payload.app_id = + domain && domain !== 'lbry.tv' + ? (domain.replace(/[.]/gi, '') + status.installation_id).slice(0, 66) + : status.installation_id; payload.node_id = status.lbry_id; Lbry.version().then((version) => { payload.daemon_version = version.lbrynet_version; @@ -140,7 +144,7 @@ export function doAuthenticate( }); checkAuthBusy() .then(() => { - return Lbryio.authenticate(getDefaultLanguage()); + return Lbryio.authenticate(DOMAIN, getDefaultLanguage()); }) .then((user) => { if (sessionStorageAvailable) window.sessionStorage.removeItem(AUTH_IN_PROGRESS); diff --git a/ui/util/saved-passwords.js b/ui/util/saved-passwords.js index 60a0c1e23..db71bdd8d 100644 --- a/ui/util/saved-passwords.js +++ b/ui/util/saved-passwords.js @@ -1,7 +1,9 @@ +const { DOMAIN } = require('../../config.js'); const AUTH_TOKEN = 'auth_token'; const SAVED_PASSWORD = 'saved_password'; -const domain = 'localhost'; -// typeof window === 'object' && window.location.hostname.includes('localhost') ? window.location.hostname : DOMAIN; +const DEPRECATED_SAVED_PASSWORD = 'saved-password'; +const domain = + typeof window === 'object' && window.location.hostname.includes('localhost') ? window.location.hostname : DOMAIN; const isProduction = process.env.NODE_ENV === 'production'; const maxExpiration = 2147483647; let sessionPassword; @@ -57,7 +59,7 @@ function deleteCookie(name) { } function setSavedPassword(value, saveToDisk) { - return new Promise((resolve) => { + return new Promise(resolve => { const password = value === undefined || value === null ? '' : value; sessionPassword = password; @@ -72,17 +74,17 @@ function setSavedPassword(value, saveToDisk) { } function getSavedPassword() { - return new Promise((resolve) => { + return new Promise(resolve => { if (sessionPassword) { resolve(sessionPassword); } - return getPasswordFromCookie().then((p) => resolve(p)); + return getPasswordFromCookie().then(p => resolve(p)); }); } function getPasswordFromCookie() { - return new Promise((resolve) => { + return new Promise(resolve => { let password; password = getCookie(SAVED_PASSWORD); resolve(password); @@ -90,7 +92,7 @@ function getPasswordFromCookie() { } function deleteSavedPassword() { - return new Promise((resolve) => { + return new Promise(resolve => { deleteCookie(SAVED_PASSWORD); resolve(); }); @@ -105,14 +107,14 @@ function setAuthToken(value) { } function deleteAuthToken() { - return new Promise((resolve) => { + return new Promise(resolve => { deleteCookie(AUTH_TOKEN); resolve(); }); } function doSignOutCleanup() { - return new Promise((resolve) => { + return new Promise(resolve => { deleteAuthToken(); deleteSavedPassword(); resolve(); @@ -127,6 +129,14 @@ function doAuthTokenRefresh() { } } +function doDeprecatedPasswordMigrationMarch2020() { + const savedPassword = getCookie(DEPRECATED_SAVED_PASSWORD); + if (savedPassword) { + deleteCookie(DEPRECATED_SAVED_PASSWORD); + setSavedPassword(savedPassword, true); + } +} + module.exports = { setCookie, getCookie, @@ -140,4 +150,5 @@ module.exports = { deleteAuthToken, doSignOutCleanup, doAuthTokenRefresh, + doDeprecatedPasswordMigrationMarch2020, };