mirror of
https://github.com/urbit/shrub.git
synced 2024-12-19 16:51:42 +03:00
interface: sketch out tutorial state
This commit is contained in:
parent
9d78271c4d
commit
cca47cc3e7
120
pkg/interface/src/logic/lib/tutorialModal.ts
Normal file
120
pkg/interface/src/logic/lib/tutorialModal.ts
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import { TutorialProgress, Associations } from "~/types";
|
||||||
|
import {
|
||||||
|
AlignX,
|
||||||
|
AlignY,
|
||||||
|
} from "~/logic/lib/relativePosition";
|
||||||
|
|
||||||
|
export const MODAL_WIDTH = 256;
|
||||||
|
export const MODAL_HEIGHT = 180;
|
||||||
|
export const MODAL_WIDTH_PX = `${MODAL_WIDTH}px`;
|
||||||
|
export const MODAL_HEIGHT_PX = `${MODAL_HEIGHT}px`;
|
||||||
|
|
||||||
|
interface StepDetail {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
url: string;
|
||||||
|
alignX: AlignX | AlignX[];
|
||||||
|
alignY: AlignY | AlignY[];
|
||||||
|
offsetX: number;
|
||||||
|
offsetY: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasTutorialGroup(props: { associations: Associations }) {
|
||||||
|
return '/ship/~hastuc-dibtux/beginner-island' in props.associations.groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const progressDetails: Record<TutorialProgress, StepDetail> = {
|
||||||
|
hidden: {} as any,
|
||||||
|
done: {
|
||||||
|
title: "End",
|
||||||
|
description: "This tutorial is finished. Would you like to leave Beginner Island?",
|
||||||
|
url: "/",
|
||||||
|
alignX: "right",
|
||||||
|
alignY: "top",
|
||||||
|
offsetX: MODAL_WIDTH + 8,
|
||||||
|
offsetY: 0
|
||||||
|
},
|
||||||
|
start: {
|
||||||
|
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
|
||||||
|
},
|
||||||
|
'group-desc': {
|
||||||
|
title: "What's a group",
|
||||||
|
description: "A group contains members and tends to be centered around a topic or multiple topics.",
|
||||||
|
url: "/~landscape/ship/~hastuc-dibtux/beginner-island",
|
||||||
|
alignX: "left",
|
||||||
|
alignY: "top",
|
||||||
|
offsetX: MODAL_WIDTH + 8,
|
||||||
|
offsetY: (MODAL_HEIGHT / 2) - 8,
|
||||||
|
},
|
||||||
|
channels: {
|
||||||
|
title: "Channels",
|
||||||
|
description: "Inside a group you have three types of Channels: Chat, Collection, or Notebook. Mix and match these depending on your group context!",
|
||||||
|
url: "/~landscape/ship/~hastuc-dibtux/beginner-island",
|
||||||
|
alignY: "top",
|
||||||
|
alignX: "right",
|
||||||
|
offsetX: MODAL_WIDTH + 8,
|
||||||
|
offsetY: -8,
|
||||||
|
},
|
||||||
|
chat: {
|
||||||
|
title: "Chat",
|
||||||
|
description: "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/~hastuc-dibtux/beginner-island/resource/chat/ship/~hastuc-dibtux/chat-8401",
|
||||||
|
alignY: "top",
|
||||||
|
alignX: "right",
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: -32,
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
title: "Collection",
|
||||||
|
description: "A collection is where you can share and view links, images, and other media within your group. Every item in a Collection can have it’s own comment thread.",
|
||||||
|
url: "/~landscape/ship/~hastuc-dibtux/beginner-island/resource/link/ship/~hastuc-dibtux/link-4353",
|
||||||
|
alignY: "top",
|
||||||
|
alignX: "right",
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: -32,
|
||||||
|
},
|
||||||
|
publish: {
|
||||||
|
title: "Notebook",
|
||||||
|
description: "Notebooks are for creating long-form content within your group. Use markdown to create rich posts with headers, lists and images.",
|
||||||
|
url: "/~landscape/ship/~hastuc-dibtux/beginner-island/resource/publish/ship/~hastuc-dibtux/notebook-9148",
|
||||||
|
alignY: "top",
|
||||||
|
alignX: "right",
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: -32,
|
||||||
|
},
|
||||||
|
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/~hastuc-dibtux/beginner-island/resource/publish/ship/~hastuc-dibtux/notebook-9148/settings#notifications",
|
||||||
|
alignY: "top",
|
||||||
|
alignX: "right",
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: -32,
|
||||||
|
},
|
||||||
|
profile: {
|
||||||
|
title: "Profile",
|
||||||
|
description: "Your profile is customizable and can be shared with other ships. Enter as much or as little information as you’d like.",
|
||||||
|
url: `/~profile/~${window.ship}`,
|
||||||
|
alignY: "top",
|
||||||
|
alignX: "right",
|
||||||
|
offsetX: -300 + (MODAL_WIDTH / 2),
|
||||||
|
offsetY: -120 + (MODAL_HEIGHT / 2),
|
||||||
|
},
|
||||||
|
leap: {
|
||||||
|
title: "Leap",
|
||||||
|
description: "Leap allows you to go to a specific channel, message, collection, profile or group simply by typing in a command or selecting a shortcut from the dropdown menu.",
|
||||||
|
url: `/~profile/~${window.ship}`,
|
||||||
|
alignY: "top",
|
||||||
|
alignX: "left",
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: -32,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,13 +1,21 @@
|
|||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode } from "react";
|
||||||
|
import f from 'lodash/fp';
|
||||||
import create, { State } from 'zustand';
|
import create, { State } from 'zustand';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import { BackgroundConfig, RemoteContentPolicy } from "~/types/local-update";
|
import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress } from "~/types/local-update";
|
||||||
|
|
||||||
|
|
||||||
export interface LocalState extends State {
|
export interface LocalState extends State {
|
||||||
hideAvatars: boolean;
|
hideAvatars: boolean;
|
||||||
hideNicknames: boolean;
|
hideNicknames: boolean;
|
||||||
remoteContentPolicy: RemoteContentPolicy;
|
remoteContentPolicy: RemoteContentPolicy;
|
||||||
|
tutorialProgress: TutorialProgress;
|
||||||
|
tutorialRef: HTMLElement | null,
|
||||||
|
hideTutorial: () => void;
|
||||||
|
nextTutStep: () => void;
|
||||||
|
prevTutStep: () => void;
|
||||||
|
setTutorialRef: (el: HTMLElement | null) => void;
|
||||||
dark: boolean;
|
dark: boolean;
|
||||||
background: BackgroundConfig;
|
background: BackgroundConfig;
|
||||||
omniboxShown: boolean;
|
omniboxShown: boolean;
|
||||||
@ -15,12 +23,35 @@ export interface LocalState extends State {
|
|||||||
toggleOmnibox: () => void;
|
toggleOmnibox: () => void;
|
||||||
set: (fn: (state: LocalState) => void) => void
|
set: (fn: (state: LocalState) => void) => void
|
||||||
};
|
};
|
||||||
|
export const selectLocalState =
|
||||||
|
<K extends keyof LocalState>(keys: K[]) => f.pick<LocalState, K>(keys);
|
||||||
|
|
||||||
const useLocalState = create<LocalState>(persist((set, get) => ({
|
const useLocalState = create<LocalState>(persist((set, get) => ({
|
||||||
dark: false,
|
dark: false,
|
||||||
background: undefined,
|
background: undefined,
|
||||||
hideAvatars: false,
|
hideAvatars: false,
|
||||||
hideNicknames: false,
|
hideNicknames: false,
|
||||||
|
tutorialProgress: 'hidden',
|
||||||
|
tutorialRef: null,
|
||||||
|
setTutorialRef: (el: HTMLElement | null) => set(produce(state => {
|
||||||
|
state.tutorialRef = el;
|
||||||
|
})),
|
||||||
|
hideTutorial: () => set(produce(state => {
|
||||||
|
state.tutorialProgress = 'hidden';
|
||||||
|
state.tutorialRef = null;
|
||||||
|
})),
|
||||||
|
nextTutStep: () => set(produce(state => {
|
||||||
|
const currIdx = tutorialProgress.findIndex(p => p === state.tutorialProgress)
|
||||||
|
if(currIdx < tutorialProgress.length) {
|
||||||
|
state.tutorialProgress = tutorialProgress[currIdx + 1];
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
prevTutStep: () => set(produce(state => {
|
||||||
|
const currIdx = tutorialProgress.findIndex(p => p === state.tutorialProgress)
|
||||||
|
if(currIdx > 0) {
|
||||||
|
state.tutorialProgress = tutorialProgress[currIdx - 1];
|
||||||
|
}
|
||||||
|
})),
|
||||||
remoteContentPolicy: {
|
remoteContentPolicy: {
|
||||||
imageShown: true,
|
imageShown: true,
|
||||||
audioShown: true,
|
audioShown: true,
|
||||||
@ -41,7 +72,10 @@ const useLocalState = create<LocalState>(persist((set, get) => ({
|
|||||||
})),
|
})),
|
||||||
set: fn => set(produce(fn))
|
set: fn => set(produce(fn))
|
||||||
}), {
|
}), {
|
||||||
blacklist: ['suspendedFocus', 'toggleOmnibox', 'omniboxShown'],
|
blacklist: [
|
||||||
|
'suspendedFocus', 'toggleOmnibox', 'omniboxShown', 'tutorialProgress',
|
||||||
|
'prevTutStep', 'nextTutStep', 'tutorialRef', 'setTutorialRef'
|
||||||
|
],
|
||||||
name: 'localReducer'
|
name: 'localReducer'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
21
pkg/interface/src/views/components/useTutorialModal.tsx
Normal file
21
pkg/interface/src/views/components/useTutorialModal.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { TutorialProgress } from "~/types";
|
||||||
|
import useLocalState, { selectLocalState } from "~/logic/state/local";
|
||||||
|
|
||||||
|
const localSelector = selectLocalState(["tutorialProgress", "setTutorialRef"]);
|
||||||
|
|
||||||
|
export function useTutorialModal(
|
||||||
|
onProgress: TutorialProgress,
|
||||||
|
show: boolean,
|
||||||
|
anchorRef: HTMLElement | null
|
||||||
|
) {
|
||||||
|
const { tutorialProgress, setTutorialRef } = useLocalState(localSelector);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (show && onProgress === tutorialProgress && anchorRef) {
|
||||||
|
setTutorialRef(anchorRef);
|
||||||
|
}
|
||||||
|
}, [onProgress, tutorialProgress, show, anchorRef]);
|
||||||
|
|
||||||
|
return show && onProgress === tutorialProgress;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user