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.
- package/CONTRIBUTING.md +0 -0
- package/LICENSE +21 -0
- package/README.md +492 -0
- package/app/app.tsx +116 -0
- package/app/components/AlertTongle.tsx +105 -0
- package/app/components/AutoImage.tsx +89 -0
- package/app/components/Button.tsx +248 -0
- package/app/components/Card.tsx +314 -0
- package/app/components/EmptyState.tsx +248 -0
- package/app/components/Header.tsx +332 -0
- package/app/components/Icon.tsx +140 -0
- package/app/components/ListItem.tsx +243 -0
- package/app/components/ListView.tsx +42 -0
- package/app/components/Screen.tsx +305 -0
- package/app/components/Text.test.tsx +23 -0
- package/app/components/Text.tsx +116 -0
- package/app/components/TextField.tsx +292 -0
- package/app/components/Toggle/Checkbox.tsx +123 -0
- package/app/components/Toggle/Radio.tsx +106 -0
- package/app/components/Toggle/Switch.tsx +264 -0
- package/app/components/Toggle/Toggle.tsx +285 -0
- package/app/components/index copy.ts +15 -0
- package/app/components/index.ts +18 -0
- package/app/config/config.base.ts +26 -0
- package/app/config/config.dev.ts +10 -0
- package/app/config/config.prod.ts +10 -0
- package/app/config/index.ts +28 -0
- package/app/context/AuthContext.tsx +14 -0
- package/app/context/EpisodeContext.tsx +136 -0
- package/app/context/auth/AuthProvider.tsx +340 -0
- package/app/context/auth/hooks.ts +29 -0
- package/app/context/auth/index.ts +38 -0
- package/app/context/auth/reducer.ts +68 -0
- package/app/context/auth/services.ts +394 -0
- package/app/context/auth/types.ts +99 -0
- package/app/context/auth/validation.ts +45 -0
- package/app/devtools/ReactotronClient.ts +9 -0
- package/app/devtools/ReactotronClient.web.ts +12 -0
- package/app/devtools/ReactotronConfig.ts +139 -0
- package/app/i18n/ar.ts +126 -0
- package/app/i18n/demo-ar.ts +464 -0
- package/app/i18n/demo-en.ts +462 -0
- package/app/i18n/demo-es.ts +469 -0
- package/app/i18n/demo-fr.ts +471 -0
- package/app/i18n/demo-hi.ts +468 -0
- package/app/i18n/demo-ja.ts +464 -0
- package/app/i18n/demo-ko.ts +457 -0
- package/app/i18n/en.ts +146 -0
- package/app/i18n/es.ts +132 -0
- package/app/i18n/fr.ts +132 -0
- package/app/i18n/hi.ts +131 -0
- package/app/i18n/index.ts +86 -0
- package/app/i18n/ja.ts +130 -0
- package/app/i18n/ko.ts +129 -0
- package/app/i18n/translate.ts +33 -0
- package/app/lib/Parse/index.ts +2 -0
- package/app/lib/Parse/parse.ts +62 -0
- package/app/navigators/AppNavigator.tsx +145 -0
- package/app/navigators/DemoNavigator.tsx +137 -0
- package/app/navigators/navigationUtilities.ts +208 -0
- package/app/screens/ChooseAuthScreen.tsx +224 -0
- package/app/screens/DemoCommunityScreen.tsx +141 -0
- package/app/screens/DemoDebugScreen.tsx +192 -0
- package/app/screens/DemoPodcastListScreen.tsx +387 -0
- package/app/screens/DemoShowroomScreen/DemoDivider.tsx +66 -0
- package/app/screens/DemoShowroomScreen/DemoShowroomScreen.tsx +313 -0
- package/app/screens/DemoShowroomScreen/DemoUseCase.tsx +52 -0
- package/app/screens/DemoShowroomScreen/DrawerIconButton.tsx +120 -0
- package/app/screens/DemoShowroomScreen/SectionListWithKeyboardAwareScrollView.tsx +59 -0
- package/app/screens/DemoShowroomScreen/demos/DemoAutoImage.tsx +230 -0
- package/app/screens/DemoShowroomScreen/demos/DemoButton.tsx +234 -0
- package/app/screens/DemoShowroomScreen/demos/DemoCard.tsx +181 -0
- package/app/screens/DemoShowroomScreen/demos/DemoEmptyState.tsx +78 -0
- package/app/screens/DemoShowroomScreen/demos/DemoHeader.tsx +151 -0
- package/app/screens/DemoShowroomScreen/demos/DemoIcon.tsx +115 -0
- package/app/screens/DemoShowroomScreen/demos/DemoListItem.tsx +218 -0
- package/app/screens/DemoShowroomScreen/demos/DemoText.tsx +144 -0
- package/app/screens/DemoShowroomScreen/demos/DemoTextField.tsx +233 -0
- package/app/screens/DemoShowroomScreen/demos/DemoToggle.tsx +354 -0
- package/app/screens/DemoShowroomScreen/demos/index.ts +12 -0
- package/app/screens/ErrorScreen/ErrorBoundary.tsx +76 -0
- package/app/screens/ErrorScreen/ErrorDetails.tsx +98 -0
- package/app/screens/ForgetPasswordScreen.tsx +180 -0
- package/app/screens/LoginScreen.tsx +260 -0
- package/app/screens/RegisterScreen.tsx +395 -0
- package/app/screens/WelcomeScreen.tsx +114 -0
- package/app/services/api/apiProblem.test.ts +73 -0
- package/app/services/api/apiProblem.ts +74 -0
- package/app/services/api/index.ts +91 -0
- package/app/services/api/types.ts +50 -0
- package/app/theme/colors.ts +85 -0
- package/app/theme/colorsDark.ts +50 -0
- package/app/theme/context.tsx +145 -0
- package/app/theme/context.utils.ts +25 -0
- package/app/theme/spacing.ts +14 -0
- package/app/theme/spacingDark.ts +14 -0
- package/app/theme/styles.ts +24 -0
- package/app/theme/theme.ts +23 -0
- package/app/theme/timing.ts +6 -0
- package/app/theme/types.ts +64 -0
- package/app/theme/typography.ts +71 -0
- package/app/utils/crashReporting.ts +62 -0
- package/app/utils/delay.ts +6 -0
- package/app/utils/formatDate.ts +49 -0
- package/app/utils/gestureHandler.native.ts +3 -0
- package/app/utils/gestureHandler.ts +6 -0
- package/app/utils/hasValidStringProp.ts +11 -0
- package/app/utils/openLinkInBrowser.ts +8 -0
- package/app/utils/storage/index.ts +82 -0
- package/app/utils/storage/storage.test.ts +61 -0
- package/app/utils/useHeader.tsx +37 -0
- package/app/utils/useIsMounted.ts +18 -0
- package/app/utils/useSafeAreaInsetsStyle.ts +46 -0
- package/app.config.ts +39 -0
- package/app.json +67 -0
- package/assets/icons/back.png +0 -0
- package/assets/icons/back@2x.png +0 -0
- package/assets/icons/back@3x.png +0 -0
- package/assets/icons/bell.png +0 -0
- package/assets/icons/bell@2x.png +0 -0
- package/assets/icons/bell@3x.png +0 -0
- package/assets/icons/caretLeft.png +0 -0
- package/assets/icons/caretLeft@2x.png +0 -0
- package/assets/icons/caretLeft@3x.png +0 -0
- package/assets/icons/caretRight.png +0 -0
- package/assets/icons/caretRight@2x.png +0 -0
- package/assets/icons/caretRight@3x.png +0 -0
- package/assets/icons/check.png +0 -0
- package/assets/icons/check@2x.png +0 -0
- package/assets/icons/check@3x.png +0 -0
- package/assets/icons/demo/clap.png +0 -0
- package/assets/icons/demo/clap@2x.png +0 -0
- package/assets/icons/demo/clap@3x.png +0 -0
- package/assets/icons/demo/community.png +0 -0
- package/assets/icons/demo/community@2x.png +0 -0
- package/assets/icons/demo/community@3x.png +0 -0
- package/assets/icons/demo/components.png +0 -0
- package/assets/icons/demo/components@2x.png +0 -0
- package/assets/icons/demo/components@3x.png +0 -0
- package/assets/icons/demo/debug.png +0 -0
- package/assets/icons/demo/debug@2x.png +0 -0
- package/assets/icons/demo/debug@3x.png +0 -0
- package/assets/icons/demo/github.png +0 -0
- package/assets/icons/demo/github@2x.png +0 -0
- package/assets/icons/demo/github@3x.png +0 -0
- package/assets/icons/demo/heart.png +0 -0
- package/assets/icons/demo/heart@2x.png +0 -0
- package/assets/icons/demo/heart@3x.png +0 -0
- package/assets/icons/demo/pin.png +0 -0
- package/assets/icons/demo/pin@2x.png +0 -0
- package/assets/icons/demo/pin@3x.png +0 -0
- package/assets/icons/demo/podcast.png +0 -0
- package/assets/icons/demo/podcast@2x.png +0 -0
- package/assets/icons/demo/podcast@3x.png +0 -0
- package/assets/icons/demo/slack.png +0 -0
- package/assets/icons/demo/slack@2x.png +0 -0
- package/assets/icons/demo/slack@3x.png +0 -0
- package/assets/icons/google.png +0 -0
- package/assets/icons/hidden.png +0 -0
- package/assets/icons/hidden@2x.png +0 -0
- package/assets/icons/hidden@3x.png +0 -0
- package/assets/icons/ladybug.png +0 -0
- package/assets/icons/ladybug@2x.png +0 -0
- package/assets/icons/ladybug@3x.png +0 -0
- package/assets/icons/lock.png +0 -0
- package/assets/icons/lock@2x.png +0 -0
- package/assets/icons/lock@3x.png +0 -0
- package/assets/icons/menu.png +0 -0
- package/assets/icons/menu@2x.png +0 -0
- package/assets/icons/menu@3x.png +0 -0
- package/assets/icons/more.png +0 -0
- package/assets/icons/more@2x.png +0 -0
- package/assets/icons/more@3x.png +0 -0
- package/assets/icons/settings.png +0 -0
- package/assets/icons/settings@2x.png +0 -0
- package/assets/icons/settings@3x.png +0 -0
- package/assets/icons/view.png +0 -0
- package/assets/icons/view@2x.png +0 -0
- package/assets/icons/view@3x.png +0 -0
- package/assets/icons/x.png +0 -0
- package/assets/icons/x@2x.png +0 -0
- package/assets/icons/x@3x.png +0 -0
- package/assets/images/app-icon-all.png +0 -0
- package/assets/images/app-icon-android-adaptive-background.png +0 -0
- package/assets/images/app-icon-android-adaptive-foreground.png +0 -0
- package/assets/images/app-icon-android-legacy.png +0 -0
- package/assets/images/app-icon-ios.png +0 -0
- package/assets/images/app-icon-web-favicon.png +0 -0
- package/assets/images/demo/cr-logo.png +0 -0
- package/assets/images/demo/cr-logo@2x.png +0 -0
- package/assets/images/demo/cr-logo@3x.png +0 -0
- package/assets/images/demo/rnl-logo.png +0 -0
- package/assets/images/demo/rnl-logo@2x.png +0 -0
- package/assets/images/demo/rnl-logo@3x.png +0 -0
- package/assets/images/demo/rnn-logo.png +0 -0
- package/assets/images/demo/rnn-logo@2x.png +0 -0
- package/assets/images/demo/rnn-logo@3x.png +0 -0
- package/assets/images/demo/rnr-image-1.png +0 -0
- package/assets/images/demo/rnr-image-1@2x.png +0 -0
- package/assets/images/demo/rnr-image-1@3x.png +0 -0
- package/assets/images/demo/rnr-image-2.png +0 -0
- package/assets/images/demo/rnr-image-2@2x.png +0 -0
- package/assets/images/demo/rnr-image-2@3x.png +0 -0
- package/assets/images/demo/rnr-image-3.png +0 -0
- package/assets/images/demo/rnr-image-3@2x.png +0 -0
- package/assets/images/demo/rnr-image-3@3x.png +0 -0
- package/assets/images/demo/rnr-logo.png +0 -0
- package/assets/images/demo/rnr-logo@2x.png +0 -0
- package/assets/images/demo/rnr-logo@3x.png +0 -0
- package/assets/images/logo.png +0 -0
- package/assets/images/logo@2x.png +0 -0
- package/assets/images/logo@3x.png +0 -0
- package/assets/images/sad-face.png +0 -0
- package/assets/images/sad-face@2x.png +0 -0
- package/assets/images/sad-face@3x.png +0 -0
- package/assets/images/welcome-face.png +0 -0
- package/assets/images/welcome-face@2x.png +0 -0
- package/assets/images/welcome-face@3x.png +0 -0
- package/babel.config.js +7 -0
- package/bin/cli.js +196 -0
- package/ignite/templates/app-icon/android-adaptive-background.png +0 -0
- package/ignite/templates/app-icon/android-adaptive-foreground.png +0 -0
- package/ignite/templates/app-icon/android-legacy.png +0 -0
- package/ignite/templates/app-icon/ios-universal.png +0 -0
- package/ignite/templates/component/NAME.tsx.ejs +39 -0
- package/ignite/templates/navigator/NAMENavigator.tsx.ejs +18 -0
- package/ignite/templates/screen/NAMEScreen.tsx.ejs +29 -0
- package/ignite/templates/splash-screen/logo.png +0 -0
- package/index.tsx +9 -0
- package/jest.config.js +5 -0
- package/metro.config.js +31 -0
- package/package.json +166 -0
- package/plugins/withSplashScreen.ts +69 -0
- package/src/app/_layout.tsx +58 -0
- package/src/app/index.tsx +5 -0
- package/test/i18n.test.ts +75 -0
- package/test/mockFile.ts +6 -0
- package/test/setup.ts +58 -0
- package/test/test-tsconfig.json +8 -0
- package/tsconfig.json +52 -0
- 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
|
+
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,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
|
+
}
|