@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.
Files changed (75) hide show
  1. package/build/components/display/heading.d.ts +8 -0
  2. package/build/components/display/heading.d.ts.map +1 -0
  3. package/build/components/display/heading.js +53 -0
  4. package/build/components/display/heading.js.map +1 -0
  5. package/build/components/display/index.d.ts +1 -0
  6. package/build/components/display/index.d.ts.map +1 -1
  7. package/build/components/display/index.js +1 -0
  8. package/build/components/display/index.js.map +1 -1
  9. package/build/components/display/text.d.ts.map +1 -1
  10. package/build/components/display/text.js +5 -4
  11. package/build/components/display/text.js.map +1 -1
  12. package/build/components/index.d.ts +3 -0
  13. package/build/components/index.d.ts.map +1 -0
  14. package/build/components/index.js +3 -0
  15. package/build/components/index.js.map +1 -0
  16. package/build/contexts/api_provider.d.ts.map +1 -1
  17. package/build/contexts/api_provider.js +7 -15
  18. package/build/contexts/api_provider.js.map +1 -1
  19. package/build/contexts/index.d.ts +3 -0
  20. package/build/contexts/index.d.ts.map +1 -0
  21. package/build/contexts/index.js +3 -0
  22. package/build/contexts/index.js.map +1 -0
  23. package/build/hooks/index.d.ts +2 -1
  24. package/build/hooks/index.d.ts.map +1 -1
  25. package/build/hooks/index.js +2 -1
  26. package/build/hooks/index.js.map +1 -1
  27. package/build/index.d.ts +4 -6
  28. package/build/index.d.ts.map +1 -1
  29. package/build/index.js +4 -4
  30. package/build/index.js.map +1 -1
  31. package/build/screens/display.d.ts.map +1 -1
  32. package/build/screens/display.js +12 -5
  33. package/build/screens/display.js.map +1 -1
  34. package/build/utils/api.d.ts +9 -0
  35. package/build/utils/api.d.ts.map +1 -0
  36. package/build/utils/api.js +36 -0
  37. package/build/utils/api.js.map +1 -0
  38. package/build/utils/index.d.ts +3 -0
  39. package/build/utils/index.d.ts.map +1 -0
  40. package/build/utils/index.js +3 -0
  41. package/build/utils/index.js.map +1 -0
  42. package/build/utils/platform_styles.d.ts +2 -0
  43. package/build/utils/platform_styles.d.ts.map +1 -0
  44. package/build/utils/platform_styles.js +7 -0
  45. package/build/utils/platform_styles.js.map +1 -0
  46. package/build/utils/session.d.ts +2 -0
  47. package/build/utils/session.d.ts.map +1 -1
  48. package/build/utils/session.js +12 -0
  49. package/build/utils/session.js.map +1 -1
  50. package/build/utils/space.d.ts +3 -0
  51. package/build/utils/space.d.ts.map +1 -0
  52. package/build/utils/space.js +22 -0
  53. package/build/utils/space.js.map +1 -0
  54. package/build/vendor/tapestry/tokens.d.ts +14 -0
  55. package/build/vendor/tapestry/tokens.d.ts.map +1 -1
  56. package/build/vendor/tapestry/tokens.js +13 -0
  57. package/build/vendor/tapestry/tokens.js.map +1 -1
  58. package/package.json +7 -5
  59. package/src/__tests__/session.tsx +43 -8
  60. package/src/__tests__/utils/space.tsx +60 -0
  61. package/src/components/display/heading.tsx +71 -0
  62. package/src/components/display/index.ts +1 -0
  63. package/src/components/display/text.tsx +5 -4
  64. package/src/components/index.tsx +2 -0
  65. package/src/contexts/api_provider.tsx +9 -18
  66. package/src/contexts/index.ts +2 -0
  67. package/src/hooks/index.ts +2 -1
  68. package/src/index.tsx +4 -15
  69. package/src/screens/display.tsx +12 -5
  70. package/src/utils/api.ts +47 -0
  71. package/src/utils/index.ts +2 -0
  72. package/src/utils/platform_styles.ts +7 -0
  73. package/src/utils/session.ts +13 -0
  74. package/src/utils/space.ts +39 -0
  75. 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":"AA4FA,eAAO,MAAM,MAAM;yBAPI,MAAM;mBAtCZ,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;mBAzDR,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;CA0E7B,CAAA"}
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;AAuB1G,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;AAqBD,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;CACnB,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\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}\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}\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"]}
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.1",
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": "469ceadce649e0dfa29e5c61281d1ed4821786cd"
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
+ }
@@ -1,3 +1,4 @@
1
1
  export { Spinner } from './spinner'
2
2
  export { Image } from './image'
3
+ export { Heading } from './heading'
3
4
  export { Text } from './text'
@@ -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: 16,
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: 14,
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
  })
@@ -0,0 +1,2 @@
1
+ export * from './conversations'
2
+ export * from './error_boundary'
@@ -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 fetch(url, {
17
- headers: {
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
- return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
50
- }
51
-
52
- const validateResponse = (response: Response) => {
53
- const isExpired = response.status === 401
46
+ useEffect(() => {
47
+ queryClient.invalidateQueries()
48
+ }, [env, token])
54
49
 
55
- if (isExpired) {
56
- throw new Error('Token expired')
57
- }
58
-
59
- return response
50
+ return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
60
51
  }
@@ -0,0 +1,2 @@
1
+ export * from './api_provider'
2
+ export * from './chat_context'
@@ -1 +1,2 @@
1
- export { useTheme } from './use_theme'
1
+ export * from './use_async_storage'
2
+ export * from './use_theme'
package/src/index.tsx CHANGED
@@ -1,16 +1,5 @@
1
- import { Conversations } from './components/conversations'
2
- import { ChatContext, ChatProvider } from './contexts/chat_context'
3
- import { OAuthToken } from './types'
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'
@@ -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: 16, padding: 24 },
47
+ container: { gap: space(2), padding: space(3) },
41
48
  listItem: { color: colors.fillColorNeutral020 },
42
49
  row: {
43
- gap: 16,
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: 32 },
56
+ column: { gap: space(4) },
50
57
  spinnerContainer: {
51
- height: 20,
58
+ height: space(2.5),
52
59
  },
53
60
  image: {
54
61
  width: 100,
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ export * from './session'
2
+ export * from './theme'
@@ -0,0 +1,7 @@
1
+ import { Platform } from 'react-native'
2
+ import { tokens } from '../vendor/tapestry/tokens'
3
+
4
+ export const platformFontWeightBold = Platform.select({
5
+ ios: tokens.fontWeightSemiBold,
6
+ android: tokens.fontWeightBold,
7
+ })
@@ -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 {