about summary refs log tree commit diff
path: root/src/state/models/discovery/onboarding.ts
blob: 8ad321ed959b43d6751bb74a2d4870803018a49a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import {makeAutoObservable} from 'mobx'
import {RootStoreModel} from '../root-store'
import {hasProp} from 'lib/type-guards'
import {track} from 'lib/analytics/analytics'
import {SuggestedActorsModel} from './suggested-actors'

export const OnboardingScreenSteps = {
  Welcome: 'Welcome',
  RecommendedFeeds: 'RecommendedFeeds',
  RecommendedFollows: 'RecommendedFollows',
  Home: 'Home',
} as const

type OnboardingStep =
  (typeof OnboardingScreenSteps)[keyof typeof OnboardingScreenSteps]
const OnboardingStepsArray = Object.values(OnboardingScreenSteps)
export class OnboardingModel {
  // state
  step: OnboardingStep = 'Home' // default state to skip onboarding, only enabled for new users by calling start()

  // data
  suggestedActors: SuggestedActorsModel

  constructor(public rootStore: RootStoreModel) {
    this.suggestedActors = new SuggestedActorsModel(this.rootStore)
    makeAutoObservable(this, {
      rootStore: false,
      hydrate: false,
      serialize: false,
    })
  }

  serialize(): unknown {
    return {
      step: this.step,
    }
  }

  hydrate(v: unknown) {
    if (typeof v === 'object' && v !== null) {
      if (
        hasProp(v, 'step') &&
        typeof v.step === 'string' &&
        OnboardingStepsArray.includes(v.step as OnboardingStep)
      ) {
        this.step = v.step as OnboardingStep
      }
    } else {
      // if there is no valid state, we'll just reset
      this.reset()
    }
  }

  /**
   * Returns the name of the next screen in the onboarding process based on the current step or screen name provided.
   * @param {OnboardingStep} [currentScreenName]
   * @returns name of next screen in the onboarding process
   */
  next(currentScreenName?: OnboardingStep) {
    currentScreenName = currentScreenName || this.step
    if (currentScreenName === 'Welcome') {
      this.step = 'RecommendedFeeds'
      return this.step
    } else if (this.step === 'RecommendedFeeds') {
      this.step = 'RecommendedFollows'
      // prefetch recommended follows
      this.suggestedActors.loadMore(true)
      return this.step
    } else if (this.step === 'RecommendedFollows') {
      this.finish()
      return this.step
    } else {
      // if we get here, we're in an invalid state, let's just go Home
      return 'Home'
    }
  }

  start() {
    this.step = 'Welcome'
    track('Onboarding:Begin')
  }

  finish() {
    this.step = 'Home'
    track('Onboarding:Complete')
  }

  reset() {
    this.step = 'Welcome'
    track('Onboarding:Reset')
  }

  skip() {
    this.step = 'Home'
    track('Onboarding:Skipped')
  }

  get isComplete() {
    return this.step === 'Home'
  }

  get isActive() {
    return !this.isComplete
  }
}