@imposium-hub/components 2.5.18 → 2.6.0-1

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 (185) hide show
  1. package/dist/cjs/Util.js +1 -2
  2. package/dist/cjs/Util.js.map +1 -1
  3. package/dist/cjs/components/app-wrapper/AppWrapper.d.ts +32 -21
  4. package/dist/cjs/components/app-wrapper/AppWrapper.js +244 -208
  5. package/dist/cjs/components/app-wrapper/AppWrapper.js.map +1 -1
  6. package/dist/cjs/components/app-wrapper/AppWrapperV2.d.ts +3 -9
  7. package/dist/cjs/components/app-wrapper/AppWrapperV2.js +119 -45
  8. package/dist/cjs/components/app-wrapper/AppWrapperV2.js.map +1 -1
  9. package/dist/cjs/components/app-wrapper/AppWrapperV3.d.ts +1 -1
  10. package/dist/cjs/components/app-wrapper/AppWrapperV3.js +7 -44
  11. package/dist/cjs/components/app-wrapper/AppWrapperV3.js.map +1 -1
  12. package/dist/cjs/components/assets/StoryTableNameFilter.d.ts +9 -2
  13. package/dist/cjs/components/assets/StoryTableNameFilter.js +3 -13
  14. package/dist/cjs/components/assets/StoryTableNameFilter.js.map +1 -1
  15. package/dist/cjs/components/compositions/TextLayer.js +0 -1
  16. package/dist/cjs/components/compositions/TextLayer.js.map +1 -1
  17. package/dist/cjs/components/context-menu/AnimateComponent.d.ts +6 -6
  18. package/dist/cjs/components/context-menu/AnimateComponent.js.map +1 -1
  19. package/dist/cjs/components/context-menu/ContextMenu.d.ts +12 -27
  20. package/dist/cjs/components/context-menu/ContextMenu.js +2 -15
  21. package/dist/cjs/components/context-menu/ContextMenu.js.map +1 -1
  22. package/dist/cjs/components/context-menu/ContextMenuItem.d.ts +6 -18
  23. package/dist/cjs/components/context-menu/ContextMenuItem.js +2 -21
  24. package/dist/cjs/components/context-menu/ContextMenuItem.js.map +1 -1
  25. package/dist/cjs/components/context-menu/ContextMenuTrigger.d.ts +6 -16
  26. package/dist/cjs/components/context-menu/ContextMenuTrigger.js +2 -20
  27. package/dist/cjs/components/context-menu/ContextMenuTrigger.js.map +1 -1
  28. package/dist/cjs/components/context-menu/SubMenu.d.ts +4 -12
  29. package/dist/cjs/components/context-menu/SubMenu.js +2 -17
  30. package/dist/cjs/components/context-menu/SubMenu.js.map +1 -1
  31. package/dist/cjs/components/data-table/Paginator.d.ts +16 -1
  32. package/dist/cjs/components/data-table/Paginator.js +1 -1
  33. package/dist/cjs/components/data-table/Paginator.js.map +1 -1
  34. package/dist/cjs/components/font-picker/font-manager/constants.d.ts +4 -0
  35. package/dist/cjs/components/font-picker/font-manager/constants.js +5 -1
  36. package/dist/cjs/components/font-picker/font-manager/constants.js.map +1 -1
  37. package/dist/cjs/components/header/Header.d.ts +8 -9
  38. package/dist/cjs/components/header/Header.js +95 -160
  39. package/dist/cjs/components/header/Header.js.map +1 -1
  40. package/dist/cjs/components/header/ProjectDropdown.d.ts +13 -0
  41. package/dist/cjs/components/header/ProjectDropdown.js +128 -0
  42. package/dist/cjs/components/header/ProjectDropdown.js.map +1 -0
  43. package/dist/cjs/components/header/StoryDropdown.d.ts +6 -0
  44. package/dist/cjs/components/header/StoryDropdown.js +111 -0
  45. package/dist/cjs/components/header/StoryDropdown.js.map +1 -0
  46. package/dist/cjs/components/modal/Modal.d.ts +1 -1
  47. package/dist/cjs/components/modal/Modal.js +2 -3
  48. package/dist/cjs/components/modal/Modal.js.map +1 -1
  49. package/dist/cjs/components/portal/Portal.d.ts +0 -2
  50. package/dist/cjs/components/portal/Portal.js.map +1 -1
  51. package/dist/cjs/components/publish-wizard/PublishWizard.d.ts +1 -3
  52. package/dist/cjs/components/publish-wizard/PublishWizard.js.map +1 -1
  53. package/dist/cjs/components/publish-wizard/publish/EmailWorkflow.js.map +1 -1
  54. package/dist/cjs/constants/copy.d.ts +3 -0
  55. package/dist/cjs/constants/copy.js +3 -0
  56. package/dist/cjs/constants/copy.js.map +1 -1
  57. package/dist/cjs/index.d.ts +4 -7
  58. package/dist/cjs/index.js +11 -17
  59. package/dist/cjs/index.js.map +1 -1
  60. package/dist/cjs/redux/actions/asset-uploads.js +0 -1
  61. package/dist/cjs/redux/actions/asset-uploads.js.map +1 -1
  62. package/dist/cjs/services/API.d.ts +10 -3
  63. package/dist/cjs/services/API.js +230 -84
  64. package/dist/cjs/services/API.js.map +1 -1
  65. package/dist/cjs/services/Session.d.ts +0 -11
  66. package/dist/cjs/services/Session.js +3 -126
  67. package/dist/cjs/services/Session.js.map +1 -1
  68. package/dist/cjs/utils/modal.d.ts +2 -0
  69. package/dist/cjs/utils/modal.js +11 -0
  70. package/dist/cjs/utils/modal.js.map +1 -0
  71. package/dist/cjs/utils/pendo.d.ts +24 -0
  72. package/dist/cjs/utils/pendo.js +62 -0
  73. package/dist/cjs/utils/pendo.js.map +1 -0
  74. package/dist/cjs/utils/routing.d.ts +2 -0
  75. package/dist/cjs/utils/routing.js +10 -1
  76. package/dist/cjs/utils/routing.js.map +1 -1
  77. package/dist/esm/Util.js +1 -2
  78. package/dist/esm/Util.js.map +1 -1
  79. package/dist/esm/components/app-wrapper/AppWrapper.d.ts +32 -21
  80. package/dist/esm/components/app-wrapper/AppWrapper.js +143 -172
  81. package/dist/esm/components/app-wrapper/AppWrapper.js.map +1 -1
  82. package/dist/esm/components/app-wrapper/AppWrapperV2.d.ts +3 -9
  83. package/dist/esm/components/app-wrapper/AppWrapperV2.js +30 -33
  84. package/dist/esm/components/app-wrapper/AppWrapperV2.js.map +1 -1
  85. package/dist/esm/components/app-wrapper/AppWrapperV3.d.ts +1 -1
  86. package/dist/esm/components/app-wrapper/AppWrapperV3.js +5 -42
  87. package/dist/esm/components/app-wrapper/AppWrapperV3.js.map +1 -1
  88. package/dist/esm/components/assets/StoryTableNameFilter.d.ts +9 -2
  89. package/dist/esm/components/assets/StoryTableNameFilter.js +3 -13
  90. package/dist/esm/components/assets/StoryTableNameFilter.js.map +1 -1
  91. package/dist/esm/components/compositions/TextLayer.js +0 -1
  92. package/dist/esm/components/compositions/TextLayer.js.map +1 -1
  93. package/dist/esm/components/context-menu/AnimateComponent.d.ts +6 -6
  94. package/dist/esm/components/context-menu/AnimateComponent.js.map +1 -1
  95. package/dist/esm/components/context-menu/ContextMenu.d.ts +12 -27
  96. package/dist/esm/components/context-menu/ContextMenu.js +2 -4
  97. package/dist/esm/components/context-menu/ContextMenu.js.map +1 -1
  98. package/dist/esm/components/context-menu/ContextMenuItem.d.ts +6 -18
  99. package/dist/esm/components/context-menu/ContextMenuItem.js +2 -10
  100. package/dist/esm/components/context-menu/ContextMenuItem.js.map +1 -1
  101. package/dist/esm/components/context-menu/ContextMenuTrigger.d.ts +6 -16
  102. package/dist/esm/components/context-menu/ContextMenuTrigger.js +2 -9
  103. package/dist/esm/components/context-menu/ContextMenuTrigger.js.map +1 -1
  104. package/dist/esm/components/context-menu/SubMenu.d.ts +4 -12
  105. package/dist/esm/components/context-menu/SubMenu.js +2 -6
  106. package/dist/esm/components/context-menu/SubMenu.js.map +1 -1
  107. package/dist/esm/components/data-table/Paginator.d.ts +16 -1
  108. package/dist/esm/components/data-table/Paginator.js +1 -1
  109. package/dist/esm/components/data-table/Paginator.js.map +1 -1
  110. package/dist/esm/components/font-picker/font-manager/constants.d.ts +4 -0
  111. package/dist/esm/components/font-picker/font-manager/constants.js +4 -0
  112. package/dist/esm/components/font-picker/font-manager/constants.js.map +1 -1
  113. package/dist/esm/components/header/Header.d.ts +8 -9
  114. package/dist/esm/components/header/Header.js +93 -147
  115. package/dist/esm/components/header/Header.js.map +1 -1
  116. package/dist/esm/components/header/ProjectDropdown.d.ts +13 -0
  117. package/dist/esm/components/header/ProjectDropdown.js +101 -0
  118. package/dist/esm/components/header/ProjectDropdown.js.map +1 -0
  119. package/dist/esm/components/header/StoryDropdown.d.ts +6 -0
  120. package/dist/esm/components/header/StoryDropdown.js +82 -0
  121. package/dist/esm/components/header/StoryDropdown.js.map +1 -0
  122. package/dist/esm/components/modal/Modal.d.ts +1 -1
  123. package/dist/esm/components/modal/Modal.js +2 -3
  124. package/dist/esm/components/modal/Modal.js.map +1 -1
  125. package/dist/esm/components/portal/Portal.d.ts +0 -2
  126. package/dist/esm/components/portal/Portal.js.map +1 -1
  127. package/dist/esm/components/publish-wizard/PublishWizard.d.ts +1 -3
  128. package/dist/esm/components/publish-wizard/PublishWizard.js.map +1 -1
  129. package/dist/esm/components/publish-wizard/publish/EmailWorkflow.js.map +1 -1
  130. package/dist/esm/constants/copy.d.ts +3 -0
  131. package/dist/esm/constants/copy.js +3 -0
  132. package/dist/esm/constants/copy.js.map +1 -1
  133. package/dist/esm/index.d.ts +4 -7
  134. package/dist/esm/index.js +4 -7
  135. package/dist/esm/index.js.map +1 -1
  136. package/dist/esm/redux/actions/asset-uploads.js +0 -1
  137. package/dist/esm/redux/actions/asset-uploads.js.map +1 -1
  138. package/dist/esm/services/API.d.ts +10 -3
  139. package/dist/esm/services/API.js +74 -27
  140. package/dist/esm/services/API.js.map +1 -1
  141. package/dist/esm/services/Session.d.ts +0 -11
  142. package/dist/esm/services/Session.js +3 -84
  143. package/dist/esm/services/Session.js.map +1 -1
  144. package/dist/esm/utils/modal.d.ts +2 -0
  145. package/dist/esm/utils/modal.js +7 -0
  146. package/dist/esm/utils/modal.js.map +1 -0
  147. package/dist/esm/utils/pendo.d.ts +24 -0
  148. package/dist/esm/utils/pendo.js +52 -0
  149. package/dist/esm/utils/pendo.js.map +1 -0
  150. package/dist/esm/utils/routing.d.ts +2 -0
  151. package/dist/esm/utils/routing.js +7 -0
  152. package/dist/esm/utils/routing.js.map +1 -1
  153. package/dist/styles.css +0 -5
  154. package/dist/styles.less +0 -1
  155. package/less/components/button.less +0 -1
  156. package/package.json +5 -2
  157. package/src/Util.ts +1 -2
  158. package/src/components/app-wrapper/AppWrapper.tsx +245 -257
  159. package/src/components/assets/StoryTableNameFilter.tsx +6 -22
  160. package/src/components/compositions/TextLayer.tsx +0 -1
  161. package/src/components/context-menu/AnimateComponent.tsx +12 -1
  162. package/src/components/context-menu/ContextMenu.tsx +16 -8
  163. package/src/components/context-menu/ContextMenuItem.tsx +12 -11
  164. package/src/components/context-menu/ContextMenuTrigger.tsx +12 -10
  165. package/src/components/context-menu/SubMenu.tsx +6 -8
  166. package/src/components/data-table/Paginator.tsx +19 -6
  167. package/src/components/font-picker/font-manager/constants.ts +5 -0
  168. package/src/components/header/Header.tsx +130 -242
  169. package/src/components/header/ProjectDropdown.tsx +174 -0
  170. package/src/components/modal/Modal.tsx +7 -10
  171. package/src/components/portal/Portal.tsx +1 -2
  172. package/src/components/publish-wizard/PublishWizard.tsx +1 -1
  173. package/src/components/publish-wizard/publish/EmailWorkflow.tsx +7 -1
  174. package/src/constants/copy.ts +3 -0
  175. package/src/index.ts +6 -14
  176. package/src/redux/actions/asset-uploads.ts +0 -1
  177. package/src/services/API.ts +92 -30
  178. package/src/utils/modal.ts +9 -0
  179. package/src/utils/pendo.ts +61 -0
  180. package/src/utils/routing.ts +10 -0
  181. package/src/components/auth-gate/AuthGate.tsx +0 -84
  182. package/src/redux/actions/auth.ts +0 -30
  183. package/src/redux/reducers/auth.ts +0 -33
  184. package/src/services/Auth0.ts +0 -82
  185. package/src/services/Session.ts +0 -153
