ignite-parse-auth-kit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/CONTRIBUTING.md +0 -0
  2. package/LICENSE +21 -0
  3. package/README.md +492 -0
  4. package/app/app.tsx +116 -0
  5. package/app/components/AlertTongle.tsx +105 -0
  6. package/app/components/AutoImage.tsx +89 -0
  7. package/app/components/Button.tsx +248 -0
  8. package/app/components/Card.tsx +314 -0
  9. package/app/components/EmptyState.tsx +248 -0
  10. package/app/components/Header.tsx +332 -0
  11. package/app/components/Icon.tsx +140 -0
  12. package/app/components/ListItem.tsx +243 -0
  13. package/app/components/ListView.tsx +42 -0
  14. package/app/components/Screen.tsx +305 -0
  15. package/app/components/Text.test.tsx +23 -0
  16. package/app/components/Text.tsx +116 -0
  17. package/app/components/TextField.tsx +292 -0
  18. package/app/components/Toggle/Checkbox.tsx +123 -0
  19. package/app/components/Toggle/Radio.tsx +106 -0
  20. package/app/components/Toggle/Switch.tsx +264 -0
  21. package/app/components/Toggle/Toggle.tsx +285 -0
  22. package/app/components/index copy.ts +15 -0
  23. package/app/components/index.ts +18 -0
  24. package/app/config/config.base.ts +26 -0
  25. package/app/config/config.dev.ts +10 -0
  26. package/app/config/config.prod.ts +10 -0
  27. package/app/config/index.ts +28 -0
  28. package/app/context/AuthContext.tsx +14 -0
  29. package/app/context/EpisodeContext.tsx +136 -0
  30. package/app/context/auth/AuthProvider.tsx +340 -0
  31. package/app/context/auth/hooks.ts +29 -0
  32. package/app/context/auth/index.ts +38 -0
  33. package/app/context/auth/reducer.ts +68 -0
  34. package/app/context/auth/services.ts +394 -0
  35. package/app/context/auth/types.ts +99 -0
  36. package/app/context/auth/validation.ts +45 -0
  37. package/app/devtools/ReactotronClient.ts +9 -0
  38. package/app/devtools/ReactotronClient.web.ts +12 -0
  39. package/app/devtools/ReactotronConfig.ts +139 -0
  40. package/app/i18n/ar.ts +126 -0
  41. package/app/i18n/demo-ar.ts +464 -0
  42. package/app/i18n/demo-en.ts +462 -0
  43. package/app/i18n/demo-es.ts +469 -0
  44. package/app/i18n/demo-fr.ts +471 -0
  45. package/app/i18n/demo-hi.ts +468 -0
  46. package/app/i18n/demo-ja.ts +464 -0
  47. package/app/i18n/demo-ko.ts +457 -0
  48. package/app/i18n/en.ts +146 -0
  49. package/app/i18n/es.ts +132 -0
  50. package/app/i18n/fr.ts +132 -0
  51. package/app/i18n/hi.ts +131 -0
  52. package/app/i18n/index.ts +86 -0
  53. package/app/i18n/ja.ts +130 -0
  54. package/app/i18n/ko.ts +129 -0
  55. package/app/i18n/translate.ts +33 -0
  56. package/app/lib/Parse/index.ts +2 -0
  57. package/app/lib/Parse/parse.ts +62 -0
  58. package/app/navigators/AppNavigator.tsx +145 -0
  59. package/app/navigators/DemoNavigator.tsx +137 -0
  60. package/app/navigators/navigationUtilities.ts +208 -0
  61. package/app/screens/ChooseAuthScreen.tsx +224 -0
  62. package/app/screens/DemoCommunityScreen.tsx +141 -0
  63. package/app/screens/DemoDebugScreen.tsx +192 -0
  64. package/app/screens/DemoPodcastListScreen.tsx +387 -0
  65. package/app/screens/DemoShowroomScreen/DemoDivider.tsx +66 -0
  66. package/app/screens/DemoShowroomScreen/DemoShowroomScreen.tsx +313 -0
  67. package/app/screens/DemoShowroomScreen/DemoUseCase.tsx +52 -0
  68. package/app/screens/DemoShowroomScreen/DrawerIconButton.tsx +120 -0
  69. package/app/screens/DemoShowroomScreen/SectionListWithKeyboardAwareScrollView.tsx +59 -0
  70. package/app/screens/DemoShowroomScreen/demos/DemoAutoImage.tsx +230 -0
  71. package/app/screens/DemoShowroomScreen/demos/DemoButton.tsx +234 -0
  72. package/app/screens/DemoShowroomScreen/demos/DemoCard.tsx +181 -0
  73. package/app/screens/DemoShowroomScreen/demos/DemoEmptyState.tsx +78 -0
  74. package/app/screens/DemoShowroomScreen/demos/DemoHeader.tsx +151 -0
  75. package/app/screens/DemoShowroomScreen/demos/DemoIcon.tsx +115 -0
  76. package/app/screens/DemoShowroomScreen/demos/DemoListItem.tsx +218 -0
  77. package/app/screens/DemoShowroomScreen/demos/DemoText.tsx +144 -0
  78. package/app/screens/DemoShowroomScreen/demos/DemoTextField.tsx +233 -0
  79. package/app/screens/DemoShowroomScreen/demos/DemoToggle.tsx +354 -0
  80. package/app/screens/DemoShowroomScreen/demos/index.ts +12 -0
  81. package/app/screens/ErrorScreen/ErrorBoundary.tsx +76 -0
  82. package/app/screens/ErrorScreen/ErrorDetails.tsx +98 -0
  83. package/app/screens/ForgetPasswordScreen.tsx +180 -0
  84. package/app/screens/LoginScreen.tsx +260 -0
  85. package/app/screens/RegisterScreen.tsx +395 -0
  86. package/app/screens/WelcomeScreen.tsx +114 -0
  87. package/app/services/api/apiProblem.test.ts +73 -0
  88. package/app/services/api/apiProblem.ts +74 -0
  89. package/app/services/api/index.ts +91 -0
  90. package/app/services/api/types.ts +50 -0
  91. package/app/theme/colors.ts +85 -0
  92. package/app/theme/colorsDark.ts +50 -0
  93. package/app/theme/context.tsx +145 -0
  94. package/app/theme/context.utils.ts +25 -0
  95. package/app/theme/spacing.ts +14 -0
  96. package/app/theme/spacingDark.ts +14 -0
  97. package/app/theme/styles.ts +24 -0
  98. package/app/theme/theme.ts +23 -0
  99. package/app/theme/timing.ts +6 -0
  100. package/app/theme/types.ts +64 -0
  101. package/app/theme/typography.ts +71 -0
  102. package/app/utils/crashReporting.ts +62 -0
  103. package/app/utils/delay.ts +6 -0
  104. package/app/utils/formatDate.ts +49 -0
  105. package/app/utils/gestureHandler.native.ts +3 -0
  106. package/app/utils/gestureHandler.ts +6 -0
  107. package/app/utils/hasValidStringProp.ts +11 -0
  108. package/app/utils/openLinkInBrowser.ts +8 -0
  109. package/app/utils/storage/index.ts +82 -0
  110. package/app/utils/storage/storage.test.ts +61 -0
  111. package/app/utils/useHeader.tsx +37 -0
  112. package/app/utils/useIsMounted.ts +18 -0
  113. package/app/utils/useSafeAreaInsetsStyle.ts +46 -0
  114. package/app.config.ts +39 -0
  115. package/app.json +67 -0
  116. package/assets/icons/back.png +0 -0
  117. package/assets/icons/back@2x.png +0 -0
  118. package/assets/icons/back@3x.png +0 -0
  119. package/assets/icons/bell.png +0 -0
  120. package/assets/icons/bell@2x.png +0 -0
  121. package/assets/icons/bell@3x.png +0 -0
  122. package/assets/icons/caretLeft.png +0 -0
  123. package/assets/icons/caretLeft@2x.png +0 -0
  124. package/assets/icons/caretLeft@3x.png +0 -0
  125. package/assets/icons/caretRight.png +0 -0
  126. package/assets/icons/caretRight@2x.png +0 -0
  127. package/assets/icons/caretRight@3x.png +0 -0
  128. package/assets/icons/check.png +0 -0
  129. package/assets/icons/check@2x.png +0 -0
  130. package/assets/icons/check@3x.png +0 -0
  131. package/assets/icons/demo/clap.png +0 -0
  132. package/assets/icons/demo/clap@2x.png +0 -0
  133. package/assets/icons/demo/clap@3x.png +0 -0
  134. package/assets/icons/demo/community.png +0 -0
  135. package/assets/icons/demo/community@2x.png +0 -0
  136. package/assets/icons/demo/community@3x.png +0 -0
  137. package/assets/icons/demo/components.png +0 -0
  138. package/assets/icons/demo/components@2x.png +0 -0
  139. package/assets/icons/demo/components@3x.png +0 -0
  140. package/assets/icons/demo/debug.png +0 -0
  141. package/assets/icons/demo/debug@2x.png +0 -0
  142. package/assets/icons/demo/debug@3x.png +0 -0
  143. package/assets/icons/demo/github.png +0 -0
  144. package/assets/icons/demo/github@2x.png +0 -0
  145. package/assets/icons/demo/github@3x.png +0 -0
  146. package/assets/icons/demo/heart.png +0 -0
  147. package/assets/icons/demo/heart@2x.png +0 -0
  148. package/assets/icons/demo/heart@3x.png +0 -0
  149. package/assets/icons/demo/pin.png +0 -0
  150. package/assets/icons/demo/pin@2x.png +0 -0
  151. package/assets/icons/demo/pin@3x.png +0 -0
  152. package/assets/icons/demo/podcast.png +0 -0
  153. package/assets/icons/demo/podcast@2x.png +0 -0
  154. package/assets/icons/demo/podcast@3x.png +0 -0
  155. package/assets/icons/demo/slack.png +0 -0
  156. package/assets/icons/demo/slack@2x.png +0 -0
  157. package/assets/icons/demo/slack@3x.png +0 -0
  158. package/assets/icons/google.png +0 -0
  159. package/assets/icons/hidden.png +0 -0
  160. package/assets/icons/hidden@2x.png +0 -0
  161. package/assets/icons/hidden@3x.png +0 -0
  162. package/assets/icons/ladybug.png +0 -0
  163. package/assets/icons/ladybug@2x.png +0 -0
  164. package/assets/icons/ladybug@3x.png +0 -0
  165. package/assets/icons/lock.png +0 -0
  166. package/assets/icons/lock@2x.png +0 -0
  167. package/assets/icons/lock@3x.png +0 -0
  168. package/assets/icons/menu.png +0 -0
  169. package/assets/icons/menu@2x.png +0 -0
  170. package/assets/icons/menu@3x.png +0 -0
  171. package/assets/icons/more.png +0 -0
  172. package/assets/icons/more@2x.png +0 -0
  173. package/assets/icons/more@3x.png +0 -0
  174. package/assets/icons/settings.png +0 -0
  175. package/assets/icons/settings@2x.png +0 -0
  176. package/assets/icons/settings@3x.png +0 -0
  177. package/assets/icons/view.png +0 -0
  178. package/assets/icons/view@2x.png +0 -0
  179. package/assets/icons/view@3x.png +0 -0
  180. package/assets/icons/x.png +0 -0
  181. package/assets/icons/x@2x.png +0 -0
  182. package/assets/icons/x@3x.png +0 -0
  183. package/assets/images/app-icon-all.png +0 -0
  184. package/assets/images/app-icon-android-adaptive-background.png +0 -0
  185. package/assets/images/app-icon-android-adaptive-foreground.png +0 -0
  186. package/assets/images/app-icon-android-legacy.png +0 -0
  187. package/assets/images/app-icon-ios.png +0 -0
  188. package/assets/images/app-icon-web-favicon.png +0 -0
  189. package/assets/images/demo/cr-logo.png +0 -0
  190. package/assets/images/demo/cr-logo@2x.png +0 -0
  191. package/assets/images/demo/cr-logo@3x.png +0 -0
  192. package/assets/images/demo/rnl-logo.png +0 -0
  193. package/assets/images/demo/rnl-logo@2x.png +0 -0
  194. package/assets/images/demo/rnl-logo@3x.png +0 -0
  195. package/assets/images/demo/rnn-logo.png +0 -0
  196. package/assets/images/demo/rnn-logo@2x.png +0 -0
  197. package/assets/images/demo/rnn-logo@3x.png +0 -0
  198. package/assets/images/demo/rnr-image-1.png +0 -0
  199. package/assets/images/demo/rnr-image-1@2x.png +0 -0
  200. package/assets/images/demo/rnr-image-1@3x.png +0 -0
  201. package/assets/images/demo/rnr-image-2.png +0 -0
  202. package/assets/images/demo/rnr-image-2@2x.png +0 -0
  203. package/assets/images/demo/rnr-image-2@3x.png +0 -0
  204. package/assets/images/demo/rnr-image-3.png +0 -0
  205. package/assets/images/demo/rnr-image-3@2x.png +0 -0
  206. package/assets/images/demo/rnr-image-3@3x.png +0 -0
  207. package/assets/images/demo/rnr-logo.png +0 -0
  208. package/assets/images/demo/rnr-logo@2x.png +0 -0
  209. package/assets/images/demo/rnr-logo@3x.png +0 -0
  210. package/assets/images/logo.png +0 -0
  211. package/assets/images/logo@2x.png +0 -0
  212. package/assets/images/logo@3x.png +0 -0
  213. package/assets/images/sad-face.png +0 -0
  214. package/assets/images/sad-face@2x.png +0 -0
  215. package/assets/images/sad-face@3x.png +0 -0
  216. package/assets/images/welcome-face.png +0 -0
  217. package/assets/images/welcome-face@2x.png +0 -0
  218. package/assets/images/welcome-face@3x.png +0 -0
  219. package/babel.config.js +7 -0
  220. package/bin/cli.js +196 -0
  221. package/ignite/templates/app-icon/android-adaptive-background.png +0 -0
  222. package/ignite/templates/app-icon/android-adaptive-foreground.png +0 -0
  223. package/ignite/templates/app-icon/android-legacy.png +0 -0
  224. package/ignite/templates/app-icon/ios-universal.png +0 -0
  225. package/ignite/templates/component/NAME.tsx.ejs +39 -0
  226. package/ignite/templates/navigator/NAMENavigator.tsx.ejs +18 -0
  227. package/ignite/templates/screen/NAMEScreen.tsx.ejs +29 -0
  228. package/ignite/templates/splash-screen/logo.png +0 -0
  229. package/index.tsx +9 -0
  230. package/jest.config.js +5 -0
  231. package/metro.config.js +31 -0
  232. package/package.json +166 -0
  233. package/plugins/withSplashScreen.ts +69 -0
  234. package/src/app/_layout.tsx +58 -0
  235. package/src/app/index.tsx +5 -0
  236. package/test/i18n.test.ts +75 -0
  237. package/test/mockFile.ts +6 -0
  238. package/test/setup.ts +58 -0
  239. package/test/test-tsconfig.json +8 -0
  240. package/tsconfig.json +52 -0
  241. package/types/lib.es5.d.ts +25 -0
