diff --git a/package.json b/package.json index bc4e6369..ec3f68bc 100644 --- a/package.json +++ b/package.json @@ -80,4 +80,4 @@ "node": "^20" }, "homepage": "" -} +} \ No newline at end of file diff --git a/src/components/start/Start.container.ts b/src/components/start/Start.container.ts index b65a3406..3552fd86 100644 --- a/src/components/start/Start.container.ts +++ b/src/components/start/Start.container.ts @@ -7,6 +7,7 @@ import { } from 'decentraland-dapps/dist/modules/wallet/selectors' import { LOAD_PROFILE_REQUEST } from 'decentraland-dapps/dist/modules/profile/actions' import { getProfileOfAddress, getLoading } from 'decentraland-dapps/dist/modules/profile/selectors' +import { areFeatureFlagsReady, FeatureFlags, isFeatureEnabled } from '../../state/selectors' import { StoreType } from '../../state/redux' import { MapStateProps } from './Start.types' import Start from './Start' @@ -22,6 +23,8 @@ const mapStateToProps = (state: StoreType): MapStateProps => { isConnected: isWalletConnected, hasInitializedConnection: getError(state) !== null || isWalletConnected || isWalletConnecting, isLoadingProfile: getLoading(state).some((a) => a.type === LOAD_PROFILE_REQUEST), + isDiscoverExplorerAlphaEnabled: isFeatureEnabled(state, FeatureFlags.DiscoverExplorerAlpha), + areFeatureFlagsReady: areFeatureFlagsReady(state), profile: (wallet?.address && getProfileOfAddress(state, wallet?.address)) || null } } diff --git a/src/components/start/Start.css b/src/components/start/Start.css index 82209bc8..7967342a 100644 --- a/src/components/start/Start.css +++ b/src/components/start/Start.css @@ -101,3 +101,71 @@ bottom: 20px; right: 20px; } + +.ui.dimmer.explorer-alpha-notice-dimmer { + background-color: rgba(0, 0, 0, 0.5); +} + +.ui.modal.explorer-alpha-notice { + width: 480px; +} + +.ui.modal.explorer-alpha-notice .content { + display: flex; + flex-direction: column; + padding: 32px; + margin: 0px; + align-items: stretch; +} + +.ui.modal.explorer-alpha-notice .content .header { + text-align: center; + margin-bottom: 24px; +} + +.ui.modal.explorer-alpha-notice .content .header .icon { + background-image: url('./images/launch-desktop.svg'); + width: 225px; + height: 150px; + margin-bottom: 12px; +} + +.ui.modal.explorer-alpha-notice .content .header .title { + font-size: 32px; + font-weight: 700; + line-height: 39.52px; + text-align: center; + margin-bottom: 24px; +} + +.ui.modal.explorer-alpha-notice .content .header .text { + font-size: 16px; + font-weight: 400; + line-height: 24px; + text-align: center; + color: #5d5b67; + margin-bottom: 24px; +} + +.ui.modal.explorer-alpha-notice .content .actions { + display: flex; + flex-direction: column; + gap: 8px; +} + +.ui.modal.explorer-alpha-notice .content .actions .ui.button + .ui.button { + margin: 0px; +} +.ui.modal.explorer-alpha-notice .content .actions .ui.button:hover { + transform: none; + box-shadow: none; +} + +.ui.modal.explorer-alpha-notice .content .actions .ui.button:not(.primary) { + background-color: transparent; + color: var(--primary); +} + +.ui.modal.explorer-alpha-notice .content .actions .ui.button:not(.primary):hover { + background-color: rgba(var(--summer-red-raw), 0.1); +} diff --git a/src/components/start/Start.tsx b/src/components/start/Start.tsx index dcbd29eb..32c9f4fa 100644 --- a/src/components/start/Start.tsx +++ b/src/components/start/Start.tsx @@ -1,20 +1,29 @@ import { useCallback, useEffect, useState } from 'react' import { CommunityBubble } from 'decentraland-ui/dist/components/CommunityBubble' import { Button } from 'decentraland-ui/dist/components/Button/Button' +import { Modal } from 'decentraland-ui/dist/components/Modal/Modal' +import { ModalNavigation } from 'decentraland-ui/dist/components/ModalNavigation/ModalNavigation' import { Loader } from 'decentraland-ui/dist/components/Loader/Loader' import Icon from 'semantic-ui-react/dist/commonjs/elements/Icon/Icon' import { localStorageGetIdentity } from '@dcl/single-sign-on-client' import { SKIP_SETUP } from '../../integration/url' +import { launchDesktopApp } from '../../integration/desktop' import { CustomWearablePreview } from '../common/CustomWearablePreview' import BannerContainer from '../banners/BannerContainer' import logo from '../../images/simple-logo.svg' import { Props } from './Start.types' import './Start.css' -function getAuthURL() { +function getAuthURL(skipSetup: boolean) { var url = new URL(window.location.href) - if (!url.searchParams.has('skipSetup')) { - url.searchParams.append('skipSetup', 'true') + if (skipSetup) { + if (!url.searchParams.has('skipSetup')) { + url.searchParams.append('skipSetup', 'true') + } + } else { + if (url.searchParams.has('skipSetup')) { + url.searchParams.delete('skipSetup') + } } return `/auth/login?redirectTo=${encodeURIComponent(url.toString())}` } @@ -34,43 +43,99 @@ const useLocalStorageListener = (key: string) => { } export default function Start(props: Props) { - const { isConnected, isConnecting, wallet, profile, initializeKernel, isLoadingProfile, hasInitializedConnection } = - props + const { + isConnected, + isConnecting, + wallet, + profile, + initializeKernel, + isLoadingProfile, + hasInitializedConnection, + isDiscoverExplorerAlphaEnabled, + areFeatureFlagsReady + } = props const [isLoadingExplorer, setIsLoadingExplorer] = useState(false) + const [showExplorerAlphaNotice, setShowExplorerAlphaNotice] = useState(false) + const [isExplorerAlphaInstalled, setIsExplorerAlphaInstalled] = useState(false) + const [isLaunchingExplorerAlpha, setIsLaunchingExplorerAlpha] = useState(false) const decentralandConnectStorage = useLocalStorageListener('decentraland-connect-storage-key') const name = profile?.avatars[0].name useEffect(() => { + if (!areFeatureFlagsReady) { + return + } + if ((!isConnected && !isConnecting && hasInitializedConnection) || decentralandConnectStorage === null) { - window.location.replace(getAuthURL()) + window.location.replace(getAuthURL(!isDiscoverExplorerAlphaEnabled)) return } if (isConnected && wallet) { const identity = localStorageGetIdentity(wallet.address) if (!identity) { - window.location.replace(getAuthURL()) + window.location.replace(getAuthURL(!isDiscoverExplorerAlphaEnabled)) return } } - }, [isConnected, isConnecting, wallet, hasInitializedConnection, decentralandConnectStorage]) + }, [ + isConnected, + isConnecting, + wallet, + hasInitializedConnection, + decentralandConnectStorage, + isDiscoverExplorerAlphaEnabled, + areFeatureFlagsReady + ]) + + const handleReLaunch = useCallback(() => { + void launchDesktopApp(true) + }, [launchDesktopApp]) + + const handleContinueWithWebVersion = useCallback(() => { + setShowExplorerAlphaNotice(false) + }, [setShowExplorerAlphaNotice]) const handleJumpIn = useCallback(() => { + setShowExplorerAlphaNotice(false) initializeKernel() setIsLoadingExplorer(true) - }, []) + }, [setShowExplorerAlphaNotice, initializeKernel, setIsLoadingExplorer]) useEffect(() => { if (SKIP_SETUP) { handleJumpIn() + } else if (wallet && isDiscoverExplorerAlphaEnabled) { + const identity = localStorageGetIdentity(wallet.address) + if (identity) { + setIsLaunchingExplorerAlpha(true) + launchDesktopApp(true).then((isInstalled) => { + setIsExplorerAlphaInstalled(isInstalled) + setShowExplorerAlphaNotice(true) + setIsLaunchingExplorerAlpha(false) + }) + } } - }, [handleJumpIn]) + }, [ + handleJumpIn, + isDiscoverExplorerAlphaEnabled, + setIsExplorerAlphaInstalled, + setShowExplorerAlphaNotice, + setIsLaunchingExplorerAlpha, + wallet + ]) if (SKIP_SETUP) { return null } - if (!hasInitializedConnection || isLoadingProfile || isConnecting) { + if ( + !hasInitializedConnection || + isLoadingProfile || + isConnecting || + isLaunchingExplorerAlpha || + !areFeatureFlagsReady + ) { return (
@@ -96,7 +161,7 @@ export default function Start(props: Props) { jump into decentraland -
@@ -111,6 +176,48 @@ export default function Start(props: Props) { + + setShowExplorerAlphaNotice(false)} /> +
+ {!isExplorerAlphaInstalled ? ( + <> +
+ +

This is An Outdated Version of Decentraland

+

+ Decentraland has been re-released as a desktop app offering a completely new experience. Download and + discover improved performance, better graphics, and lots of new features! +

+
+
+ + +
+ + ) : ( + <> +
+ +

Continue on Desktop

+

For a better experience, we suggest you use the desktop explorer.

+
+
+ + +
+ + )} +
+
) } diff --git a/src/components/start/Start.types.ts b/src/components/start/Start.types.ts index b6ef5f21..610c40cf 100644 --- a/src/components/start/Start.types.ts +++ b/src/components/start/Start.types.ts @@ -9,9 +9,18 @@ export type Props = { isConnecting: boolean isLoadingProfile: boolean profile: Profile | null + isDiscoverExplorerAlphaEnabled: boolean + areFeatureFlagsReady: boolean } export type MapStateProps = Pick< Props, - 'wallet' | 'isConnected' | 'hasInitializedConnection' | 'isConnecting' | 'isLoadingProfile' | 'profile' + | 'wallet' + | 'isConnected' + | 'hasInitializedConnection' + | 'isConnecting' + | 'isLoadingProfile' + | 'profile' + | 'isDiscoverExplorerAlphaEnabled' + | 'areFeatureFlagsReady' > diff --git a/src/components/start/images/launch-desktop.svg b/src/components/start/images/launch-desktop.svg new file mode 100644 index 00000000..a7937b1e --- /dev/null +++ b/src/components/start/images/launch-desktop.svg @@ -0,0 +1,1309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/integration/desktop.ts b/src/integration/desktop.ts index d7d31edb..e71f31da 100644 --- a/src/integration/desktop.ts +++ b/src/integration/desktop.ts @@ -63,18 +63,18 @@ export const initializeDesktopApp = callOnce(() => { }) /** - * Try to launch the desktop version using the custom protocol `dcl://position=x,y&realm=zzz` + * Try to launch the desktop version using the custom protocol `decentraland://position=x,y&realm=zzz` * and return a boolean that represents if a loss of focus was detected on the current window * (assuming it was due to the interaction generated by the desktop version) */ -export const launchDesktopApp = callOnce(async () => { +export const launchDesktopApp = async (force = false) => { // prevent launch for desktop and mobile if (isElectron() || isMobile() || SKIP_SETUP) { return false } // prevent launch if the user logged in into the web version recently - if (hasRecentlyLoggedIn()) { + if (hasRecentlyLoggedIn() && !force) { return false } @@ -89,7 +89,7 @@ export const launchDesktopApp = callOnce(async () => { customProtocolParams.push(`realm=${data.realm}`) } - const customProtocolTarget = `dcl://${customProtocolParams.join('&')}` + const customProtocolTarget = `decentraland://${customProtocolParams.join('&')}` // assume that the desktop version is installed only if // we detect a loss of focus on window @@ -121,4 +121,4 @@ export const launchDesktopApp = callOnce(async () => { resolve(installed) }, 500) }) -}) +} diff --git a/src/state/selectors.ts b/src/state/selectors.ts index 1b6cea21..49881f03 100644 --- a/src/state/selectors.ts +++ b/src/state/selectors.ts @@ -12,9 +12,10 @@ export function getRequiredAnalyticsContext(state: StoreType): SessionTraits { export enum FeatureFlags { Stream = 'explorer-stream', - AuthDapp = 'dapps-auth-dapp', + AuthDapp = 'dapps-auth-dapp', SeamlessLogin = 'explorer-seamless_login_variant', - DesktopClientSignInWithAuthDapp = 'dapps-desktop-client-with-auth-dapp' + DesktopClientSignInWithAuthDapp = 'dapps-desktop-client-with-auth-dapp', + DiscoverExplorerAlpha = 'explorer-discover-alpha' } export enum VariantNames { @@ -26,6 +27,10 @@ export enum ABTestingVariant { Disabled = 'disabled' } +export function areFeatureFlagsReady(state: Pick): boolean { + return !!state.featureFlags.ready +} + export function isFeatureEnabled(state: Pick, key: string): boolean { const ff = state.featureFlags || defaultFeatureFlagsState return !!ff.flags[key] diff --git a/src/utils/callOnce.ts b/src/utils/callOnce.ts index 471d38c2..9f92fa7c 100644 --- a/src/utils/callOnce.ts +++ b/src/utils/callOnce.ts @@ -7,4 +7,4 @@ export function callOnce(fun: () => T): () => T { return result.value } -} \ No newline at end of file +}