diff --git a/.env.defaults b/.env.defaults index 378761947..6ebb9fad7 100644 --- a/.env.defaults +++ b/.env.defaults @@ -16,7 +16,7 @@ COMMENT_SERVER_NAME=Odysee SEARCH_SERVER_API=https://lighthouse.odysee.com/search SOCKETY_SERVER_API=wss://sockety.odysee.com/ws THUMBNAIL_CDN_URL=https://image-processor.vanwanet.com/optimize/ -WELCOME_VERSION=1.0 +WELCOME_VERSION=1.3 # STRIPE # STRIPE_PUBLIC_KEY='pk_test_NoL1JWL7i1ipfhVId5KfDZgo' diff --git a/static/app-strings.json b/static/app-strings.json index e4f31fa78..7cea4e7d6 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2301,5 +2301,10 @@ "Add to %collection%": "Add to %collection%", "Show this channel your appreciation by sending a donation of Credits. ": "Show this channel your appreciation by sending a donation of Credits. ", "%action% %collection%": "%action% %collection%", + "You've entered the land of content freedom! Let's make sure everything is ship shape.": "You've entered the land of content freedom! Let's make sure everything is ship shape.", + "By continuing, you agree to the %terms%": "By continuing, you agree to the %terms%", + "Privacy": "Privacy", + "LBRY takes privacy and choice seriously. Is it ok if we monitor performance and help creators track their views?": "LBRY takes privacy and choice seriously. Is it ok if we monitor performance and help creators track their views?", + "Yes, share with LBRY": "Yes, share with LBRY", "--end--": "--end--" } diff --git a/static/img/yrblhappy.svg b/static/img/yrblhappy.svg new file mode 100644 index 000000000..800a7df3f --- /dev/null +++ b/static/img/yrblhappy.svg @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/yrblsad.svg b/static/img/yrblsad.svg new file mode 100644 index 000000000..40bb0922f --- /dev/null +++ b/static/img/yrblsad.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/yrblsec.svg b/static/img/yrblsec.svg new file mode 100644 index 000000000..e5e2ca2dd --- /dev/null +++ b/static/img/yrblsec.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/component/privacyAgreement/view.jsx b/ui/component/privacyAgreement/view.jsx index 79704c627..d3350d861 100644 --- a/ui/component/privacyAgreement/view.jsx +++ b/ui/component/privacyAgreement/view.jsx @@ -6,10 +6,8 @@ import { FormField } from 'component/common/form-components/form-field'; import { Form } from 'component/common/form-components/form'; import { withRouter } from 'react-router-dom'; // $FlowFixMe cannot resolve ... -import image from 'static/img/unlocklbry.svg'; -import { WELCOME_VERSION } from 'config'; +import image from 'static/img/yrblsec.svg'; -const FREE = 'free'; const LIMITED = 'limited'; const NONE = 'none'; @@ -18,29 +16,24 @@ type Props = { signOut: () => void, setShareDataInternal: (boolean) => void, setShareDataThirdParty: (boolean) => void, - history: { replace: (string) => void }, authenticated: boolean, authenticateIfSharingData: () => void, + handleNextPage: () => void, }; function PrivacyAgreement(props: Props) { const { - setWelcomeVersion, setShareDataInternal, setShareDataThirdParty, - history, authenticated, signOut, authenticateIfSharingData, + handleNextPage, } = props; const [share, setShare] = useState(undefined); // preload - const [agree, setAgree] = useState(false); // preload function handleSubmit() { - if (share === FREE) { - setShareDataInternal(true); - setShareDataThirdParty(true); - } else if (share === LIMITED) { + if (share === LIMITED) { setShareDataInternal(true); setShareDataThirdParty(false); } else { @@ -48,133 +41,85 @@ function PrivacyAgreement(props: Props) { setShareDataThirdParty(false); } - if (share === FREE || share === LIMITED) { + if (share === LIMITED) { authenticateIfSharingData(); } - setWelcomeVersion(WELCOME_VERSION); - history.replace(`/`); + // setWelcomeVersion(WELCOME_VERSION); + handleNextPage(); + // history.replace(`/`); } return (
-
-
-

{__('Welcome')}

-

- {__( - `LBRY takes privacy and choice seriously. Just a few questions before you enter the land of content freedom.` +
+
+
+

{__('Privacy')}

+

+ {__( + `LBRY takes privacy and choice seriously. Is it ok if we monitor performance and help creators track their views?` + )} +

+
+
+
+ + {__('Yes, share with LBRY')} 🙂 + + } + onChange={(e) => setShare(LIMITED)} + /> + + {__('No')} 😢 + + } + helper={__(`* Note that as + peer-to-peer software, your IP address and potentially other system information can be sent to other + users, though this information is not stored permanently.`)} + onChange={(e) => setShare(NONE)} + /> + {authenticated && ( +
+

+ , + }} + > + You are signed in and sharing data with your cloud service provider. %signout_button%. + +

