From fe22d33696ee7c05501e407119c05577e80cafba Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 11 Feb 2021 16:13:23 +1000 Subject: [PATCH] Tutorial: bring into line with designs --- pkg/interface/src/logic/lib/tutorialModal.ts | 78 ++++++--- pkg/interface/src/types/local-update.ts | 2 +- .../apps/notifications/notifications.tsx | 22 ++- .../views/apps/profile/components/Profile.tsx | 3 +- .../landscape/components/TutorialModal.tsx | 159 +++++++++++++----- 5 files changed, 192 insertions(+), 72 deletions(-) diff --git a/pkg/interface/src/logic/lib/tutorialModal.ts b/pkg/interface/src/logic/lib/tutorialModal.ts index cc7aacd2c0..ef1d753b1e 100644 --- a/pkg/interface/src/logic/lib/tutorialModal.ts +++ b/pkg/interface/src/logic/lib/tutorialModal.ts @@ -1,8 +1,9 @@ import { TutorialProgress, Associations } from "~/types"; import { AlignX, AlignY } from "~/logic/lib/relativePosition"; +import { Direction } from "~/views/components/Triangle"; export const MODAL_WIDTH = 256; -export const MODAL_HEIGHT = 180; +export const MODAL_HEIGHT = 256; export const MODAL_WIDTH_PX = `${MODAL_WIDTH}px`; export const MODAL_HEIGHT_PX = `${MODAL_HEIGHT}px`; @@ -20,6 +21,7 @@ interface StepDetail { alignY: AlignY | AlignY[]; offsetX: number; offsetY: number; + arrow: Direction; } export function hasTutorialGroup(props: { associations: Associations }) { @@ -28,8 +30,36 @@ export function hasTutorialGroup(props: { associations: Associations }) { ); } +export const getTrianglePosition = (dir: Direction) => { + const midY = `${MODAL_HEIGHT / 2 - 8}px`; + const midX = `${MODAL_WIDTH / 2 - 8}px`; + switch(dir) { + case 'East': + return { + top: midY, + right: '-32px' + }; + case 'West': + return { + top: midY, + left: '-32px' + } + case 'North': + return { + top: '-32px', + left: midX + }; + case 'South': + return { + bottom: '-32px', + left: midX + }; + } +} + export const progressDetails: Record = { hidden: {} as any, + exit: {} as any, done: { title: "End", description: @@ -41,14 +71,15 @@ export const progressDetails: Record = { offsetY: 0, }, start: { - title: "New group added", + title: "New Group added", description: "We just added you to the Beginner island group to show you around. This group is public, but other groups can be private", url: "/", alignX: "right", alignY: "top", - offsetX: MODAL_WIDTH + 8, - offsetY: 0, + arrow: "West", + offsetX: MODAL_WIDTH + 24, + offsetY: 64, }, "group-desc": { title: "What's a group", @@ -57,7 +88,8 @@ export const progressDetails: Record = { url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}`, alignX: "left", alignY: "top", - offsetX: MODAL_WIDTH + 8, + arrow: "East", + offsetX: MODAL_WIDTH + 24, offsetY: MODAL_HEIGHT / 2 - 8, }, channels: { @@ -67,7 +99,8 @@ export const progressDetails: Record = { url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}`, alignY: "top", alignX: "right", - offsetX: MODAL_WIDTH + 8, + arrow: "West", + offsetX: MODAL_WIDTH + 24, offsetY: -8, }, chat: { @@ -76,9 +109,10 @@ export const progressDetails: Record = { "Chat channels are for messaging within your group. Direct Messages are also supported, and are accessible from the “DMs” tile on the homescreen", url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}/resource/chat/ship/${TUTORIAL_HOST}/${TUTORIAL_CHAT}`, alignY: "top", + arrow: "North", alignX: "right", - offsetX: 0, - offsetY: -32, + offsetY: -56, + offsetX: -8, }, link: { title: "Collection", @@ -87,8 +121,9 @@ export const progressDetails: Record = { url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}/resource/link/ship/${TUTORIAL_HOST}/${TUTORIAL_LINKS}`, alignY: "top", alignX: "right", - offsetX: 0, - offsetY: -32, + arrow: "North", + offsetX: -8, + offsetY: -56, }, publish: { title: "Notebook", @@ -97,18 +132,19 @@ export const progressDetails: Record = { url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}/resource/publish/ship/${TUTORIAL_HOST}/${TUTORIAL_BOOK}`, alignY: "top", alignX: "right", - offsetX: 0, - offsetY: -32, + arrow: "North", + offsetX: -8, + offsetY: -56, }, notifications: { title: "Notifications", - description: - "Subscribing to a channel will send you notifications when there are new updates. You will also receive a notification when someone mentions your name in a channel.", - url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}/resource/publish/ship/${TUTORIAL_HOST}/${TUTORIAL_BOOK}/settings#notifications`, + description: "You will get updates from subscribed channels and mentions here. You can access Notifications through Leap.", + url: '/~notifications', alignY: "top", - alignX: "right", - offsetX: 0, - offsetY: -32, + alignX: "left", + arrow: "North", + offsetX: (MODAL_WIDTH / 2) - 16, + offsetY: -48, }, profile: { title: "Profile", @@ -117,6 +153,7 @@ export const progressDetails: Record = { url: `/~profile/~${window.ship}`, alignY: "top", alignX: "right", + arrow: "South", offsetX: -300 + MODAL_WIDTH / 2, offsetY: -120 + MODAL_HEIGHT / 2, }, @@ -127,7 +164,8 @@ export const progressDetails: Record = { url: `/~profile/~${window.ship}`, alignY: "top", alignX: "left", - offsetX: 0, - offsetY: -32, + arrow: "North", + offsetX: 0.3 *MODAL_HEIGHT, + offsetY: -48, }, }; diff --git a/pkg/interface/src/types/local-update.ts b/pkg/interface/src/types/local-update.ts index 814832e963..5849931fe2 100644 --- a/pkg/interface/src/types/local-update.ts +++ b/pkg/interface/src/types/local-update.ts @@ -1,4 +1,4 @@ -export const tutorialProgress = ['hidden', 'start', 'group-desc', 'channels', 'chat', 'link', 'publish', 'notifications', 'profile', 'leap', 'done'] as const; +export const tutorialProgress = ['hidden', 'start', 'group-desc', 'channels', 'chat', 'link', 'publish', 'profile', 'leap', 'notifications', 'done', 'exit'] as const; export type TutorialProgress = typeof tutorialProgress[number]; interface LocalUpdateSetDark { diff --git a/pkg/interface/src/views/apps/notifications/notifications.tsx b/pkg/interface/src/views/apps/notifications/notifications.tsx index 16f2658b27..42f35862e5 100644 --- a/pkg/interface/src/views/apps/notifications/notifications.tsx +++ b/pkg/interface/src/views/apps/notifications/notifications.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useState, useRef } from "react"; import _ from 'lodash'; import { Box, Col, Text, Row } from "@tlon/indigo-react"; import { Link, Switch, Route } from "react-router-dom"; @@ -12,11 +12,13 @@ import { Dropdown } from "~/views/components/Dropdown"; import { Formik } from "formik"; import { FormikOnBlur } from "~/views/components/FormikOnBlur"; import GroupSearch from "~/views/components/GroupSearch"; +import {useTutorialModal} from "~/views/components/useTutorialModal"; const baseUrl = "/~notifications"; -const HeaderLink = ( - props: PropFunc & { view?: string; current: string } +const HeaderLink = React.forwardRef(( + props: PropFunc & { view?: string; current: string }, + ref ) => { const { current, view, ...textProps } = props; const to = view ? `${baseUrl}/${view}` : baseUrl; @@ -24,10 +26,10 @@ const HeaderLink = ( return ( - + ); -}; +}); interface NotificationFilter { groups: string[]; @@ -37,15 +39,17 @@ export default function NotificationsScreen(props: any) { const relativePath = (p: string) => baseUrl + p; const [filter, setFilter] = useState({ groups: [] }); - const onSubmit = async (values: { groups: string }) => { - setFilter({ groups: values.groups ? [values.groups] : [] }); + const onSubmit = async ({ groups } : NotificationFilter) => { + setFilter({ groups }); }; const groupFilterDesc = filter.groups.length === 0 ? "All" : filter.groups .map((g) => props.associations?.groups?.[g]?.metadata?.title) - .join(", "); + .join(", "); + const anchorRef = useRef(null); + useTutorialModal('notifications', true, anchorRef.current); return ( Updates - + Inbox diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 0676d72487..53115b9736 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -54,13 +54,14 @@ export function Profile(props: any) { height="100%" width="100%"> { ship === `~${window.ship}` ? ( ) : null } - + {cover} ) => { - e.stopPropagation(); + const next = useCallback( () => { const idx = progress.findIndex((p) => p === tutorialProgress); const { url } = progressDetails[progress[idx + 1]]; - history.push(url); nextTutStep(); + history.push(url); }, [nextTutStep, history, tutorialProgress, setCoords] ); const prev = useCallback(() => { const idx = progress.findIndex((p) => p === tutorialProgress); - history.push(progressDetails[progress[idx - 1]].url); prevTutStep(); + history.push(progressDetails[progress[idx - 1]].url); }, [prevTutStep, history, tutorialProgress]); const updatePos = useCallback(() => { @@ -87,14 +92,25 @@ export function TutorialModal(props: { api: GlobalApi }) { if (newCoords) { setCoords(withMobile); + } else { + setCoords({}); + } }, [tutorialRef]); - const dismiss = useCallback(() => { + const dismiss = useCallback(async () => { hideTutorial(); - props.api.settings.putEntry("tutorial", "seen", true); + await props.api.settings.putEntry('tutorial', 'seen', true); }, [hideTutorial, props.api]); + const bailExit = useCallback(() => { + setPaused(false); + }, []); + + const tryExit = useCallback(() => { + setPaused(true); + }, []); + const leaveGroup = useCallback(async () => { await props.api.groups.leaveGroup(TUTORIAL_HOST, TUTORIAL_GROUP); }, [props.api]); @@ -109,27 +125,81 @@ export function TutorialModal(props: { api: GlobalApi }) { ) { const interval = setInterval(updatePos, 100); return () => { + setCoords({}); clearInterval(interval); }; } return () => {}; }, [tutorialRef, tutorialProgress, updatePos]); - // manually center final window - useEffect(() => { - if (tutorialProgress === "done") { - const { innerWidth, innerHeight } = window; - const left = ["0px", `${(innerWidth - MODAL_WIDTH) / 2}px`]; - const top = [null, `${(innerHeight - MODAL_HEIGHT) / 2}px`]; - const bottom = ["0px", null]; - setCoords({ top, left, bottom }); - } - }, [tutorialProgress]); + const triPos = getTrianglePosition(arrow); + + if (tutorialProgress === 'done') { + return ( + + + + + + Tutorial Finished + + + {progressIdx} of {progress.length - 1} + + + + This tutorial is finished. Would you like to leave Beginner Island? + + + + + Leave Group + + + + + + ); + } if (tutorialProgress === "hidden") { return null; } + if(paused) { + return ( + + + + + End Tutorial Now? + + + + You can always restart the tutorial by typing "tutorial" in Leap. + + + + + End Tutorial + + + + + + ) + + } + + + if(Object.keys(coords).length === 0) { + return null; + } + return ( + + - - {title} - + + + {title} + + + {progressIdx} of {progress.length - 2} + + + {description} - {tutorialProgress !== "done" ? ( - - - - - - {progressIdx}/{progress.length - 1} - - - - - - ) : ( - - - Leave Group - - - - )} + + { progressIdx > 1 && ( + + )} + +