package/dist/styles.less CHANGED
@@ -816,7 +816,6 @@ body{
816
816
  .boldButton(@color){
817
817
  background:@color;
818
818
  color:@buttonTextDefault;
819
- border:1px solid @color;
820
819
  &:hover, &.active{
821
820
  background:darken(@color, 10%);
822
821
  }
@@ -53,7 +53,6 @@
53
53
  .boldButton(@color){
54
54
  background:@color;
55
55
  color:@buttonTextDefault;
56
- border:1px solid @color;
57
56
  &:hover, &.active{
58
57
  background:darken(@color, 10%);
59
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imposium-hub/components",
3
- "version": "2.5.18",
3
+ "version": "2.6.0-1",
4
4
  "description": "React & Typescript component / asset library for Imposium front-ends",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -36,7 +36,6 @@
36
36
  "dependencies": {
37
37
  "@imposium-hub/react-color": "^2.19.4",
38
38
  "ansi_up": "^5.1.0",
39
- "auth0-js": "^9.11.3",
40
39
  "cogo-toast": "^4.2.3",
41
40
  "export-to-csv": "^0.2.1",
42
41
  "file-saver": "^2.0.5",
@@ -51,6 +50,7 @@
51
50
  "react-transition-group": "^4.4.5"
52
51
  },
53
52
  "peerDependencies": {
53
+ "@auth0/auth0-react": "^2.3.0",
54
54
  "@fortawesome/fontawesome-svg-core": "^6.1.1",
55
55
  "@fortawesome/free-brands-svg-icons": "^6.1.1",
56
56
  "@fortawesome/free-solid-svg-icons": "^6.1.1",
@@ -71,9 +71,12 @@
71
71
  "react-spinners": "^0.6.1",
72
72
  "react-table": "^7.7.0",
73
73
  "react-tooltip": "^5.27.0",
74
+ "react-router": "^3.2.6",
74
75
  "smpte-timecode": "^1.2.3"
75
76
  },
76
77
  "devDependencies": {
78
+ "react-router": "^3.2.6",
79
+ "@auth0/auth0-react": "^2.3.0",
77
80
  "@fortawesome/fontawesome-svg-core": "^6.1.1",
78
81
  "@fortawesome/free-brands-svg-icons": "^6.1.1",
79
82
  "@fortawesome/free-solid-svg-icons": "^6.1.1",
package/src/Util.ts CHANGED
@@ -264,8 +264,7 @@ export const IMAGE_EXTENSIONS = [
264
264
  'jb2',
265
265
  'jpc',
266
266
  'xbm',
267
- 'wbmp',
268
- 'webp'
267
+ 'wbmp'
269
268
  ];
270
269
 
271
270
  export const isFont = (mimeType, extension) => {
@@ -1,308 +1,296 @@
1
1
  import * as React from 'react';
2
- import NoAccess from '../no-access/NoAccess';
3
2
  import Header from '../header/Header';
4
- import SessionService from '../../services/Session';
5
- import AuthService, { IIdentity } from '../../services/Auth0';
6
- import { connect } from 'react-redux';
7
- import { bindActionCreators } from 'redux';
8
- import { login, clearCachedAuth } from '../../redux/actions/auth';
9
3
  import { validateAccessLevel } from '../../Util';
10
- import { setAccessData } from '../../redux/actions/access';
11
4
  import { ConfirmModal } from '../confirm-modal/ConfirmModal';
5
+ import { IImposiumAPI } from '../../services/API';
6
+ import { useAuth0 } from '@auth0/auth0-react';
7
+ import { replaceRoute } from '../../utils/routing';
8
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
9
+ import { faExclamationTriangle } from '@fortawesome/pro-light-svg-icons';
12
10
 
13
11
  export interface IAppWrapperProps {
14
- children: React.ReactChildren;
15
- auth0Domain: string;
16
- auth0ClientId: string;
17
- organizationId: string;
18
12
  baseUrl: string;
19
- storyId?: string;
13
+ api: IImposiumAPI;
14
+ organizationId: string;
20
15
  serviceId: number;
21
- access: any;
22
- auth: any;
23
- isFreshUser: boolean;
24
- setAccessData(accessData: any): any;
25
- allowUnauthenticatedRender: boolean;
26
- onAuthenticated: (activeOrgId: string, activeStoryId: string) => any;
27
- onAuthenticationFailure: (e: Error) => any;
28
- showRequestAccess?: boolean;
29
- login: (id: IIdentity) => any;
30
- clearCachedAuth: () => any;
31
- }
32
-
33
- export interface IAppWrapperState {
34
- blockRender: boolean;
35
- restrictAccess: boolean;
16
+ unsavedChanges: boolean;
17
+ setAccessData(getAccessData): void;
18
+ onAuthenticated: (orgId: string, storyId?: string) => any;
19
+ allowNoStories?: boolean;
20
+ saveStory?(): void;
21
+ storyId?: string;
22
+ CrMLink?: string;
23
+ hideStoryPicker?: boolean;
24
+ hideDocs?: boolean;
25
+ hideOrgPicker?: boolean;
36
26
  }
37
27
 
38
- class AppWrapper extends React.Component<IAppWrapperProps, IAppWrapperState> {
39
- private static readonly RESTRICTED_REQUEST: JSX.Element = (
40
- <NoAccess
41
- key='restriction-msg'
42
- type='request'
43
- />
44
- );
45
-
46
- private static readonly RESTRICTED_BLOCK: JSX.Element = (
47
- <NoAccess
48
- key='restriction-msg'
49
- type='restrict'
50
- />
51
- );
52
-
53
- constructor(p: IAppWrapperProps) {
54
- super(p);
55
-
56
- this.state = {
57
- blockRender: true,
58
- restrictAccess: false
59
- };
60
- }
61
-
62
- public componentDidMount = (): void => {
63
- const { auth0ClientId, auth0Domain } = this.props;
64
-
65
- AuthService.bindToClient(auth0ClientId, auth0Domain);
66
-
67
- const validAccessLevel = validateAccessLevel(
68
- this.props.organizationId,
69
- this.props.serviceId,
70
- this.props.access
71
- );
72
-
73
- if (!this.state.restrictAccess && !validAccessLevel) {
74
- this.setState({ restrictAccess: true });
75
- }
28
+ export const APP_WRAPPER_ERROR_STATES = {
29
+ UNAUTHORIZED_ORG: 'UNAUTHORIZED_ORG',
30
+ NO_ORGS_FOUND: 'NO_ORGS_FOUND',
31
+ NO_STORIES: 'NO_STORIES',
32
+ UNAUTHORIZED_APP: 'UNAUTHORIZED_APP',
33
+ STORY_NOT_FOUND: 'STORY_NOT_FOUND'
34
+ };
76
35
 
77
- if (this.state.restrictAccess && validAccessLevel) {
78
- this.setState({ restrictAccess: false });
36
+ export const AppWrapper: React.FC<IAppWrapperProps> = (props) => {
37
+ const [blockRender, setBlockRender] = React.useState(true);
38
+ const {
39
+ children,
40
+ organizationId,
41
+ storyId,
42
+ hideStoryPicker,
43
+ hideDocs,
44
+ hideOrgPicker,
45
+ CrMLink,
46
+ baseUrl,
47
+ api,
48
+ serviceId,
49
+ unsavedChanges,
50
+ saveStory,
51
+ allowNoStories
52
+ } = props;
53
+ const { isAuthenticated, isLoading, getAccessTokenSilently, loginWithRedirect, logout, user } =
54
+ useAuth0();
55
+ const [errorState, setErrorState] = React.useState(null);
56
+
57
+ React.useEffect(() => {
58
+ void doCheckSession(true);
59
+ }, [isAuthenticated, isLoading]);
60
+
61
+ React.useEffect(() => {
62
+ void doCheckSession(false);
63
+ }, [organizationId, storyId]);
64
+
65
+ const getCachedStoryAndOrgForService = (freshAccess, sId) => {
66
+ const service = freshAccess.services.find((s) => s.id === sId);
67
+ if (service) {
68
+ return {
69
+ story_id: service.story_id,
70
+ organization_id: service.organization_id
71
+ };
79
72
  }
80
-
81
- // Always check the session
82
- this.doCheckSession(true);
83
73
  };
84
74
 
85
- /*
86
- Check the auth0 session expiry
87
- */
88
- public componentDidUpdate = (prevProps): void => {
89
- const { auth } = this.props;
90
- let validAccessLevel: boolean;
91
-
92
- if (auth) {
93
- const {
94
- idTokenPayload: { exp }
95
- } = auth;
96
- const validSession: boolean = AuthService.checkExpiry(exp);
97
-
98
- if (!validSession) {
99
- this.props.clearCachedAuth();
100
- this.doCheckSession();
101
- }
75
+ const validateStoryExists = (freshAccess, oId, sId) => {
76
+ const org = freshAccess.organizations.find((o) => o.id === oId);
77
+ if (!org) {
78
+ return false;
102
79
  }
103
80
 
104
- if (
105
- this.props.organizationId !== prevProps.organizationId ||
106
- this.props.access !== prevProps.access
107
- ) {
108
- validAccessLevel = validateAccessLevel(
109
- this.props.organizationId,
110
- this.props.serviceId,
111
- this.props.access
112
- );
113
-
114
- if (!this.state.restrictAccess && !validAccessLevel) {
115
- this.setState({ restrictAccess: true });
116
- }
81
+ const story = org.stories.find((s) => s.id === sId);
117
82
 
118
- if (this.state.restrictAccess && validAccessLevel) {
119
- this.setState({ restrictAccess: false });
120
- }
83
+ if (!story) {
84
+ return false;
121
85
  }
122
- };
123
86
 
124
- /*
125
- Emit creds to React parent
126
- */
127
- private propagateCreds = (freshAccess, freshIdentity): void => {
128
- const { storyId, organizationId, onAuthenticated } = this.props;
129
-
130
- let initialStoryId = storyId;
131
- let initialOrganizationId = organizationId;
87
+ return true;
88
+ };
132
89
 
133
- const firstOrg = freshAccess.organizations[0];
90
+ const propagateCreds = (freshAccess): void => {
91
+ const {
92
+ storyId: initialStoryId,
93
+ organizationId: initialOrganizationId,
94
+ onAuthenticated
95
+ } = props;
96
+
97
+ // If an org and story ID was passed in, we're following a deeplink. Verify we have access to it, and if so, propagate those IDs and set them on the service
98
+ if (initialOrganizationId && initialStoryId) {
99
+ // Validate the user has access to the orgId and storyID passed in, if not, show the "Unauthorized" interface
100
+ const validAccessLevel = validateAccessLevel(
101
+ initialOrganizationId,
102
+ serviceId,
103
+ freshAccess
104
+ );
134
105
 
135
- const getIDsFromSession = () => {
136
- const session = SessionService.getSession();
137
- if (session && session.story_id && session.organization_id) {
138
- const org = freshAccess.organizations.find(
139
- (o: any) => o.id === session.organization_id
106
+ if (validAccessLevel) {
107
+ // Check to see if the story exists, if not, show the error state
108
+ const storyExists = validateStoryExists(
109
+ freshAccess,
110
+ initialOrganizationId,
111
+ initialStoryId
140
112
  );
141
- if (org) {
142
- const story = org.stories.find((s: any) => s.id === session.story_id);
143
- if (story) {
144
- return { orgId: org.id, storyId: story.id };
145
- }
113
+ if (!storyExists) {
114
+ setErrorState(APP_WRAPPER_ERROR_STATES.STORY_NOT_FOUND);
115
+ return;
146
116
  }
117
+
118
+ api.init(baseUrl, getAccessTokenSilently, initialOrganizationId);
119
+ onAuthenticated(initialOrganizationId, initialStoryId);
120
+ setBlockRender(false);
121
+ setErrorState(null);
122
+ } else {
123
+ setErrorState(APP_WRAPPER_ERROR_STATES.UNAUTHORIZED_APP);
124
+ return;
147
125
  }
148
- return null;
149
- };
150
126
 
151
- // If the org ID is not provided, but the storyID is provided, find the org ID if we can, if not, use the first org ID found and we'll show the story not found interface
152
- if (!initialOrganizationId && initialStoryId) {
153
- const org = freshAccess.organizations.find((o: any) =>
154
- o.stories.find((s: any) => s.id === initialStoryId)
127
+ // If no orgID or storyID was passed in from the URL use the cached orgID and story ID on the service
128
+ } else if (!initialOrganizationId && !initialStoryId) {
129
+ const { story_id, organization_id } = getCachedStoryAndOrgForService(
130
+ freshAccess,
131
+ serviceId
155
132
  );
156
- if (org) {
157
- initialOrganizationId = org.id;
158
- } else if (firstOrg) {
159
- initialOrganizationId = firstOrg.id;
133
+
134
+ // No org was found to show the user
135
+ if (!organization_id) {
136
+ setErrorState(APP_WRAPPER_ERROR_STATES.NO_ORGS_FOUND);
137
+ return;
160
138
  }
161
- }
162
139
 
163
- if (!initialStoryId) {
164
- // If storyID is not provided, try to use the one from the previous session
165
- const sessionIds = getIDsFromSession();
166
- if (sessionIds) {
167
- initialOrganizationId = sessionIds.orgId;
168
- initialStoryId = sessionIds.storyId;
140
+ const validAccessLevel = validateAccessLevel(organization_id, serviceId, freshAccess);
141
+
142
+ if (validAccessLevel) {
143
+ api.init(baseUrl, getAccessTokenSilently, organization_id);
144
+ onAuthenticated(organization_id, story_id);
145
+ replaceRoute(`/${organization_id}/${story_id}`);
146
+ setBlockRender(false);
147
+ setErrorState(null);
169
148
  } else {
170
- // If that doesn't work - default to the first org & story
171
- if (firstOrg) {
172
- initialOrganizationId = firstOrg.id;
173
- const story = firstOrg.stories[0];
174
- if (story) {
175
- initialStoryId = story.id;
176
- } else {
177
- initialStoryId = null;
178
- }
149
+ setErrorState(APP_WRAPPER_ERROR_STATES.UNAUTHORIZED_APP);
150
+ }
151
+ } else if (initialOrganizationId) {
152
+ if (allowNoStories) {
153
+ const validAccessLevel = validateAccessLevel(
154
+ initialOrganizationId,
155
+ serviceId,
156
+ freshAccess
157
+ );
158
+ if (validAccessLevel) {
159
+ api.init(baseUrl, getAccessTokenSilently, initialOrganizationId);
160
+ onAuthenticated(initialOrganizationId, null);
161
+ setBlockRender(false);
162
+ setErrorState(null);
179
163
  } else {
180
- initialOrganizationId = null;
181
- initialStoryId = null;
164
+ setErrorState(APP_WRAPPER_ERROR_STATES.UNAUTHORIZED_APP);
182
165
  }
166
+ } else {
167
+ setErrorState(APP_WRAPPER_ERROR_STATES.NO_STORIES);
183
168
  }
184
169
  }
185
-
186
- // If at least one org was found, propagate that
187
- if (initialOrganizationId) {
188
- SessionService.buildFreshSession(freshIdentity, initialOrganizationId, initialStoryId);
189
- onAuthenticated(initialOrganizationId, initialStoryId);
190
-
191
- if (this.state.blockRender) {
192
- this.setState({ blockRender: false });
193
- }
194
-
195
- // If not, show the no-orgs interface
196
- } else {
197
- this.setState({ restrictAccess: true, blockRender: false });
198
- }
199
- };
200
-
201
- /*
202
- Bust cached creds if exist, invoke auth failure handler
203
- */
204
- private handleCheckSessionFailure = (e: Error): void => {
205
- if (this.props.storyId) {
206
- SessionService.cacheStoryId(this.props.storyId);
207
- } else {
208
- SessionService.clearCachedStoryId();
209
- }
210
-
211
- if (this.props.organizationId) {
212
- SessionService.cacheOrgId(this.props.organizationId);
213
- } else {
214
- SessionService.clearCachedOrgId();
215
- }
216
-
217
- this.props.clearCachedAuth();
218
- this.props.onAuthenticationFailure(e);
219
170
  };
220
171
 
221
172
  /*
222
173
  Check auth0 session, pull Imposium access creds on success and initialize app-wrapper
223
174
  */
224
- private doCheckSession = (blockRender: boolean = false): void => {
225
- const { baseUrl } = this.props;
226
-
227
- if (blockRender) {
228
- this.setState({ blockRender: true });
175
+ const doCheckSession = async (doBlockRender: boolean = false) => {
176
+ if (doBlockRender) {
177
+ setBlockRender(true);
229
178
  }
230
- AuthService.checkSession()
231
- .then((freshIdentity: IIdentity) => {
232
- const orgId = this.props.organizationId
233
- ? this.props.organizationId
234
- : SessionService.getCachedOrgId();
235
- SessionService.getAccessData(freshIdentity.accessToken, baseUrl, false, orgId)
236
- .then((freshAccess: any) => {
237
- this.props.setAccessData(freshAccess);
238
- this.props.login(freshIdentity);
239
- this.propagateCreds(freshAccess, freshIdentity);
240
- })
241
- .catch((e: Error) => {
242
- this.setState({ restrictAccess: true, blockRender: false });
243
- });
244
- })
245
- .catch((e: Error) => {
246
- this.handleCheckSessionFailure(e);
247
- });
248
- };
249
-
250
- public render = (): JSX.Element => {
251
- const { blockRender, restrictAccess } = this.state;
252
- const { children, showRequestAccess, allowUnauthenticatedRender } = this.props;
253
179
 
254
- let innerContent: any;
180
+ // If we're authenticated, get the access token, pull the access data, and propagate back to the parent component
181
+ if (isAuthenticated) {
182
+ try {
183
+ await getAccessTokenSilently();
184
+ } catch (e) {
185
+ // Trigger a logout if we can't get the access token
186
+ onLogout();
187
+ }
255
188
 
256
- if (!blockRender && restrictAccess) {
257
- const showNoOrgs =
258
- this.props.access &&
259
- this.props.access.organizations &&
260
- this.props.access.organizations.length === 0;
261
- const errorIndicator = showNoOrgs ? (
262
- <NoAccess
263
- email={this.props.auth.idTokenPayload.userEmail}
264
- key='restriction-msg'
265
- type='no-orgs'
266
- />
267
- ) : showRequestAccess ? (
268
- AppWrapper.RESTRICTED_REQUEST
269
- ) : (
270
- AppWrapper.RESTRICTED_BLOCK
271
- );
272
- innerContent = [
273
- typeof children[0] === 'object' && children[0].type === Header && children[0],
274
- errorIndicator
275
- ];
189
+ const orgId = organizationId ? organizationId : null;
190
+ api.init(baseUrl, getAccessTokenSilently, orgId);
191
+ api.getAccessData(false)
192
+ .then((freshAccess: any) => {
193
+ props.setAccessData(freshAccess);
194
+ propagateCreds(freshAccess);
195
+ })
196
+ .catch((e: Error) => {
197
+ setErrorState(APP_WRAPPER_ERROR_STATES.UNAUTHORIZED_ORG);
198
+ });
199
+
200
+ // If we're not loading, and we're not authenticated, call login, and cache story + org ID from URL if it's there
201
+ } else if (!isLoading && !isAuthenticated) {
202
+ void loginWithRedirect({
203
+ appState: { returnTo: window.location.href }
204
+ });
276
205
  }
206
+ };
277
207
 
278
- if ((!blockRender && !restrictAccess) || allowUnauthenticatedRender) {
279
- innerContent = children;
280
- }
208
+ const onLogout = () => {
209
+ setBlockRender(true);
210
+ void logout({ logoutParams: { returnTo: window.location.origin } });
211
+ };
281
212
 
282
- return (
283
- <div
284
- id='app'
285
- className='app'>
286
- {innerContent}
287
- <ConfirmModal />
288
- </div>
213
+ let innerContent: any;
214
+
215
+ if (errorState) {
216
+ innerContent = (
217
+ <AppWrapperErrors
218
+ error={errorState}
219
+ email={user.userEmail}
220
+ />
289
221
  );
290
- };
291
- }
222
+ } else if (!blockRender) {
223
+ innerContent = children;
224
+ }
292
225
 
293
- const mapDispatchToProps = (dispatch) => {
294
- return bindActionCreators(
295
- {
296
- login,
297
- setAccessData,
298
- clearCachedAuth
299
- },
300
- dispatch
226
+ return (
227
+ <div
228
+ id='app'
229
+ className='app'>
230
+ <Header
231
+ errorState={errorState}
232
+ saveStory={saveStory}
233
+ onLogout={onLogout}
234
+ activeServiceId={serviceId}
235
+ baseUrl={baseUrl}
236
+ api={api}
237
+ unsavedChanges={unsavedChanges}
238
+ activeOrganization={organizationId}
239
+ activeStory={storyId}
240
+ hideStoryPicker={hideStoryPicker}
241
+ hideDocs={hideDocs}
242
+ showFTLogo={false}
243
+ hideOrgPicker={hideOrgPicker}
244
+ CrMLink={CrMLink}
245
+ />
246
+ {innerContent}
247
+ <ConfirmModal />
248
+ </div>
301
249
  );
302
250
  };
303
251
 
304
- const mapStateToProps = (state): any => {
305
- return { auth: state.auth, access: state.access };
252
+ export const ERROR_HEADINGS = {
253
+ [APP_WRAPPER_ERROR_STATES.UNAUTHORIZED_ORG]: 'Organization Not Found',
254
+ [APP_WRAPPER_ERROR_STATES.NO_ORGS_FOUND]: 'No Organizations Found',
255
+ [APP_WRAPPER_ERROR_STATES.UNAUTHORIZED_APP]: 'Service Not Found',
256
+ [APP_WRAPPER_ERROR_STATES.NO_STORIES]: 'No Projects Found',
257
+ [APP_WRAPPER_ERROR_STATES.STORY_NOT_FOUND]: 'Project Not Found'
306
258
  };
307
259
 
308
- export default connect(mapStateToProps, mapDispatchToProps)(AppWrapper);
260
+ export const ERROR_DESCRIPTIONS = {
261
+ [APP_WRAPPER_ERROR_STATES.UNAUTHORIZED_ORG]:
262
+ 'Your user "[email]" does not have access to this organization. Please request access from an admin already on the organization.',
263
+ [APP_WRAPPER_ERROR_STATES.NO_ORGS_FOUND]:
264
+ 'Your user "[email]" has not been added to any organizations. Please request access from an admin.',
265
+ [APP_WRAPPER_ERROR_STATES.UNAUTHORIZED_APP]:
266
+ 'Your user "[email]" does not have access to this serivce. Please request access from an admin.',
267
+ [APP_WRAPPER_ERROR_STATES.NO_STORIES]: 'There are no projects on this organization.',
268
+ [APP_WRAPPER_ERROR_STATES.STORY_NOT_FOUND]:
269
+ 'The Project cound not be found. Please check the URL and try again.'
270
+ };
271
+
272
+ export interface AppWrappeErrorProps {
273
+ error: string;
274
+ email: string;
275
+ onCreateStory?: () => void;
276
+ }
277
+
278
+ export const AppWrapperErrors: React.FC<AppWrappeErrorProps> = ({
279
+ error,
280
+ email,
281
+ onCreateStory
282
+ }) => {
283
+ return (
284
+ <div className='no-access'>
285
+ <FontAwesomeIcon
286
+ icon={faExclamationTriangle}
287
+ size='6x'
288
+ />
289
+
290
+ <div className='no-access-copy'>
291
+ <h1 className='no-access-heading'>{ERROR_HEADINGS[error]}</h1>
292
+ <p>{ERROR_DESCRIPTIONS[error].replace('[email]', email)}</p>
293
+ </div>
294
+ </div>
295
+ );
296
+ };
@@ -1,40 +1,24 @@
1
1
  import * as React from 'react';
2
2
  import TextField from '../text-field/TextField';
3
- import { connect } from 'react-redux';
4
- import { bindActionCreators } from 'redux';
5
- import { updateStoryFilter } from '../../redux/actions/story-filter';
6
3
 
7
4
  interface IStoryTableNameFilterProps {
8
- storyFilter: any;
9
- updateStoryFilter: (filters: any) => any;
5
+ filter: any;
6
+ updateFilter: (filters: any) => any;
10
7
  }
11
8
 
12
9
  class StoryTableNameFilter extends React.PureComponent<IStoryTableNameFilterProps> {
13
10
  public render = (): JSX.Element => {
14
- const { storyFilter } = this.props;
11
+ const { filter } = this.props;
15
12
 
16
13
  return (
17
14
  <TextField
18
15
  className='story-name'
19
16
  focusOnMount
20
- value={storyFilter}
21
- onChange={(n) => this.props.updateStoryFilter({ name: n })}
17
+ value={filter}
18
+ onChange={(n) => this.props.updateFilter({ name: n })}
22
19
  />
23
20
  );
24
21
  };
25
22
  }
26
23
 
27
- const mapDispatchToProps = (dispatch): any => {
28
- return bindActionCreators({ updateStoryFilter }, dispatch);
29
- };
30
-
31
- const mapStateToProps = (state): any => {
32
- return { storyFilter: state.storyFilter.name };
33
- };
34
-
35
- const StoryTableNameFilterMemoized = connect(
36
- mapStateToProps,
37
- mapDispatchToProps
38
- )(React.memo(StoryTableNameFilter));
39
-
40
- export default StoryTableNameFilterMemoized;
24
+ export default StoryTableNameFilter;
@@ -122,7 +122,6 @@ export default class TextLayer extends React.PureComponent<ITextLayerProps, ITex
122
122
  this.fontReady();
123
123
  }
124
124
  } else if (font_type === FONT_TYPES.STANDARD) {
125
- // TODO: check this?
126
125
  this.fontReady();
127
126
  }
128
127
  }