@planningcenter/chat-react-native 1.4.1 → 1.4.2-qa-84.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/build/components/display/heading.d.ts +8 -0
- package/build/components/display/heading.d.ts.map +1 -0
- package/build/components/display/heading.js +53 -0
- package/build/components/display/heading.js.map +1 -0
- package/build/components/display/index.d.ts +1 -0
- package/build/components/display/index.d.ts.map +1 -1
- package/build/components/display/index.js +1 -0
- package/build/components/display/index.js.map +1 -1
- package/build/components/display/text.d.ts.map +1 -1
- package/build/components/display/text.js +5 -4
- package/build/components/display/text.js.map +1 -1
- package/build/components/index.d.ts +3 -0
- package/build/components/index.d.ts.map +1 -0
- package/build/components/index.js +3 -0
- package/build/components/index.js.map +1 -0
- package/build/contexts/api_provider.d.ts.map +1 -1
- package/build/contexts/api_provider.js +7 -15
- package/build/contexts/api_provider.js.map +1 -1
- package/build/contexts/index.d.ts +3 -0
- package/build/contexts/index.d.ts.map +1 -0
- package/build/contexts/index.js +3 -0
- package/build/contexts/index.js.map +1 -0
- package/build/hooks/index.d.ts +2 -1
- package/build/hooks/index.d.ts.map +1 -1
- package/build/hooks/index.js +2 -1
- package/build/hooks/index.js.map +1 -1
- package/build/index.d.ts +4 -6
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -4
- package/build/index.js.map +1 -1
- package/build/screens/display.d.ts.map +1 -1
- package/build/screens/display.js +12 -5
- package/build/screens/display.js.map +1 -1
- package/build/utils/api.d.ts +9 -0
- package/build/utils/api.d.ts.map +1 -0
- package/build/utils/api.js +36 -0
- package/build/utils/api.js.map +1 -0
- package/build/utils/index.d.ts +3 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +3 -0
- package/build/utils/index.js.map +1 -0
- package/build/utils/platform_styles.d.ts +2 -0
- package/build/utils/platform_styles.d.ts.map +1 -0
- package/build/utils/platform_styles.js +7 -0
- package/build/utils/platform_styles.js.map +1 -0
- package/build/utils/session.d.ts +2 -0
- package/build/utils/session.d.ts.map +1 -1
- package/build/utils/session.js +12 -0
- package/build/utils/session.js.map +1 -1
- package/build/utils/space.d.ts +3 -0
- package/build/utils/space.d.ts.map +1 -0
- package/build/utils/space.js +22 -0
- package/build/utils/space.js.map +1 -0
- package/build/vendor/tapestry/tokens.d.ts +14 -0
- package/build/vendor/tapestry/tokens.d.ts.map +1 -1
- package/build/vendor/tapestry/tokens.js +13 -0
- package/build/vendor/tapestry/tokens.js.map +1 -1
- package/package.json +7 -5
- package/src/__tests__/session.tsx +43 -8
- package/src/__tests__/utils/space.tsx +60 -0
- package/src/components/display/heading.tsx +71 -0
- package/src/components/display/index.ts +1 -0
- package/src/components/display/text.tsx +5 -4
- package/src/components/index.tsx +2 -0
- package/src/contexts/api_provider.tsx +9 -18
- package/src/contexts/index.ts +2 -0
- package/src/hooks/index.ts +2 -1
- package/src/index.tsx +4 -15
- package/src/screens/display.tsx +12 -5
- package/src/utils/api.ts +47 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/platform_styles.ts +7 -0
- package/src/utils/session.ts +13 -0
- package/src/utils/space.ts +39 -0
- package/src/vendor/tapestry/tokens.ts +28 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TextStyle } from 'react-native';
|
|
1
2
|
export declare const tokens: {
|
|
2
3
|
borderRadiusDefault: number;
|
|
3
4
|
spacingFourth: number;
|
|
@@ -16,6 +17,19 @@ export declare const tokens: {
|
|
|
16
17
|
borderRadiusRound: number;
|
|
17
18
|
borderSizeDefault: number;
|
|
18
19
|
borderSizeThick: number;
|
|
20
|
+
fontWeightNormal: TextStyle["fontWeight"];
|
|
21
|
+
fontWeightMedium: TextStyle["fontWeight"];
|
|
22
|
+
fontWeightSemiBold: TextStyle["fontWeight"];
|
|
23
|
+
fontWeightBold: TextStyle["fontWeight"];
|
|
24
|
+
fontSize4xl: number;
|
|
25
|
+
fontSize3xl: number;
|
|
26
|
+
fontSize2xl: number;
|
|
27
|
+
fontSizeXl: number;
|
|
28
|
+
fontSizeLg: number;
|
|
29
|
+
fontSizeMd: number;
|
|
30
|
+
fontSizeSm: number;
|
|
31
|
+
fontSizeXs: number;
|
|
32
|
+
fontSize2xs: number;
|
|
19
33
|
colorNeutral7: string;
|
|
20
34
|
colorNeutral12: string;
|
|
21
35
|
colorNeutral15: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/vendor/tapestry/tokens.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/vendor/tapestry/tokens.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAoHxC,eAAO,MAAM,MAAM;yBAPI,MAAM;mBAhEZ,MAAM;iBACR,MAAM;cACT,MAAM;cACN,MAAM;cACN,MAAM;cACN,MAAM;cACN,MAAM;cACN,MAAM;cACN,MAAM;oBACA,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;uBACH,MAAM;uBACN,MAAM;qBACR,MAAM;sBACL,SAAS,CAAC,YAAY,CAAC;sBACvB,SAAS,CAAC,YAAY,CAAC;wBACrB,SAAS,CAAC,YAAY,CAAC;oBAC3B,SAAS,CAAC,YAAY,CAAC;iBAC1B,MAAM;iBACN,MAAM;iBACN,MAAM;gBACP,MAAM;gBACN,MAAM;gBACN,MAAM;gBACN,MAAM;gBACN,MAAM;iBACL,MAAM;mBAtEJ,MAAM;oBACL,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,MAAM;0BACA,MAAM;CAoG7B,CAAA"}
|
|
@@ -38,6 +38,19 @@ const numericPrimtives = {
|
|
|
38
38
|
borderRadiusRound: 56,
|
|
39
39
|
borderSizeDefault: 1,
|
|
40
40
|
borderSizeThick: 2,
|
|
41
|
+
fontWeightNormal: '400',
|
|
42
|
+
fontWeightMedium: '500',
|
|
43
|
+
fontWeightSemiBold: '600',
|
|
44
|
+
fontWeightBold: '700',
|
|
45
|
+
fontSize4xl: 32,
|
|
46
|
+
fontSize3xl: 28,
|
|
47
|
+
fontSize2xl: 24,
|
|
48
|
+
fontSizeXl: 20,
|
|
49
|
+
fontSizeLg: 18,
|
|
50
|
+
fontSizeMd: 16,
|
|
51
|
+
fontSizeSm: 14,
|
|
52
|
+
fontSizeXs: 12,
|
|
53
|
+
fontSize2xs: 10,
|
|
41
54
|
};
|
|
42
55
|
const numericAliases = {
|
|
43
56
|
borderRadiusDefault: numericPrimtives.borderRadiusMd,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../../src/vendor/tapestry/tokens.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,2FAA2F;AAC3F,0GAA0G;
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../../src/vendor/tapestry/tokens.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,2FAA2F;AAC3F,0GAA0G;AAyB1G,MAAM,eAAe,GAAmB;IACtC,aAAa,EAAE,gBAAgB;IAC/B,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,oBAAoB,EAAE,kBAAkB;CACzC,CAAA;AAkCD,MAAM,gBAAgB,GAAsB;IAC1C,aAAa,EAAE,CAAC;IAChB,WAAW,EAAE,CAAC;IACd,QAAQ,EAAE,CAAC;IACX,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;IACZ,cAAc,EAAE,CAAC;IACjB,cAAc,EAAE,CAAC;IACjB,cAAc,EAAE,CAAC;IACjB,cAAc,EAAE,EAAE;IAClB,iBAAiB,EAAE,EAAE;IACrB,iBAAiB,EAAE,CAAC;IACpB,eAAe,EAAE,CAAC;IAClB,gBAAgB,EAAE,KAAK;IACvB,gBAAgB,EAAE,KAAK;IACvB,kBAAkB,EAAE,KAAK;IACzB,cAAc,EAAE,KAAK;IACrB,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,EAAE;IACf,UAAU,EAAE,EAAE;IACd,UAAU,EAAE,EAAE;IACd,UAAU,EAAE,EAAE;IACd,UAAU,EAAE,EAAE;IACd,UAAU,EAAE,EAAE;IACd,WAAW,EAAE,EAAE;CAChB,CAAA;AAMD,MAAM,cAAc,GAAmB;IACrC,mBAAmB,EAAE,gBAAgB,CAAC,cAAc;CACrD,CAAA;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,GAAG,eAAe;IAClB,GAAG,gBAAgB;IACnB,GAAG,cAAc;CAClB,CAAA","sourcesContent":["// Copied from `@planningcenter/tapestry` package.\n// Defining these tokens locally is a temporary solution until the package supports mobile.\n// Tokens Reference: https://planningcenter.github.io/tapestry/?path=/docs/foundations-design-tokens--docs\n\nimport { TextStyle } from 'react-native'\n\ninterface ColorPrimitves {\n colorNeutral7: string\n colorNeutral12: string\n colorNeutral15: string\n colorNeutral17: string\n colorNeutral19: string\n colorNeutral24: string\n colorNeutral32: string\n colorNeutral45: string\n colorNeutral50: string\n colorNeutral58: string\n colorNeutral68: string\n colorNeutral81: string\n colorNeutral88: string\n colorNeutral93: string\n colorNeutral95: string\n colorNeutral97: string\n colorNeutral98: string\n colorNeutral100White: string\n}\n\nconst colorPrimitives: ColorPrimitves = {\n colorNeutral7: 'hsl(0, 0%, 7%)',\n colorNeutral12: 'hsl(0, 0%, 12%)',\n colorNeutral15: 'hsl(0, 0%, 15%)',\n colorNeutral17: 'hsl(0, 0%, 17%)',\n colorNeutral19: 'hsl(0, 0%, 19%)',\n colorNeutral24: 'hsl(0, 0%, 24%)',\n colorNeutral32: 'hsl(0, 0%, 32%)',\n colorNeutral45: 'hsl(0, 0%, 45%)',\n colorNeutral50: 'hsl(0, 0%, 50%)',\n colorNeutral58: 'hsl(0, 0%, 58%)',\n colorNeutral68: 'hsl(0, 0%, 68%)',\n colorNeutral81: 'hsl(0, 0%, 81%)',\n colorNeutral88: 'hsl(0, 0%, 88%)',\n colorNeutral93: 'hsl(0, 0%, 93%)',\n colorNeutral95: 'hsl(0, 0%, 95%)',\n colorNeutral97: 'hsl(0, 0%, 97%)',\n colorNeutral98: 'hsl(0, 0%, 98%)',\n colorNeutral100White: 'hsl(0, 0%, 100%)',\n}\n\ninterface NumericPrimitives {\n spacingFourth: number\n spacingHalf: number\n spacing1: number\n spacing2: number\n spacing3: number\n spacing4: number\n spacing5: number\n spacing6: number\n spacing7: number\n borderRadiusSm: number\n borderRadiusMd: number\n borderRadiusLg: number\n borderRadiusXl: number\n borderRadiusRound: number\n borderSizeDefault: number\n borderSizeThick: number\n fontWeightNormal: TextStyle['fontWeight']\n fontWeightMedium: TextStyle['fontWeight']\n fontWeightSemiBold: TextStyle['fontWeight']\n fontWeightBold: TextStyle['fontWeight']\n fontSize4xl: number\n fontSize3xl: number\n fontSize2xl: number\n fontSizeXl: number\n fontSizeLg: number\n fontSizeMd: number\n fontSizeSm: number\n fontSizeXs: number\n fontSize2xs: number\n}\n\nconst numericPrimtives: NumericPrimitives = {\n spacingFourth: 2,\n spacingHalf: 4,\n spacing1: 8,\n spacing2: 16,\n spacing3: 24,\n spacing4: 32,\n spacing5: 40,\n spacing6: 48,\n spacing7: 56,\n borderRadiusSm: 2,\n borderRadiusMd: 4,\n borderRadiusLg: 8,\n borderRadiusXl: 16,\n borderRadiusRound: 56,\n borderSizeDefault: 1,\n borderSizeThick: 2,\n fontWeightNormal: '400',\n fontWeightMedium: '500',\n fontWeightSemiBold: '600',\n fontWeightBold: '700',\n fontSize4xl: 32,\n fontSize3xl: 28,\n fontSize2xl: 24,\n fontSizeXl: 20,\n fontSizeLg: 18,\n fontSizeMd: 16,\n fontSizeSm: 14,\n fontSizeXs: 12,\n fontSize2xs: 10,\n}\n\ninterface NumericAliases {\n borderRadiusDefault: number\n}\n\nconst numericAliases: NumericAliases = {\n borderRadiusDefault: numericPrimtives.borderRadiusMd,\n}\n\nexport const tokens = {\n ...colorPrimitives,\n ...numericPrimtives,\n ...numericAliases,\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/chat-react-native",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2-qa-84.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"prepublishOnly": "expo-module prepublishOnly"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@planningcenter/chat-core": "^1.4.0"
|
|
20
|
+
"@planningcenter/chat-core": "^1.4.2-qa-84.0"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"@react-navigation/elements": "*",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"@tanstack/react-query": "^5.0.0",
|
|
26
26
|
"lodash": "*",
|
|
27
27
|
"react": "*",
|
|
28
|
-
"react-native": "*"
|
|
28
|
+
"react-native": "*",
|
|
29
|
+
"react-native-device-info": "*"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@react-native/eslint-config": "^0.77.0",
|
|
@@ -36,7 +37,8 @@
|
|
|
36
37
|
"expo-module-scripts": "^4.0.3",
|
|
37
38
|
"lodash": "^4.17.21",
|
|
38
39
|
"prettier": "^3.4.2",
|
|
39
|
-
"react-native": "0.74.5"
|
|
40
|
+
"react-native": "0.74.5",
|
|
41
|
+
"react-native-device-info": "^14.0.4"
|
|
40
42
|
},
|
|
41
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "3f31e659fe437da51e7cd2b7b7a1547d8564c360"
|
|
42
44
|
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { OAuthToken } from '../types'
|
|
2
2
|
import { Session } from '../utils/session'
|
|
3
3
|
|
|
4
|
+
const token: OAuthToken = {
|
|
5
|
+
access_token: 'access_token',
|
|
6
|
+
refresh_token: 'refresh_token',
|
|
7
|
+
token_type: undefined,
|
|
8
|
+
created_at: 0,
|
|
9
|
+
expires_in: undefined,
|
|
10
|
+
scope: '',
|
|
11
|
+
}
|
|
12
|
+
|
|
4
13
|
describe('Session', () => {
|
|
5
14
|
describe('constructor', () => {
|
|
6
15
|
it('should track the environment', () => {
|
|
@@ -10,14 +19,6 @@ describe('Session', () => {
|
|
|
10
19
|
})
|
|
11
20
|
|
|
12
21
|
it('should track the token', () => {
|
|
13
|
-
const token: OAuthToken = {
|
|
14
|
-
access_token: 'access_token',
|
|
15
|
-
refresh_token: 'refresh_token',
|
|
16
|
-
token_type: undefined,
|
|
17
|
-
created_at: 0,
|
|
18
|
-
expires_in: undefined,
|
|
19
|
-
scope: '',
|
|
20
|
-
}
|
|
21
22
|
const session = new Session({ token })
|
|
22
23
|
expect(session.token).toEqual(token)
|
|
23
24
|
expect(session.isAuthenticated).toEqual(true)
|
|
@@ -43,4 +44,38 @@ describe('Session', () => {
|
|
|
43
44
|
expect(session.uploadUrl).toBe('https://upload-staging.planningcenteronline.com/v2/files')
|
|
44
45
|
})
|
|
45
46
|
})
|
|
47
|
+
|
|
48
|
+
describe('hydrate', () => {
|
|
49
|
+
describe('success', () => {
|
|
50
|
+
it('should return a hydrated Session instance', () => {
|
|
51
|
+
const session = new Session({ token })
|
|
52
|
+
expect(session.token).toEqual(token)
|
|
53
|
+
expect(session.isAuthenticated).toEqual(true)
|
|
54
|
+
|
|
55
|
+
const sessionString = session.toString()
|
|
56
|
+
const hydratedSession = Session.hydrate(sessionString)
|
|
57
|
+
expect(hydratedSession.token).toEqual(token)
|
|
58
|
+
expect(hydratedSession.isAuthenticated).toEqual(true)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('should store the environment of hydrated Session instance', () => {
|
|
62
|
+
const env = 'production'
|
|
63
|
+
const session = new Session({ token, env })
|
|
64
|
+
|
|
65
|
+
const sessionString = session.toString()
|
|
66
|
+
const hydratedSession = Session.hydrate(sessionString)
|
|
67
|
+
expect(hydratedSession.env).toEqual(env)
|
|
68
|
+
expect(hydratedSession.token).toEqual(token)
|
|
69
|
+
expect(hydratedSession.isAuthenticated).toEqual(true)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('failure', () => {
|
|
74
|
+
it('should return a new Session instance', () => {
|
|
75
|
+
const session = Session.hydrate('')
|
|
76
|
+
expect(session.env).toBe('development')
|
|
77
|
+
expect(session.isAuthenticated).toBe(false)
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
})
|
|
46
81
|
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { space, SpacingValues } from '../../utils/space'
|
|
2
|
+
import { tokens } from '../../vendor/tapestry/tokens'
|
|
3
|
+
|
|
4
|
+
describe('space function', () => {
|
|
5
|
+
// Spy on console.warn to check for invalid inputs
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
jest.restoreAllMocks()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const validTokenValues: Record<SpacingValues, number> = {
|
|
15
|
+
0.25: tokens.spacingFourth,
|
|
16
|
+
0.5: tokens.spacingHalf,
|
|
17
|
+
1: tokens.spacing1,
|
|
18
|
+
1.5: tokens.spacing1 + tokens.spacingHalf,
|
|
19
|
+
2: tokens.spacing2,
|
|
20
|
+
2.5: tokens.spacing2 + tokens.spacingHalf,
|
|
21
|
+
3: tokens.spacing3,
|
|
22
|
+
3.5: tokens.spacing3 + tokens.spacingHalf,
|
|
23
|
+
4: tokens.spacing4,
|
|
24
|
+
4.5: tokens.spacing4 + tokens.spacingHalf,
|
|
25
|
+
5: tokens.spacing5,
|
|
26
|
+
5.5: tokens.spacing5 + tokens.spacingHalf,
|
|
27
|
+
6: tokens.spacing6,
|
|
28
|
+
6.5: tokens.spacing6 + tokens.spacingHalf,
|
|
29
|
+
7: tokens.spacing7,
|
|
30
|
+
7.5: tokens.spacing7 + tokens.spacingHalf,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
it('should return the correct token values for all valid SpacingValues', () => {
|
|
34
|
+
Object.entries(validTokenValues).forEach(([value, expected]) => {
|
|
35
|
+
expect(space(Number(value) as SpacingValues)).toBe(expected)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should warn and return 0 for an invalid value less than 1 (e.g., 0.8)', () => {
|
|
40
|
+
const result = space(0.8 as SpacingValues)
|
|
41
|
+
expect(result).toBe(0)
|
|
42
|
+
expect(console.warn).toHaveBeenCalledWith(warnText('0.8'))
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should warn and return 0 for an invalid fractional value (e.g., 1.8)', () => {
|
|
46
|
+
const result = space(1.8 as SpacingValues)
|
|
47
|
+
expect(result).toBe(0)
|
|
48
|
+
expect(console.warn).toHaveBeenCalledWith(warnText('1.8'))
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should warn and return 0 for a value greater than 7.5 (e.g., 19)', () => {
|
|
52
|
+
const result = space(19 as SpacingValues)
|
|
53
|
+
expect(result).toBe(0)
|
|
54
|
+
expect(console.warn).toHaveBeenCalledWith(warnText('19'))
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
function warnText(value: string) {
|
|
59
|
+
return `Invalid space value: ${value} — Must be a whole or half number between 1–7.`
|
|
60
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useTheme } from '../../hooks'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import {
|
|
4
|
+
Platform,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
Text as ReactNativeText,
|
|
7
|
+
TextProps as ReactNativeTextProps,
|
|
8
|
+
} from 'react-native'
|
|
9
|
+
import { tokens } from '../../vendor/tapestry/tokens'
|
|
10
|
+
import { platformFontWeightBold } from '../../utils/platform_styles'
|
|
11
|
+
|
|
12
|
+
interface TextProps extends ReactNativeTextProps {
|
|
13
|
+
variant?: 'h1' | 'h2' | 'h3' | 'h4'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function Heading({ style, variant = 'h1', children, ...rest }: TextProps) {
|
|
17
|
+
const styles = useStyles()
|
|
18
|
+
const variantStyleMap = {
|
|
19
|
+
h1: styles.heading1,
|
|
20
|
+
h2: styles.heading2,
|
|
21
|
+
h3: styles.heading3,
|
|
22
|
+
h4: styles.heading4,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<ReactNativeText
|
|
27
|
+
style={[styles.global, variantStyleMap[variant], style]}
|
|
28
|
+
accessibilityRole="header"
|
|
29
|
+
{...rest}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</ReactNativeText>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const useStyles = () => {
|
|
37
|
+
const { colors } = useTheme()
|
|
38
|
+
|
|
39
|
+
return StyleSheet.create({
|
|
40
|
+
global: {
|
|
41
|
+
fontFamily: Platform.select({
|
|
42
|
+
ios: 'System',
|
|
43
|
+
android: 'normal',
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
heading1: {
|
|
47
|
+
color: colors.textColorDefaultHeadline,
|
|
48
|
+
fontSize: 25, // Todo: Check with UX on correct token
|
|
49
|
+
lineHeight: 32,
|
|
50
|
+
},
|
|
51
|
+
heading2: {
|
|
52
|
+
color: colors.textColorDefaultHeadline,
|
|
53
|
+
fontWeight: platformFontWeightBold,
|
|
54
|
+
fontSize: 21, // Todo: Check with UX on correct token
|
|
55
|
+
lineHeight: 24,
|
|
56
|
+
},
|
|
57
|
+
heading3: {
|
|
58
|
+
color: colors.textColorDefaultHeadline,
|
|
59
|
+
fontWeight: platformFontWeightBold,
|
|
60
|
+
fontSize: tokens.fontSizeLg,
|
|
61
|
+
lineHeight: 22,
|
|
62
|
+
},
|
|
63
|
+
heading4: {
|
|
64
|
+
color: colors.textColorDefaultSecondary,
|
|
65
|
+
fontWeight: platformFontWeightBold,
|
|
66
|
+
fontSize: tokens.fontSizeSm,
|
|
67
|
+
lineHeight: 20,
|
|
68
|
+
textTransform: 'uppercase',
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
Text as ReactNativeText,
|
|
7
7
|
TextProps as ReactNativeTextProps,
|
|
8
8
|
} from 'react-native'
|
|
9
|
+
import { tokens } from '../../vendor/tapestry/tokens'
|
|
9
10
|
|
|
10
11
|
interface TextProps extends ReactNativeTextProps {
|
|
11
12
|
variant?: 'plain' | 'secondary' | 'tertiary' | 'footnote'
|
|
@@ -38,22 +39,22 @@ const useStyles = () => {
|
|
|
38
39
|
},
|
|
39
40
|
plain: {
|
|
40
41
|
color: colors.textColorDefaultPrimary,
|
|
41
|
-
fontSize:
|
|
42
|
+
fontSize: tokens.fontSizeMd,
|
|
42
43
|
lineHeight: 24,
|
|
43
44
|
},
|
|
44
45
|
secondary: {
|
|
45
46
|
color: colors.textColorDefaultPrimary,
|
|
46
|
-
fontSize: 15,
|
|
47
|
+
fontSize: 15, // TODO: Check with UX on correct token
|
|
47
48
|
lineHeight: 22,
|
|
48
49
|
},
|
|
49
50
|
tertiary: {
|
|
50
51
|
color: colors.textColorDefaultPrimary,
|
|
51
|
-
fontSize:
|
|
52
|
+
fontSize: tokens.fontSizeSm,
|
|
52
53
|
lineHeight: 20,
|
|
53
54
|
},
|
|
54
55
|
footnote: {
|
|
55
56
|
color: colors.textColorDefaultSecondary,
|
|
56
|
-
fontSize: 13,
|
|
57
|
+
fontSize: 13, // TODO: Check with UX on correct token
|
|
57
58
|
lineHeight: 16,
|
|
58
59
|
},
|
|
59
60
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { JSONAPIResponse } from '@planningcenter/chat-core'
|
|
1
2
|
import { QueryClient, QueryClientProvider, QueryKey } from '@tanstack/react-query'
|
|
2
|
-
import React from 'react'
|
|
3
|
+
import React, { useEffect } from 'react'
|
|
3
4
|
import { ViewProps } from 'react-native'
|
|
4
5
|
import { OAuthToken } from '../types'
|
|
6
|
+
import apiRequest from '../utils/api'
|
|
5
7
|
import { ENV, session } from '../utils/session'
|
|
6
8
|
|
|
7
9
|
let handleTokenExpired: () => void
|
|
@@ -13,13 +15,8 @@ const defaultQueryFn = ({ queryKey }: { queryKey: QueryKey }) => {
|
|
|
13
15
|
|
|
14
16
|
const url = `${session.baseUrl}${queryKey[0]}`
|
|
15
17
|
|
|
16
|
-
return
|
|
17
|
-
|
|
18
|
-
Authorization: `Bearer ${session.token?.access_token}`,
|
|
19
|
-
},
|
|
20
|
-
})
|
|
21
|
-
.then(validateResponse)
|
|
22
|
-
.then(response => response.json())
|
|
18
|
+
return apiRequest<JSONAPIResponse>(url)
|
|
19
|
+
.then(r => r.json)
|
|
23
20
|
.catch(error => {
|
|
24
21
|
if (error.message === 'Token expired') {
|
|
25
22
|
handleTokenExpired()
|
|
@@ -46,15 +43,9 @@ export function ApiProvider({
|
|
|
46
43
|
session.token = token
|
|
47
44
|
handleTokenExpired = onTokenExpired
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const validateResponse = (response: Response) => {
|
|
53
|
-
const isExpired = response.status === 401
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
queryClient.invalidateQueries()
|
|
48
|
+
}, [env, token])
|
|
54
49
|
|
|
55
|
-
|
|
56
|
-
throw new Error('Token expired')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return response
|
|
50
|
+
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
60
51
|
}
|
package/src/hooks/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './use_async_storage'
|
|
2
|
+
export * from './use_theme'
|
package/src/index.tsx
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { baseUrlMap, uploadUrlMap } from './utils/session'
|
|
5
|
-
import { TemporaryDefaultColorsType } from './utils/theme'
|
|
1
|
+
export * from './components'
|
|
2
|
+
export * from './contexts'
|
|
3
|
+
export * from './hooks'
|
|
6
4
|
export * from './screens'
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
baseUrlMap,
|
|
10
|
-
ChatContext,
|
|
11
|
-
ChatProvider,
|
|
12
|
-
Conversations,
|
|
13
|
-
OAuthToken,
|
|
14
|
-
TemporaryDefaultColorsType,
|
|
15
|
-
uploadUrlMap,
|
|
16
|
-
}
|
|
5
|
+
export * from './utils'
|
package/src/screens/display.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { ScrollView, StyleSheet, View } from 'react-native'
|
|
3
3
|
import { useTheme } from '../hooks'
|
|
4
|
-
import { Image, Spinner, Text } from '../components/display'
|
|
4
|
+
import { Heading, Image, Spinner, Text } from '../components/display'
|
|
5
|
+
import { space } from '../utils/space'
|
|
5
6
|
|
|
6
7
|
export function DisplayScreen() {
|
|
7
8
|
const styles = useStyles()
|
|
@@ -27,6 +28,12 @@ export function DisplayScreen() {
|
|
|
27
28
|
<Text variant="tertiary">Tertiary</Text>
|
|
28
29
|
<Text variant="footnote">Footnote</Text>
|
|
29
30
|
</View>
|
|
31
|
+
<View style={styles.row}>
|
|
32
|
+
<Heading>Heading 1</Heading>
|
|
33
|
+
<Heading variant="h2">Heading 2</Heading>
|
|
34
|
+
<Heading variant="h3">Heading 3</Heading>
|
|
35
|
+
<Heading variant="h4">Heading 4</Heading>
|
|
36
|
+
</View>
|
|
30
37
|
</View>
|
|
31
38
|
</ScrollView>
|
|
32
39
|
)
|
|
@@ -37,18 +44,18 @@ const useStyles = () => {
|
|
|
37
44
|
|
|
38
45
|
return StyleSheet.create({
|
|
39
46
|
scrollView: { flex: 1, backgroundColor: colors.fillColorNeutral090 },
|
|
40
|
-
container: { gap:
|
|
47
|
+
container: { gap: space(2), padding: space(3) },
|
|
41
48
|
listItem: { color: colors.fillColorNeutral020 },
|
|
42
49
|
row: {
|
|
43
|
-
gap:
|
|
50
|
+
gap: space(2),
|
|
44
51
|
flexDirection: 'row',
|
|
45
52
|
alignItems: 'center',
|
|
46
53
|
justifyContent: 'center',
|
|
47
54
|
flexWrap: 'wrap',
|
|
48
55
|
},
|
|
49
|
-
column: { gap:
|
|
56
|
+
column: { gap: space(4) },
|
|
50
57
|
spinnerContainer: {
|
|
51
|
-
height:
|
|
58
|
+
height: space(2.5),
|
|
52
59
|
},
|
|
53
60
|
image: {
|
|
54
61
|
width: 100,
|
package/src/utils/api.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import DeviceInfo from 'react-native-device-info'
|
|
2
|
+
import { session } from './session'
|
|
3
|
+
|
|
4
|
+
const brand = DeviceInfo.getBrand()
|
|
5
|
+
const model = DeviceInfo.getModel()
|
|
6
|
+
const systemName = DeviceInfo.getSystemName()
|
|
7
|
+
const systemVersion = DeviceInfo.getSystemVersion()
|
|
8
|
+
const readableVersion = DeviceInfo.getReadableVersion()
|
|
9
|
+
const appName = DeviceInfo.getApplicationName()
|
|
10
|
+
|
|
11
|
+
export default function apiRequest<T = unknown>(
|
|
12
|
+
url: string,
|
|
13
|
+
{ method = 'GET', data = null } = {}
|
|
14
|
+
): Promise<{ json: T; ok: boolean; response: Response }> {
|
|
15
|
+
const options: RequestInit = {
|
|
16
|
+
headers: {
|
|
17
|
+
Accept: 'application/vnd.api+json',
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
'User-Agent': `${appName}/${readableVersion} (${brand}, ${model}, ${systemName}, ${systemVersion})`,
|
|
20
|
+
Authorization: `Bearer ${session.token?.access_token}`,
|
|
21
|
+
},
|
|
22
|
+
method,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (data && method !== 'GET') {
|
|
26
|
+
options.body = JSON.stringify(data)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return fetch(url, options)
|
|
30
|
+
.then(validateResponse)
|
|
31
|
+
.then(response =>
|
|
32
|
+
response
|
|
33
|
+
.json()
|
|
34
|
+
.then(json => ({ json: json as T, ok: response.ok, response }))
|
|
35
|
+
.catch(() => ({ json: null as T, ok: response.ok, response }))
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const validateResponse = (response: Response) => {
|
|
40
|
+
const isExpired = response.status === 401
|
|
41
|
+
|
|
42
|
+
if (isExpired) {
|
|
43
|
+
throw new Error('Token expired')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return response
|
|
47
|
+
}
|
package/src/utils/session.ts
CHANGED
|
@@ -42,6 +42,19 @@ export class Session {
|
|
|
42
42
|
get uploadUrl() {
|
|
43
43
|
return uploadUrlMap[this.env]
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
toString() {
|
|
47
|
+
return JSON.stringify({ env: this.env, token: this.token })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static hydrate(sessionString: string) {
|
|
51
|
+
try {
|
|
52
|
+
const props = JSON.parse(sessionString)
|
|
53
|
+
return new Session(props)
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return new Session()
|
|
56
|
+
}
|
|
57
|
+
}
|
|
45
58
|
}
|
|
46
59
|
|
|
47
60
|
export const session = new Session({ env: 'development' })
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { tokens } from '../vendor/tapestry/tokens'
|
|
2
|
+
|
|
3
|
+
export type SpacingValues =
|
|
4
|
+
| 0.25
|
|
5
|
+
| 0.5
|
|
6
|
+
| 1
|
|
7
|
+
| 1.5
|
|
8
|
+
| 2
|
|
9
|
+
| 2.5
|
|
10
|
+
| 3
|
|
11
|
+
| 3.5
|
|
12
|
+
| 4
|
|
13
|
+
| 4.5
|
|
14
|
+
| 5
|
|
15
|
+
| 5.5
|
|
16
|
+
| 6
|
|
17
|
+
| 6.5
|
|
18
|
+
| 7
|
|
19
|
+
| 7.5
|
|
20
|
+
|
|
21
|
+
export function space(value: SpacingValues): number {
|
|
22
|
+
if (value === 0.25) return tokens.spacingFourth
|
|
23
|
+
if (value === 0.5) return tokens.spacingHalf
|
|
24
|
+
if (value < 1 || value > 7.5) return handleInvalidSpace(value)
|
|
25
|
+
|
|
26
|
+
// Reject fractional values that are not 0 or 0.5
|
|
27
|
+
const wholeValue = Math.floor(value)
|
|
28
|
+
const fractionalValue = value % 1
|
|
29
|
+
if (fractionalValue !== 0 && fractionalValue !== 0.5) return handleInvalidSpace(value)
|
|
30
|
+
|
|
31
|
+
// Deliver a whole value or add a half spacing token to it
|
|
32
|
+
const remainderValue = fractionalValue === 0.5 ? tokens.spacingHalf : 0
|
|
33
|
+
return tokens[`spacing${wholeValue}`] + remainderValue
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function handleInvalidSpace(value: number) {
|
|
37
|
+
console.warn(`Invalid space value: ${value} — Must be a whole or half number between 1–7.`)
|
|
38
|
+
return 0
|
|
39
|
+
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Defining these tokens locally is a temporary solution until the package supports mobile.
|
|
3
3
|
// Tokens Reference: https://planningcenter.github.io/tapestry/?path=/docs/foundations-design-tokens--docs
|
|
4
4
|
|
|
5
|
+
import { TextStyle } from 'react-native'
|
|
6
|
+
|
|
5
7
|
interface ColorPrimitves {
|
|
6
8
|
colorNeutral7: string
|
|
7
9
|
colorNeutral12: string
|
|
@@ -61,6 +63,19 @@ interface NumericPrimitives {
|
|
|
61
63
|
borderRadiusRound: number
|
|
62
64
|
borderSizeDefault: number
|
|
63
65
|
borderSizeThick: number
|
|
66
|
+
fontWeightNormal: TextStyle['fontWeight']
|
|
67
|
+
fontWeightMedium: TextStyle['fontWeight']
|
|
68
|
+
fontWeightSemiBold: TextStyle['fontWeight']
|
|
69
|
+
fontWeightBold: TextStyle['fontWeight']
|
|
70
|
+
fontSize4xl: number
|
|
71
|
+
fontSize3xl: number
|
|
72
|
+
fontSize2xl: number
|
|
73
|
+
fontSizeXl: number
|
|
74
|
+
fontSizeLg: number
|
|
75
|
+
fontSizeMd: number
|
|
76
|
+
fontSizeSm: number
|
|
77
|
+
fontSizeXs: number
|
|
78
|
+
fontSize2xs: number
|
|
64
79
|
}
|
|
65
80
|
|
|
66
81
|
const numericPrimtives: NumericPrimitives = {
|
|
@@ -80,6 +95,19 @@ const numericPrimtives: NumericPrimitives = {
|
|
|
80
95
|
borderRadiusRound: 56,
|
|
81
96
|
borderSizeDefault: 1,
|
|
82
97
|
borderSizeThick: 2,
|
|
98
|
+
fontWeightNormal: '400',
|
|
99
|
+
fontWeightMedium: '500',
|
|
100
|
+
fontWeightSemiBold: '600',
|
|
101
|
+
fontWeightBold: '700',
|
|
102
|
+
fontSize4xl: 32,
|
|
103
|
+
fontSize3xl: 28,
|
|
104
|
+
fontSize2xl: 24,
|
|
105
|
+
fontSizeXl: 20,
|
|
106
|
+
fontSizeLg: 18,
|
|
107
|
+
fontSizeMd: 16,
|
|
108
|
+
fontSizeSm: 14,
|
|
109
|
+
fontSizeXs: 12,
|
|
110
|
+
fontSize2xs: 10,
|
|
83
111
|
}
|
|
84
112
|
|
|
85
113
|
interface NumericAliases {
|