+
+ )} +
+
+
+ {share === NONE && ( +

+ {__( + 'While we respect the desire for maximally private usage, please note that choosing this option hurts the ability for creators to understand how their content is performing.' + )} +

)} -

+
-
+
-
-

- {__('Can this app send information about your usage to inform publishers and improve the software?')} -

-
-
- - {__('Yes, including with third-party analytics platforms')} 😄 - - } - helper={__(`Sending information to third parties (e.g. Google Analytics or Mixpanel) allows us to use detailed - analytical reports to improve all aspects of LBRY.`)} - checked={share === FREE} - onChange={(e) => setShare(FREE)} - /> - - {__('Yes, but only with LBRY, Inc.')} 🙂 - - } - helper={__( - `Sharing information with LBRY, Inc. allows us to report to publishers how their content is doing, as - well as track basic usage and performance. This is the minimum required to earn rewards from LBRY, Inc.` - )} - onChange={(e) => setShare(LIMITED)} - /> - - {__('No')} 😢 - - } - helper={__(`No information will be sent directly to LBRY, Inc. or third-parties about your usage. Note that as - peer-to-peer software, your IP address and potentially other system information can be sent to other - users, though this information is not stored permanently.`)} - onChange={(e) => setShare(NONE)} - /> - {authenticated && ( -
-

- , - }} - > - You are signed in and sharing data with your cloud service provider. %signout_button%. - -

-
- )} -
- -
-

- - ), - }} - > - Do you agree to the %terms%? - -

-
- setAgree(e.target.checked)} - /> - setAgree(!e.target.checked)} - /> -
-
-
-
- {share === NONE && ( -

- {__( - 'While we respect the desire for maximally private usage, please note that choosing this option hurts the ability for creators to understand how their content is performing.' - )} -

- )} -
); } diff --git a/ui/component/welcomeSplash/index.js b/ui/component/welcomeSplash/index.js new file mode 100644 index 000000000..e463615c1 --- /dev/null +++ b/ui/component/welcomeSplash/index.js @@ -0,0 +1,25 @@ +import { connect } from 'react-redux'; +import { doSetDaemonSetting } from 'redux/actions/settings'; +import { doSetWelcomeVersion } from 'redux/actions/app'; +import * as DAEMON_SETTINGS from 'constants/daemon_settings'; +import { WELCOME_VERSION } from 'config.js'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; +import { selectDaemonSettings, selectDaemonStatus } from 'redux/selectors/settings'; + +import WelcomeSplash from './view'; +import { selectDiskSpace } from 'redux/selectors/app'; + +const select = (state) => ({ + authenticated: selectUserVerifiedEmail(state), + diskSpace: selectDiskSpace(state), + daemonSettings: selectDaemonSettings(state), + daemonStatus: selectDaemonStatus(state), +}); + +const perform = (dispatch) => ({ + setWelcomeVersion: (version) => dispatch(doSetWelcomeVersion(version || WELCOME_VERSION)), + setShareDataInternal: (share) => dispatch(doSetDaemonSetting(DAEMON_SETTINGS.SHARE_USAGE_DATA, share)), + setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(DAEMON_SETTINGS.BLOB_STORAGE_LIMIT_MB, value)), +}); + +export default connect(select, perform)(WelcomeSplash); diff --git a/ui/component/welcomeSplash/view.jsx b/ui/component/welcomeSplash/view.jsx new file mode 100644 index 000000000..ca3e3bbda --- /dev/null +++ b/ui/component/welcomeSplash/view.jsx @@ -0,0 +1,49 @@ +// @flow +import React from 'react'; +import Button from 'component/button'; +import { withRouter } from 'react-router-dom'; +import I18nMessage from 'component/i18nMessage'; +// $FlowFixMe cannot resolve ... +import YrblHappy from 'static/img/yrblhappy.svg'; + +type Props = { + setWelcomeVersion: (number) => void, + setShareDataInternal: (boolean) => void, + authenticated: boolean, + handleNextPage: () => void, + diskSpace?: DiskSpace, +}; + +function WelcomePage(props: Props) { + const { handleNextPage } = props; + + return ( +
+
+
+

{__('Welcome')}

+

+ {__(`You've entered the land of content freedom! Let's make sure everything is ship shape.`)} +

+
+
+ +
+
+
+
+

+ , + }} + > + By continuing, you agree to the %terms% + +