@@ -0,0 +1,91 @@
1
+ /**
2
+ * This Api class lets you define an API endpoint and methods to request
3
+ * data and process it.
4
+ *
5
+ * See the [Backend API Integration](https://docs.infinite.red/ignite-cli/boilerplate/app/services/#backend-api-integration)
6
+ * documentation for more details.
7
+ */
8
+ import {
9
+ ApiResponse, // @demo remove-current-line
10
+ ApisauceInstance,
11
+ create,
12
+ } from "apisauce"
13
+
14
+ import Config from "@/config"
15
+ import type { EpisodeItem } from "@/services/api/types" // @demo remove-current-line
16
+
17
+ import { GeneralApiProblem, getGeneralApiProblem } from "./apiProblem" // @demo remove-current-line
18
+ import type {
19
+ ApiConfig,
20
+ ApiFeedResponse, // @demo remove-current-line
21
+ } from "./types"
22
+
23
+ /**
24
+ * Configuring the apisauce instance.
25
+ */
26
+ export const DEFAULT_API_CONFIG: ApiConfig = {
27
+ url: Config.API_URL,
28
+ timeout: 10000,
29
+ }
30
+
31
+ /**
32
+ * Manages all requests to the API. You can use this class to build out
33
+ * various requests that you need to call from your backend API.
34
+ */
35
+ export class Api {
36
+ apisauce: ApisauceInstance
37
+ config: ApiConfig
38
+
39
+ /**
40
+ * Set up our API instance. Keep this lightweight!
41
+ */
42
+ constructor(config: ApiConfig = DEFAULT_API_CONFIG) {
43
+ this.config = config
44
+ this.apisauce = create({
45
+ baseURL: this.config.url,
46
+ timeout: this.config.timeout,
47
+ headers: {
48
+ Accept: "application/json",
49
+ },
50
+ })
51
+ }
52
+
53
+ // @demo remove-block-start
54
+ /**
55
+ * Gets a list of recent React Native Radio episodes.
56
+ */
57
+ async getEpisodes(): Promise<{ kind: "ok"; episodes: EpisodeItem[] } | GeneralApiProblem> {
58
+ // make the api call
59
+ const response: ApiResponse<ApiFeedResponse> = await this.apisauce.get(
60
+ `api.json?rss_url=https%3A%2F%2Ffeeds.simplecast.com%2FhEI_f9Dx`,
61
+ )
62
+
63
+ // the typical ways to die when calling an api
64
+ if (!response.ok) {
65
+ const problem = getGeneralApiProblem(response)
66
+ if (problem) return problem
67
+ }
68
+
69
+ // transform the data into the format we are expecting
70
+ try {
71
+ const rawData = response.data
72
+
73
+ // This is where we transform the data into the shape we expect for our model.
74
+ const episodes: EpisodeItem[] =
75
+ rawData?.items.map((raw) => ({
76
+ ...raw,
77
+ })) ?? []
78
+
79
+ return { kind: "ok", episodes }
80
+ } catch (e) {
81
+ if (__DEV__ && e instanceof Error) {
82
+ console.error(`Bad data: ${e.message}\n${response.data}`, e.stack)
83
+ }
84
+ return { kind: "bad-data" }
85
+ }
86
+ }
87
+ // @demo remove-block-end
88
+ }
89
+
90
+ // Singleton instance of the API for convenience
91
+ export const api = new Api()
@@ -0,0 +1,50 @@
1
+ /**
2
+ * These types indicate the shape of the data you expect to receive from your
3
+ * API endpoint, assuming it's a JSON object like we have.
4
+ */
5
+ export interface EpisodeItem {
6
+ title: string
7
+ pubDate: string
8
+ link: string
9
+ guid: string
10
+ author: string
11
+ thumbnail: string
12
+ description: string
13
+ content: string
14
+ enclosure: {
15
+ link: string
16
+ type: string
17
+ length: number
18
+ duration: number
19
+ rating: { scheme: string; value: string }
20
+ }
21
+ categories: string[]
22
+ }
23
+
24
+ export interface ApiFeedResponse {
25
+ status: string
26
+ feed: {
27
+ url: string
28
+ title: string
29
+ link: string
30
+ author: string
31
+ description: string
32
+ image: string
33
+ }
34
+ items: EpisodeItem[]
35
+ }
36
+
37
+ /**
38
+ * The options used to configure apisauce.
39
+ */
40
+ export interface ApiConfig {
41
+ /**
42
+ * The URL of the api.
43
+ */
44
+ url: string
45
+
46
+ /**
47
+ * Milliseconds before we timeout the request.
48
+ */
49
+ timeout: number
50
+ }
@@ -0,0 +1,85 @@
1
+ const palette = {
2
+ neutral100: "#FFFFFF",
3
+ neutral200: "#F4F2F1",
4
+ neutral300: "#D7CEC9",
5
+ neutral400: "#B6ACA6",
6
+ neutral500: "#978F8A",
7
+ neutral600: "#564E4A",
8
+ neutral700: "#3C3836",
9
+ neutral800: "#191015",
10
+ neutral900: "#000000",
11
+
12
+ primary100: "#F4E0D9",
13
+ primary200: "#E8C1B4",
14
+ primary300: "#DDA28E",
15
+ primary400: "#D28468",
16
+ primary500: "#C76542",
17
+ primary600: "#A54F31",
18
+
19
+ secondary100: "#DCDDE9",
20
+ secondary200: "#BCC0D6",
21
+ secondary300: "#9196B9",
22
+ secondary400: "#626894",
23
+ secondary500: "#41476E",
24
+
25
+ accent100: "#FFEED4",
26
+ accent200: "#FFE1B2",
27
+ accent300: "#FDD495",
28
+ accent400: "#FBC878",
29
+ accent500: "#FFBB50",
30
+
31
+ angry100: "#F2D6CD",
32
+ angry500: "#C03403",
33
+
34
+ overlay20: "rgba(25, 16, 21, 0.2)",
35
+ overlay50: "rgba(25, 16, 21, 0.5)",
36
+ } as const
37
+
38
+ export const colors = {
39
+ /**
40
+ * The palette is available to use, but prefer using the name.
41
+ * This is only included for rare, one-off cases. Try to use
42
+ * semantic names as much as possible.
43
+ */
44
+ palette,
45
+ /**
46
+ * A helper for making something see-thru.
47
+ */
48
+ transparent: "rgba(0, 0, 0, 0)",
49
+ /**
50
+ * The default text color in many components.
51
+ */
52
+ text: palette.neutral800,
53
+ /**
54
+ * Secondary text information.
55
+ */
56
+ textDim: palette.neutral600,
57
+ /**
58
+ * The default color of the screen background.
59
+ */
60
+ background: palette.neutral200,
61
+ /**
62
+ * The default border color.
63
+ */
64
+ border: palette.neutral400,
65
+ /**
66
+ * The main tinting color.
67
+ */
68
+ tint: palette.primary500,
69
+ /**
70
+ * The inactive tinting color.
71
+ */
72
+ tintInactive: palette.neutral300,
73
+ /**
74
+ * A subtle color used for lines.
75
+ */
76
+ separator: palette.neutral300,
77
+ /**
78
+ * Error messages.
79
+ */
80
+ error: palette.angry500,
81
+ /**
82
+ * Error Background.
83
+ */
84
+ errorBackground: palette.angry100,
85
+ } as const
@@ -0,0 +1,50 @@
1
+ const palette = {
2
+ neutral900: "#FFFFFF",
3
+ neutral800: "#F4F2F1",
4
+ neutral700: "#D7CEC9",
5
+ neutral600: "#B6ACA6",
6
+ neutral500: "#978F8A",
7
+ neutral400: "#564E4A",
8
+ neutral300: "#3C3836",
9
+ neutral200: "#191015",
10
+ neutral100: "#000000",
11
+
12
+ primary600: "#F4E0D9",
13
+ primary500: "#E8C1B4",
14
+ primary400: "#DDA28E",
15
+ primary300: "#D28468",
16
+ primary200: "#C76542",
17
+ primary100: "#A54F31",
18
+
19
+ secondary500: "#DCDDE9",
20
+ secondary400: "#BCC0D6",
21
+ secondary300: "#9196B9",
22
+ secondary200: "#626894",
23
+ secondary100: "#41476E",
24
+
25
+ accent500: "#FFEED4",
26
+ accent400: "#FFE1B2",
27
+ accent300: "#FDD495",
28
+ accent200: "#FBC878",
29
+ accent100: "#FFBB50",
30
+
31
+ angry100: "#F2D6CD",
32
+ angry500: "#C03403",
33
+
34
+ overlay20: "rgba(25, 16, 21, 0.2)",
35
+ overlay50: "rgba(25, 16, 21, 0.5)",
36
+ } as const
37
+
38
+ export const colors = {
39
+ palette,
40
+ transparent: "rgba(0, 0, 0, 0)",
41
+ text: palette.neutral800,
42
+ textDim: palette.neutral600,
43
+ background: palette.neutral200,
44
+ border: palette.neutral400,
45
+ tint: palette.primary500,
46
+ tintInactive: palette.neutral300,
47
+ separator: palette.neutral300,
48
+ error: palette.angry500,
49
+ errorBackground: palette.angry100,
50
+ } as const
@@ -0,0 +1,145 @@
1
+ import {
2
+ createContext,
3
+ FC,
4
+ PropsWithChildren,
5
+ useCallback,
6
+ useContext,
7
+ useEffect,
8
+ useMemo,
9
+ } from "react"
10
+ import { StyleProp, useColorScheme } from "react-native"
11
+ import {
12
+ DarkTheme as NavDarkTheme,
13
+ DefaultTheme as NavDefaultTheme,
14
+ Theme as NavTheme,
15
+ } from "@react-navigation/native"
16
+ import { useMMKVString } from "react-native-mmkv"
17
+
18
+ import { storage } from "@/utils/storage"
19
+
20
+ import { setImperativeTheming } from "./context.utils"
21
+ import { darkTheme, lightTheme } from "./theme"
22
+ import type {
23
+ AllowedStylesT,
24
+ ImmutableThemeContextModeT,
25
+ Theme,
26
+ ThemeContextModeT,
27
+ ThemedFnT,
28
+ ThemedStyle,
29
+ } from "./types"
30
+
31
+ export type ThemeContextType = {
32
+ navigationTheme: NavTheme
33
+ setThemeContextOverride: (newTheme: ThemeContextModeT) => void
34
+ theme: Theme
35
+ themeContext: ImmutableThemeContextModeT
36
+ themed: ThemedFnT
37
+ }
38
+
39
+ export const ThemeContext = createContext<ThemeContextType | null>(null)
40
+
41
+ export interface ThemeProviderProps {
42
+ initialContext?: ThemeContextModeT
43
+ }
44
+
45
+ /**
46
+ * The ThemeProvider is the heart and soul of the design token system. It provides a context wrapper
47
+ * for your entire app to consume the design tokens as well as global functionality like the app's theme.
48
+ *
49
+ * To get started, you want to wrap your entire app's JSX hierarchy in `ThemeProvider`
50
+ * and then use the `useAppTheme()` hook to access the theme context.
51
+ *
52
+ * Documentation: https://docs.infinite.red/ignite-cli/boilerplate/app/theme/Theming/
53
+ */
54
+ export const ThemeProvider: FC<PropsWithChildren<ThemeProviderProps>> = ({
55
+ children,
56
+ initialContext,
57
+ }) => {
58
+ // The operating system theme:
59
+ const systemColorScheme = useColorScheme()
60
+ // Our saved theme context: can be "light", "dark", or undefined (system theme)
61
+ const [themeScheme, setThemeScheme] = useMMKVString("ignite.themeScheme", storage)
62
+
63
+ /**
64
+ * This function is used to set the theme context and is exported from the useAppTheme() hook.
65
+ * - setThemeContextOverride("dark") sets the app theme to dark no matter what the system theme is.
66
+ * - setThemeContextOverride("light") sets the app theme to light no matter what the system theme is.
67
+ * - setThemeContextOverride(undefined) the app will follow the operating system theme.
68
+ */
69
+ const setThemeContextOverride = useCallback(
70
+ (newTheme: ThemeContextModeT) => {
71
+ setThemeScheme(newTheme)
72
+ },
73
+ [setThemeScheme],
74
+ )
75
+
76
+ /**
77
+ * initialContext is the theme context passed in from the app.tsx file and always takes precedence.
78
+ * themeScheme is the value from MMKV. If undefined, we fall back to the system theme
79
+ * systemColorScheme is the value from the device. If undefined, we fall back to "light"
80
+ */
81
+ const themeContext: ImmutableThemeContextModeT = useMemo(() => {
82
+ const t = initialContext || themeScheme || (!!systemColorScheme ? systemColorScheme : "light")
83
+ return t === "dark" ? "dark" : "light"
84
+ }, [initialContext, themeScheme, systemColorScheme])
85
+
86
+ const navigationTheme: NavTheme = useMemo(() => {
87
+ switch (themeContext) {
88
+ case "dark":
89
+ return NavDarkTheme
90
+ default:
91
+ return NavDefaultTheme
92
+ }
93
+ }, [themeContext])
94
+
95
+ const theme: Theme = useMemo(() => {
96
+ switch (themeContext) {
97
+ case "dark":
98
+ return darkTheme
99
+ default:
100
+ return lightTheme
101
+ }
102
+ }, [themeContext])
103
+
104
+ useEffect(() => {
105
+ setImperativeTheming(theme)
106
+ }, [theme])
107
+
108
+ const themed = useCallback(
109
+ <T,>(styleOrStyleFn: AllowedStylesT<T>) => {
110
+ const flatStyles = [styleOrStyleFn].flat(3) as (ThemedStyle<T> | StyleProp<T>)[]
111
+ const stylesArray = flatStyles.map((f) => {
112
+ if (typeof f === "function") {
113
+ return (f as ThemedStyle<T>)(theme)
114
+ } else {
115
+ return f
116
+ }
117
+ })
118
+ // Flatten the array of styles into a single object
119
+ return Object.assign({}, ...stylesArray) as T
120
+ },
121
+ [theme],
122
+ )
123
+
124
+ const value = {
125
+ navigationTheme,
126
+ theme,
127
+ themeContext,
128
+ setThemeContextOverride,
129
+ themed,
130
+ }
131
+
132
+ return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
133
+ }
134
+
135
+ /**
136
+ * This is the primary hook that you will use to access the theme context in your components.
137
+ * Documentation: https://docs.infinite.red/ignite-cli/boilerplate/app/theme/useAppTheme.tsx/
138
+ */
139
+ export const useAppTheme = () => {
140
+ const context = useContext(ThemeContext)
141
+ if (!context) {
142
+ throw new Error("useAppTheme must be used within an ThemeProvider")
143
+ }
144
+ return context
145
+ }
@@ -0,0 +1,25 @@
1
+ import type { Theme } from "./types"
2
+
3
+ const systemui = require("expo-system-ui")
4
+
5
+ /**
6
+ * Set the system UI background color to the given color. This is only available if the app has
7
+ * installed expo-system-ui.
8
+ *
9
+ * @param color The color to set the system UI background to
10
+ */
11
+ export const setSystemUIBackgroundColor = (color: string) => {
12
+ if (systemui) {
13
+ systemui.setBackgroundColorAsync(color)
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Set the app's native background color to match the theme.
19
+ * This is only available if the app has installed expo-system-ui
20
+ *
21
+ * @param theme The theme object to use for the background color
22
+ */
23
+ export const setImperativeTheming = (theme: Theme) => {
24
+ setSystemUIBackgroundColor(theme.colors.background)
25
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ Use these spacings for margins/paddings and other whitespace throughout your app.
3
+ */
4
+ export const spacing = {
5
+ xxxs: 2,
6
+ xxs: 4,
7
+ xs: 8,
8
+ sm: 12,
9
+ md: 16,
10
+ lg: 24,
11
+ xl: 32,
12
+ xxl: 48,
13
+ xxxl: 64,
14
+ } as const
@@ -0,0 +1,14 @@
1
+ const SPACING_MULTIPLIER = 1.0
2
+
3
+ // This is an example of how you can have different spacing values for different themes.
4
+ export const spacing = {
5
+ xxxs: 2 * SPACING_MULTIPLIER,
6
+ xxs: 4 * SPACING_MULTIPLIER,
7
+ xs: 8 * SPACING_MULTIPLIER,
8
+ sm: 12 * SPACING_MULTIPLIER,
9
+ md: 16 * SPACING_MULTIPLIER,
10
+ lg: 24 * SPACING_MULTIPLIER,
11
+ xl: 32 * SPACING_MULTIPLIER,
12
+ xxl: 48 * SPACING_MULTIPLIER,
13
+ xxxl: 64 * SPACING_MULTIPLIER,
14
+ } as const
@@ -0,0 +1,24 @@
1
+ import { ViewStyle } from "react-native"
2
+
3
+ import { spacing } from "./spacing" // @demo remove-current-line
4
+
5
+ /* Use this file to define styles that are used in multiple places in your app. */
6
+ export const $styles = {
7
+ row: { flexDirection: "row" } as ViewStyle,
8
+ flex1: { flex: 1 } as ViewStyle,
9
+ flexWrap: { flexWrap: "wrap" } as ViewStyle,
10
+
11
+ // @demo remove-block-start
12
+ container: {
13
+ paddingTop: spacing.lg + spacing.xl,
14
+ paddingHorizontal: spacing.lg,
15
+ } as ViewStyle,
16
+ // @demo remove-block-end
17
+ toggleInner: {
18
+ width: "100%",
19
+ height: "100%",
20
+ alignItems: "center",
21
+ justifyContent: "center",
22
+ overflow: "hidden",
23
+ } as ViewStyle,
24
+ }
@@ -0,0 +1,23 @@
1
+ import { colors as colorsLight } from "./colors"
2
+ import { colors as colorsDark } from "./colorsDark"
3
+ import { spacing as spacingLight } from "./spacing"
4
+ import { spacing as spacingDark } from "./spacingDark"
5
+ import { timing } from "./timing"
6
+ import type { Theme } from "./types"
7
+ import { typography } from "./typography"
8
+
9
+ // Here we define our themes.
10
+ export const lightTheme: Theme = {
11
+ colors: colorsLight,
12
+ spacing: spacingLight,
13
+ typography,
14
+ timing,
15
+ isDark: false,
16
+ }
17
+ export const darkTheme: Theme = {
18
+ colors: colorsDark,
19
+ spacing: spacingDark,
20
+ typography,
21
+ timing,
22
+ isDark: true,
23
+ }
@@ -0,0 +1,6 @@
1
+ export const timing = {
2
+ /**
3
+ * The duration (ms) for quick animations.
4
+ */
5
+ quick: 300,
6
+ }
@@ -0,0 +1,64 @@
1
+ import type { StyleProp } from "react-native"
2
+
3
+ import { colors as colorsLight } from "./colors"
4
+ import { colors as colorsDark } from "./colorsDark"
5
+ import { spacing as spacingLight } from "./spacing"
6
+ import { spacing as spacingDark } from "./spacingDark"
7
+ import { timing } from "./timing"
8
+ import { typography } from "./typography"
9
+
10
+ // This supports "light" and "dark" themes by default. If undefined, it'll use the system theme
11
+ export type ImmutableThemeContextModeT = "light" | "dark"
12
+ export type ThemeContextModeT = ImmutableThemeContextModeT | undefined
13
+
14
+ // Because we have two themes, we need to define the types for each of them.
15
+ // colorsLight and colorsDark should have the same keys, but different values.
16
+ export type Colors = typeof colorsLight | typeof colorsDark
17
+ // The spacing type needs to take into account the different spacing values for light and dark themes.
18
+ export type Spacing = typeof spacingLight | typeof spacingDark
19
+
20
+ // These two are consistent across themes.
21
+ export type Timing = typeof timing
22
+ export type Typography = typeof typography
23
+
24
+ // The overall Theme object should contain all of the data you need to style your app.
25
+ export interface Theme {
26
+ colors: Colors
27
+ spacing: Spacing
28
+ typography: Typography
29
+ timing: Timing
30
+ isDark: boolean
31
+ }
32
+
33
+ /**
34
+ * Represents a function that returns a styled component based on the provided theme.
35
+ * @template T The type of the style.
36
+ * @param theme The theme object.
37
+ * @returns The styled component.
38
+ *
39
+ * @example
40
+ * const $container: ThemedStyle<ViewStyle> = (theme) => ({
41
+ * flex: 1,
42
+ * backgroundColor: theme.colors.background,
43
+ * justifyContent: "center",
44
+ * alignItems: "center",
45
+ * })
46
+ * // Then use in a component like so:
47
+ * const Component = () => {
48
+ * const { themed } = useAppTheme()
49
+ * return <View style={themed($container)} />
50
+ * }
51
+ */
52
+ export type ThemedStyle<T> = (theme: Theme) => T
53
+ export type ThemedStyleArray<T> = (
54
+ | ThemedStyle<T>
55
+ | StyleProp<T>
56
+ | (StyleProp<T> | ThemedStyle<T>)[]
57
+ )[]
58
+
59
+ /**
60
+ */
61
+ export type AllowedStylesT<T> = ThemedStyle<T> | StyleProp<T> | ThemedStyleArray<T>
62
+ /**
63
+ */
64
+ export type ThemedFnT = <T>(styleOrStyleFn: AllowedStylesT<T>) => T
@@ -0,0 +1,71 @@
1
+ // TODO: write documentation about fonts and typography along with guides on how to add custom fonts in own
2
+ // markdown file and add links from here
3
+
4
+ import { Platform } from "react-native"
5
+ import {
6
+ SpaceGrotesk_300Light as spaceGroteskLight,
7
+ SpaceGrotesk_400Regular as spaceGroteskRegular,
8
+ SpaceGrotesk_500Medium as spaceGroteskMedium,
9
+ SpaceGrotesk_600SemiBold as spaceGroteskSemiBold,
10
+ SpaceGrotesk_700Bold as spaceGroteskBold,
11
+ } from "@expo-google-fonts/space-grotesk"
12
+
13
+ export const customFontsToLoad = {
14
+ spaceGroteskLight,
15
+ spaceGroteskRegular,
16
+ spaceGroteskMedium,
17
+ spaceGroteskSemiBold,
18
+ spaceGroteskBold,
19
+ }
20
+
21
+ const fonts = {
22
+ spaceGrotesk: {
23
+ // Cross-platform Google font.
24
+ light: "spaceGroteskLight",
25
+ normal: "spaceGroteskRegular",
26
+ medium: "spaceGroteskMedium",
27
+ semiBold: "spaceGroteskSemiBold",
28
+ bold: "spaceGroteskBold",
29
+ },
30
+ helveticaNeue: {
31
+ // iOS only font.
32
+ thin: "HelveticaNeue-Thin",
33
+ light: "HelveticaNeue-Light",
34
+ normal: "Helvetica Neue",
35
+ medium: "HelveticaNeue-Medium",
36
+ },
37
+ courier: {
38
+ // iOS only font.
39
+ normal: "Courier",
40
+ },
41
+ sansSerif: {
42
+ // Android only font.
43
+ thin: "sans-serif-thin",
44
+ light: "sans-serif-light",
45
+ normal: "sans-serif",
46
+ medium: "sans-serif-medium",
47
+ },
48
+ monospace: {
49
+ // Android only font.
50
+ normal: "monospace",
51
+ },
52
+ }
53
+
54
+ export const typography = {
55
+ /**
56
+ * The fonts are available to use, but prefer using the semantic name.
57
+ */
58
+ fonts,
59
+ /**
60
+ * The primary font. Used in most places.
61
+ */
62
+ primary: fonts.spaceGrotesk,
63
+ /**
64
+ * An alternate font used for perhaps titles and stuff.
65
+ */
66
+ secondary: Platform.select({ ios: fonts.helveticaNeue, android: fonts.sansSerif }),
67
+ /**
68
+ * Lets get fancy with a monospace font!
69
+ */
70
+ code: Platform.select({ ios: fonts.courier, android: fonts.monospace }),
71
+ }