+
+ ); +} + +export default withRouter(WelcomePage); diff --git a/ui/component/yrbl/index.jsx b/ui/component/yrbl/index.jsx index 44af157be..48b627d3c 100644 --- a/ui/component/yrbl/index.jsx +++ b/ui/component/yrbl/index.jsx @@ -2,7 +2,10 @@ import type { Node } from 'react'; import * as React from 'react'; import classnames from 'classnames'; -import { YRBL_HAPPY_IMG_URL, YRBL_SAD_IMG_URL } from 'config'; +// $FlowFixMe cannot resolve ... +import YrblHappy from 'static/img/yrblhappy.svg'; +// $FlowFixMe cannot resolve ... +import YrblSad from 'static/img/yrblsad.svg'; type Props = { title?: string, @@ -14,8 +17,8 @@ type Props = { }; const yrblTypes = { - happy: YRBL_HAPPY_IMG_URL, - sad: YRBL_SAD_IMG_URL, + happy: YrblHappy, + sad: YrblSad, }; export default class extends React.PureComponent { diff --git a/ui/page/welcome/index.js b/ui/page/welcome/index.js index 860215821..ab1c8a3b7 100644 --- a/ui/page/welcome/index.js +++ b/ui/page/welcome/index.js @@ -1,10 +1,16 @@ import { connect } from 'react-redux'; import Welcome from './view'; +import { doSetWelcomeVersion } from 'redux/actions/app'; +import { WELCOME_VERSION } from 'config'; +import { selectDaemonSettings, selectDaemonStatus } from 'redux/selectors/settings'; -const select = () => ({}); -const perform = () => ({}); +const select = (state) => ({ + daemonSettings: selectDaemonSettings(state), + daemonStatus: selectDaemonStatus(state), +}); -export default connect( - select, - perform -)(Welcome); +const perform = (dispatch) => ({ + updateWelcomeVersion: (version) => dispatch(doSetWelcomeVersion(version || WELCOME_VERSION)), +}); + +export default connect(select, perform)(Welcome); diff --git a/ui/page/welcome/view.jsx b/ui/page/welcome/view.jsx index c5a794a40..015f9db46 100644 --- a/ui/page/welcome/view.jsx +++ b/ui/page/welcome/view.jsx @@ -1,12 +1,62 @@ // @flow import React from 'react'; import PrivacyAgreement from 'component/privacyAgreement'; +import WelcomeSplash from 'component/welcomeSplash'; import Page from 'component/page'; +import { useHistory } from 'react-router-dom'; + +const SPLASH_PAGE = 0; +const PRIVACY_PAGE = 1; +// const HOSTING_PAGE = 2; +// const WELCOME_PAGES = [SPLASH_PAGE, PRIVACY_PAGE]; +type DaemonStatus = { + disk_space: { + content_blobs_storage_used_mb: string, + published_blobs_storage_used_mb: string, + running: true, + seed_blobs_storage_used_mb: string, + total_used_mb: string, + }, +}; + +type DaemonSettings = { + download_dir: string, + share_usage_data: boolean, + max_connections_per_download?: number, + save_files: boolean, + save_blobs: boolean, + ffmpeg_path: string, +}; + +type Props = { + // --- select --- + daemonSettings: DaemonSettings, + daemonStatus: DaemonStatus, + // -- perform --- + updateWelcomeVersion: () => void, +}; +export default function Welcome(props: Props) { + const { updateWelcomeVersion } = props; + // const { save_blobs: saveBlobs } = daemonSettings || {}; + const [welcomePage, setWelcomePage] = React.useState(SPLASH_PAGE); + const { replace } = useHistory(); + + const handleNextPage = () => { + if (welcomePage === SPLASH_PAGE) { + setWelcomePage(PRIVACY_PAGE); + } + }; + + const handleDone = () => { + updateWelcomeVersion(); + replace('/'); + }; -export default function Welcome() { return ( - + {welcomePage === SPLASH_PAGE && } + {welcomePage === PRIVACY_PAGE && } + {/* {welcomePage === HOSTING_PAGE && } */} ); } diff --git a/ui/scss/all.scss b/ui/scss/all.scss index 9deafe5b0..392ee8533 100644 --- a/ui/scss/all.scss +++ b/ui/scss/all.scss @@ -70,3 +70,4 @@ @import 'component/utils'; @import 'component/settings'; @import 'component/embed-player'; +@import 'component/first-run'; diff --git a/ui/scss/component/_first-run.scss b/ui/scss/component/_first-run.scss new file mode 100644 index 000000000..e238e51c6 --- /dev/null +++ b/ui/scss/component/_first-run.scss @@ -0,0 +1,35 @@ +.first-run__image-wrapper { + .privacy-img { + min-width: 100%; + height: 30vh; + } + height: 100%; +} + +.first-run__wrapper { + margin: auto; + .first-run__left { + min-height: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; + } +} + +.first-run__actions { + display: flex; + flex-direction: row; + justify-content: space-between; + @media (min-width: $breakpoint-small) { + justify-content: flex-start; + :not(:first-child) { + margin-left: var(--spacing-s); + } + } +} + +.first-run__code { + padding: var(--spacing-s); + background-color: var(--color-card-background); +}