@unisphere/nx 1.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 (102) hide show
  1. package/README.md +32 -0
  2. package/dist/generators/add-application/add-application.d.ts +5 -0
  3. package/dist/generators/add-application/add-application.d.ts.map +1 -0
  4. package/dist/generators/add-application/add-application.js +98 -0
  5. package/dist/generators/add-application/schema.d.ts +6 -0
  6. package/dist/generators/add-application/schema.json +92 -0
  7. package/dist/generators/add-application/templates/default/.babelrc +11 -0
  8. package/dist/generators/add-application/templates/default/.eslintrc.json +18 -0
  9. package/dist/generators/add-application/templates/default/jest.config.d.ts +14 -0
  10. package/dist/generators/add-application/templates/default/jest.config.d.ts.map +1 -0
  11. package/dist/generators/add-application/templates/default/jest.config.js +13 -0
  12. package/dist/generators/add-application/templates/default/package.json.template +7 -0
  13. package/dist/generators/add-application/templates/default/project.json.template +9 -0
  14. package/dist/generators/add-application/templates/default/src/app/app.tsx.template +76 -0
  15. package/dist/generators/add-application/templates/default/src/favicon.ico +0 -0
  16. package/dist/generators/add-application/templates/default/src/index.html.template +17 -0
  17. package/dist/generators/add-application/templates/default/src/main.tsx +13 -0
  18. package/dist/generators/add-application/templates/default/src/styles.css +0 -0
  19. package/dist/generators/add-application/templates/default/tsconfig.app.json +24 -0
  20. package/dist/generators/add-application/templates/default/tsconfig.json +20 -0
  21. package/dist/generators/add-application/templates/default/tsconfig.spec.json +25 -0
  22. package/dist/generators/add-application/templates/default/webpack.config.js.template +33 -0
  23. package/dist/generators/add-application/templates/interactive-playground/.babelrc +11 -0
  24. package/dist/generators/add-application/templates/interactive-playground/.eslintrc.json +18 -0
  25. package/dist/generators/add-application/templates/interactive-playground/jest.config.d.ts +14 -0
  26. package/dist/generators/add-application/templates/interactive-playground/jest.config.d.ts.map +1 -0
  27. package/dist/generators/add-application/templates/interactive-playground/jest.config.js +13 -0
  28. package/dist/generators/add-application/templates/interactive-playground/package.json +7 -0
  29. package/dist/generators/add-application/templates/interactive-playground/project.json +9 -0
  30. package/dist/generators/add-application/templates/interactive-playground/src/app/app.tsx.template +122 -0
  31. package/dist/generators/add-application/templates/interactive-playground/src/app/components/header.tsx.template +123 -0
  32. package/dist/generators/add-application/templates/interactive-playground/src/app/components/settings-buttons.tsx +59 -0
  33. package/dist/generators/add-application/templates/interactive-playground/src/app/components/settings-form.tsx +104 -0
  34. package/dist/generators/add-application/templates/interactive-playground/src/app/components/settings.tsx +74 -0
  35. package/dist/generators/add-application/templates/interactive-playground/src/app/configuration-provider.tsx +163 -0
  36. package/dist/generators/add-application/templates/interactive-playground/src/app/definitions.d.ts +11 -0
  37. package/dist/generators/add-application/templates/interactive-playground/src/app/definitions.d.ts.map +1 -0
  38. package/dist/generators/add-application/templates/interactive-playground/src/app/definitions.js +19 -0
  39. package/dist/generators/add-application/templates/interactive-playground/src/app/utils/merge-deep.d.ts +13 -0
  40. package/dist/generators/add-application/templates/interactive-playground/src/app/utils/merge-deep.d.ts.map +1 -0
  41. package/dist/generators/add-application/templates/interactive-playground/src/app/utils/merge-deep.js +35 -0
  42. package/dist/generators/add-application/templates/interactive-playground/src/favicon.ico +0 -0
  43. package/dist/generators/add-application/templates/interactive-playground/src/index.html +17 -0
  44. package/dist/generators/add-application/templates/interactive-playground/src/main.tsx +13 -0
  45. package/dist/generators/add-application/templates/interactive-playground/src/styles.css +0 -0
  46. package/dist/generators/add-application/templates/interactive-playground/tsconfig.app.json +24 -0
  47. package/dist/generators/add-application/templates/interactive-playground/tsconfig.json +20 -0
  48. package/dist/generators/add-application/templates/interactive-playground/tsconfig.spec.json +25 -0
  49. package/dist/generators/add-application/templates/interactive-playground/webpack.config.js +33 -0
  50. package/dist/generators/add-package/README.md +70 -0
  51. package/dist/generators/add-package/add-package.d.ts +5 -0
  52. package/dist/generators/add-package/add-package.d.ts.map +1 -0
  53. package/dist/generators/add-package/add-package.js +85 -0
  54. package/dist/generators/add-package/files/.babelrc +12 -0
  55. package/dist/generators/add-package/files/.eslintrc.json +18 -0
  56. package/dist/generators/add-package/files/README.md +1 -0
  57. package/dist/generators/add-package/files/package.json.template +22 -0
  58. package/dist/generators/add-package/files/project.json.template +22 -0
  59. package/dist/generators/add-package/files/rollup.config.js +6 -0
  60. package/dist/generators/add-package/files/src/index.ts +1 -0
  61. package/dist/generators/add-package/files/tsconfig.json +20 -0
  62. package/dist/generators/add-package/files/tsconfig.lib.json +23 -0
  63. package/dist/generators/add-package/files/tsconfig.spec.json +26 -0
  64. package/dist/generators/add-package/files/vite.config.ts +30 -0
  65. package/dist/generators/add-package/schema.d.ts +4 -0
  66. package/dist/generators/add-package/schema.json +46 -0
  67. package/dist/generators/add-runtime/add-runtime.d.ts +5 -0
  68. package/dist/generators/add-runtime/add-runtime.d.ts.map +1 -0
  69. package/dist/generators/add-runtime/add-runtime.js +112 -0
  70. package/dist/generators/add-runtime/core-templates/__runtimeName__-runtime/runtime-types.ts.template +21 -0
  71. package/dist/generators/add-runtime/files/.babelrc +12 -0
  72. package/dist/generators/add-runtime/files/.eslintrc.json +18 -0
  73. package/dist/generators/add-runtime/files/README.md.template +1 -0
  74. package/dist/generators/add-runtime/files/package.json.template +12 -0
  75. package/dist/generators/add-runtime/files/project.json.template +22 -0
  76. package/dist/generators/add-runtime/files/rollup.config.js +6 -0
  77. package/dist/generators/add-runtime/files/src/index.ts +1 -0
  78. package/dist/generators/add-runtime/files/src/lib/create-factory.tsx.template +21 -0
  79. package/dist/generators/add-runtime/files/src/lib/runtime.tsx.template +66 -0
  80. package/dist/generators/add-runtime/files/tsconfig.json +20 -0
  81. package/dist/generators/add-runtime/files/tsconfig.lib.json +23 -0
  82. package/dist/generators/add-runtime/files/tsconfig.spec.json +26 -0
  83. package/dist/generators/add-runtime/files/vite.config.ts +30 -0
  84. package/dist/generators/add-runtime/schema.d.ts +4 -0
  85. package/dist/generators/add-runtime/schema.json +44 -0
  86. package/dist/generators/add-visual/add-visual.d.ts +8 -0
  87. package/dist/generators/add-visual/add-visual.d.ts.map +1 -0
  88. package/dist/generators/add-visual/add-visual.js +236 -0
  89. package/dist/generators/add-visual/files/render-method.template +31 -0
  90. package/dist/generators/add-visual/schema.d.ts +5 -0
  91. package/dist/generators/add-visual/schema.json +30 -0
  92. package/dist/generators/add-visual/visual-utils.d.ts +42 -0
  93. package/dist/generators/add-visual/visual-utils.d.ts.map +1 -0
  94. package/dist/generators/add-visual/visual-utils.js +99 -0
  95. package/dist/generators/utils.d.ts +18 -0
  96. package/dist/generators/utils.d.ts.map +1 -0
  97. package/dist/generators/utils.js +198 -0
  98. package/dist/index.d.ts +2 -0
  99. package/dist/index.d.ts.map +1 -0
  100. package/dist/index.js +2 -0
  101. package/generators.json +24 -0
  102. package/package.json +81 -0
@@ -0,0 +1,122 @@
1
+ import { useContext, useState } from 'react';
2
+ import { HostThemeProvider } from '@kaltura/ds-react-theme';
3
+ import { Box } from '@mui/material';
4
+ import { ConfigurationContext } from './configuration-provider';
5
+ import { isConfigurationValid } from './definitions';
6
+ import { Header } from './components/header';
7
+ import { ConfigurationProvider } from './configuration-provider';
8
+ import { UnisphereProvider } from '@unisphere/runtime-react';
9
+ import { UnisphereWorkspaceConfig } from '@unisphere/runtime';
10
+ import { UnisphereWorkspaceType } from '@unisphere/runtime';
11
+ import { <%= runtimeName__pascalCase %>Runtime } from '@kaltura/unisphere-<%= widgetName__lowerDashCase %>-core';
12
+
13
+ export function AppContent() {
14
+ const { configuration } = useContext(ConfigurationContext);
15
+
16
+ const [workspace, setWorkspace] = useState<UnisphereWorkspaceType | null>(
17
+ null
18
+ );
19
+ const [runtime, setRuntime] = useState<<%= runtimeName__pascalCase %>Runtime | null>(null);
20
+
21
+ const [initialConfiguration] = useState<UnisphereWorkspaceConfig | null>(
22
+ () => {
23
+ /*
24
+ Developer Note:
25
+ • You don’t need to touch the code to enable runtime configuration.
26
+
27
+ • If this runtime is already active in an app:
28
+ 1. Open Unisphere, press Cmd + K, then choose Manage Workspace Runtimes.
29
+ 2. Find the runtime you want, click … (Action), and select Copy Settings.
30
+ 3. Paste into NVQ2 with a valid host—settings will be ready to go.
31
+
32
+ • If the runtime isn’t yet in use:
33
+ 1. Manually create the JSON payload matching the runtime’s schema.
34
+
35
+ Once you run the application you will have a button where you can save the configurations.
36
+ The application will save it in the local storage and will load it on the next run.
37
+ */
38
+
39
+ try {
40
+ if (!isConfigurationValid(configuration)) {
41
+ return null;
42
+ }
43
+
44
+ return {
45
+ appId: '<%= runtimeName__lowerDashCase %>-expo',
46
+ appVersion: '1.0.0',
47
+ serverUrl: configuration.unisphereServerUrl,
48
+ ui: {
49
+ theme: configuration.theme,
50
+ language: configuration.language,
51
+ },
52
+ runtimes: [
53
+ {
54
+ widgetName: 'unisphere.widget.<%= widgetName__lowerDashCase %>',
55
+ runtimeName: '<%= runtimeName__lowerDashCase %>',
56
+ settings: JSON.parse(configuration.settings),
57
+ visuals: [],
58
+ },
59
+ ],
60
+ };
61
+ } catch (e) {
62
+ console.error('Failed to parse settings', e);
63
+ return null;
64
+ }
65
+ }
66
+ );
67
+
68
+ const onWorkspaceLoaded = (workspace: UnisphereWorkspaceType) => {
69
+ setWorkspace(workspace);
70
+ const runtime = workspace.getRuntime<<%= runtimeName__pascalCase %>Runtime>(
71
+ 'unisphere.widget.<%= widgetName__lowerDashCase %>',
72
+ '<%= runtimeName__lowerDashCase %>'
73
+ );
74
+ setRuntime(runtime);
75
+ };
76
+
77
+ return (
78
+ <Box
79
+ sx={{
80
+ width: '100vw',
81
+ height: '100vh',
82
+ overflow: 'hidden',
83
+ padding: '12px 24px',
84
+ }}
85
+ >
86
+ <HostThemeProvider
87
+ overrides={{
88
+ mode: configuration.theme,
89
+ }}
90
+ >
91
+ <Box sx={{ height: '100vh', width: '100vw', pt: '64px' }}>
92
+ <Header />
93
+ {initialConfiguration && (
94
+ <UnisphereProvider
95
+ onWorkspaceLoaded={onWorkspaceLoaded}
96
+ initialConfiguration={initialConfiguration}
97
+ >
98
+ {/* <UnisphereRuntimeVisual
99
+ widgetName="unisphere.widget.<%= widgetName__lowerDashCase %>"
100
+ runtimeName="<%= runtimeName__lowerDashCase %>"
101
+ visualType="-- provide here the visual name --"
102
+ visualSettings={{
103
+ -- provide here the visual settings --
104
+ }}
105
+ /> */}
106
+ </UnisphereProvider>
107
+ )}
108
+ </Box>
109
+ </HostThemeProvider>
110
+ </Box>
111
+ );
112
+ }
113
+
114
+ const App = () => {
115
+ return (
116
+ <ConfigurationProvider configurationKey="unisphere-<%= widgetName__lowerDashCase %>-<%= runtimeName__lowerDashCase %>-expo">
117
+ <AppContent />
118
+ </ConfigurationProvider>
119
+ );
120
+ };
121
+
122
+ export default App;
@@ -0,0 +1,123 @@
1
+ import React, { useContext, useState } from 'react';
2
+ import {
3
+ AppBar,
4
+ Box,
5
+ IconButton,
6
+ ListItemIcon,
7
+ Menu,
8
+ MenuItem,
9
+ Toolbar,
10
+ Tooltip,
11
+ Typography,
12
+ } from '@mui/material';
13
+ import { ConfigurationContext } from '../configuration-provider';
14
+ import { isConfigurationValid } from '../definitions';
15
+ import {
16
+ DeveloperBoard as DeveloperBoardIcon,
17
+ Settings as SettingsIcon,
18
+ Storage as StorageIcon,
19
+ } from '@mui/icons-material';
20
+ import { Settings } from './settings';
21
+
22
+ export const Header: React.FC = () => {
23
+ const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
24
+ const { configuration: Configuration, createSharedUrl } =
25
+ useContext(ConfigurationContext);
26
+
27
+ const open = Boolean(anchorEl);
28
+ const replaceBaseUrlInNewTab = () => {
29
+ const baseUrl = prompt('please provide the new base url');
30
+ if (!baseUrl) {
31
+ return;
32
+ }
33
+
34
+ const filteredUrl = baseUrl.replace(/\/configuration$/, '');
35
+ window.open(
36
+ createSharedUrl({
37
+ baseUrl: `${filteredUrl}`,
38
+ })
39
+ );
40
+ return;
41
+ };
42
+
43
+ const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
44
+ setAnchorEl(event.currentTarget);
45
+ };
46
+
47
+ const handleClose = () => {
48
+ setAnchorEl(null);
49
+ };
50
+
51
+ const [settingsOpen, setSettingsOpen] = useState(() => {
52
+ return !isConfigurationValid(Configuration);
53
+ });
54
+
55
+ const handleSettingsClosed = () => {
56
+ setSettingsOpen(false);
57
+ };
58
+
59
+ return (
60
+ <AppBar position="fixed" sx={{ zIndex: 1000 }}>
61
+ <Toolbar style={{ justifyContent: 'space-between' }}>
62
+ <Box
63
+ sx={{
64
+ display: 'flex',
65
+ alignItems: 'center',
66
+ }}
67
+ >
68
+ <Typography variant="h6" sx={{ flexGrow: 1 }}>
69
+ <%= widgetName__pascalCase %> - <%= runtimeName__pascalCase %> Expo
70
+ </Typography>
71
+ </Box>
72
+
73
+ <Box
74
+ sx={{
75
+ display: 'flex',
76
+ alignItems: 'center',
77
+ }}
78
+ >
79
+ <Tooltip title="Menu">
80
+ <IconButton onClick={handleMenu} color="inherit" aria-label="menu">
81
+ <SettingsIcon />
82
+ </IconButton>
83
+ </Tooltip>
84
+
85
+ <Menu
86
+ id="menu-appbar"
87
+ anchorEl={anchorEl}
88
+ anchorOrigin={{
89
+ vertical: 'top',
90
+ horizontal: 'right',
91
+ }}
92
+ keepMounted
93
+ transformOrigin={{
94
+ vertical: 'top',
95
+ horizontal: 'right',
96
+ }}
97
+ open={open}
98
+ onClose={handleClose}
99
+ >
100
+ <MenuItem
101
+ onClick={() => {
102
+ handleClose();
103
+ setSettingsOpen(true);
104
+ }}
105
+ >
106
+ <ListItemIcon>
107
+ <StorageIcon fontSize="small" />
108
+ </ListItemIcon>
109
+ <Typography variant="inherit">Configuration</Typography>
110
+ </MenuItem>
111
+ <MenuItem onClick={replaceBaseUrlInNewTab}>
112
+ <ListItemIcon>
113
+ <DeveloperBoardIcon fontSize="small" />
114
+ </ListItemIcon>
115
+ <Typography variant="inherit">Change Base URL</Typography>
116
+ </MenuItem>
117
+ </Menu>
118
+ </Box>
119
+ </Toolbar>
120
+ {settingsOpen && <Settings onClose={handleSettingsClosed} />}
121
+ </AppBar>
122
+ );
123
+ };
@@ -0,0 +1,59 @@
1
+ import React, { useContext } from 'react';
2
+ import { Box, Button } from '@mui/material';
3
+ import {
4
+ ConfigurationContext,
5
+ ConfigurationTypes,
6
+ } from '../configuration-provider';
7
+
8
+ export interface SettingsButtonsProps {
9
+ onReset: () => void;
10
+ customSave?: React.ReactElement;
11
+ }
12
+
13
+ export const ButtonsContainer: React.FC<{
14
+ children?: React.ReactChild;
15
+ }> = ({ children }) => {
16
+ return (
17
+ <Box
18
+ sx={{
19
+ display: 'flex',
20
+ justifyContent: 'space-between',
21
+ alignItems: 'center',
22
+ marginTop: '20px',
23
+ }}
24
+ >
25
+ {children}
26
+ </Box>
27
+ );
28
+ };
29
+
30
+ export const SettingsButtons: React.FC<SettingsButtonsProps> = ({
31
+ customSave,
32
+ onReset,
33
+ }) => {
34
+ const { ConfigurationType } = useContext(ConfigurationContext);
35
+
36
+ return (
37
+ <ButtonsContainer>
38
+ <>
39
+ {ConfigurationType === ConfigurationTypes.Localstorage && (
40
+ <Button
41
+ onClick={onReset}
42
+ variant="contained"
43
+ color="secondary"
44
+ style={{ marginRight: '8px' }}
45
+ >
46
+ Clear All
47
+ </Button>
48
+ )}
49
+ {customSave ? (
50
+ customSave
51
+ ) : (
52
+ <Button variant="contained" color="primary" type={'submit'}>
53
+ Save & Close
54
+ </Button>
55
+ )}
56
+ </>
57
+ </ButtonsContainer>
58
+ );
59
+ };
@@ -0,0 +1,104 @@
1
+ import { Controller, useFormContext } from 'react-hook-form';
2
+ import React, { MouseEvent } from 'react';
3
+ import { MenuItem, Select, Stack, TextField, Typography } from '@mui/material';
4
+ import { Configuration } from '../definitions';
5
+ import { UseFormReturn } from 'react-hook-form/dist/types';
6
+
7
+ const FormCard: React.FC<React.PropsWithChildren<{ title: string }>> = ({
8
+ title,
9
+ children,
10
+ }) => {
11
+ return (
12
+ <Stack
13
+ sx={{
14
+ padding: '8px',
15
+ border: '1px solid grey',
16
+ borderRadius: '8px',
17
+ gap: '16px',
18
+ }}
19
+ >
20
+ <Typography variant="body1" sx={{ fontWeight: 700 }}>
21
+ {title}
22
+ </Typography>
23
+ {children}
24
+ </Stack>
25
+ );
26
+ };
27
+
28
+ const FormItem: React.FC<React.PropsWithChildren<{ title: string }>> = ({
29
+ title,
30
+ children,
31
+ }) => {
32
+ return (
33
+ <Stack
34
+ sx={{
35
+ padding: '4px',
36
+ gap: '8px',
37
+ }}
38
+ >
39
+ <Typography variant="body1">{title}</Typography>
40
+ {children}
41
+ </Stack>
42
+ );
43
+ };
44
+
45
+ export const SettingsForm: React.FC = () => {
46
+ const context = useFormContext<Configuration>();
47
+
48
+ const {
49
+ register,
50
+ reset,
51
+ formState: { errors },
52
+ control,
53
+ watch,
54
+ } = context;
55
+
56
+ return (
57
+ <Stack gap="16px">
58
+ <FormCard title="Host Settings">
59
+ <TextField
60
+ name={'unisphereServerUrl'}
61
+ fullWidth
62
+ inputProps={{
63
+ ...register('unisphereServerUrl', { required: true }),
64
+ }}
65
+ label={'Service URL'}
66
+ helperText={errors?.unisphereServerUrl ? 'required' : ''}
67
+ error={!!errors?.unisphereServerUrl}
68
+ />
69
+
70
+ <TextField
71
+ name={'language'}
72
+ fullWidth
73
+ inputProps={{ ...register('language') }}
74
+ label={'Language code'}
75
+ />
76
+
77
+ <FormItem title="Theme">
78
+ <Controller
79
+ control={control}
80
+ name="theme"
81
+ render={({ field }) => (
82
+ <Select {...field}>
83
+ <MenuItem value="light">Light</MenuItem>
84
+ <MenuItem value="dark">Dark</MenuItem>
85
+ </Select>
86
+ )}
87
+ />
88
+ </FormItem>
89
+ </FormCard>
90
+ <FormCard title="Unisphere Settings">
91
+ <TextField
92
+ name={'settings'}
93
+ fullWidth
94
+ inputProps={{
95
+ ...register('settings', { required: true }),
96
+ }}
97
+ label={'Unisphere Settings'}
98
+ helperText={`Easily copy settings from Unisphere by entering a workable host in NVQ2 and then do: Unisphere cmd+k > Manage Workspace Runtimes > show ... (actions) of desired runtime > copy settings`}
99
+ error={!!errors?.settings}
100
+ />
101
+ </FormCard>
102
+ </Stack>
103
+ );
104
+ };
@@ -0,0 +1,74 @@
1
+ import React, { useContext } from 'react';
2
+ import { Dialog, DialogActions, DialogContent } from '@mui/material';
3
+ import { FormProvider, useForm } from 'react-hook-form';
4
+ import { ConfigurationContext } from '../configuration-provider';
5
+ import { getDefaultConfiguration, Configuration } from '../definitions';
6
+ import { SettingsButtons } from './settings-buttons';
7
+ import { SettingsForm } from './settings-form';
8
+
9
+ export const Settings: React.FC<{ onClose: () => void }> = ({ onClose }) => {
10
+ const {
11
+ clearConfiguration,
12
+ configuration: Configuration,
13
+ updateConfiguration,
14
+ } = useContext(ConfigurationContext);
15
+
16
+ const formMethods = useForm<Configuration>({
17
+ defaultValues: Configuration,
18
+ });
19
+
20
+ const onSubmit = (data: any) => {
21
+ const updatedData = {
22
+ ...data,
23
+ };
24
+ updateConfiguration(updatedData);
25
+
26
+ onClose();
27
+
28
+ window.location.reload();
29
+ };
30
+
31
+ const handleClose = (event: any, reason: string) => {
32
+ if (reason === 'backdropClick') {
33
+ return;
34
+ }
35
+ onClose();
36
+ };
37
+
38
+ const handleReset = () => {
39
+ // eslint-disable-next-line no-restricted-globals
40
+ const accepted = confirm('Do you want to clear all values?');
41
+ if (!accepted) {
42
+ return;
43
+ }
44
+ clearConfiguration();
45
+ formMethods.reset(getDefaultConfiguration());
46
+ };
47
+ return (
48
+ <div>
49
+ <Dialog open={true} onClose={handleClose} disableEscapeKeyDown={true}>
50
+ <FormProvider {...formMethods}>
51
+ <form
52
+ noValidate
53
+ autoComplete="off"
54
+ onSubmit={formMethods.handleSubmit(onSubmit)}
55
+ >
56
+ <DialogContent
57
+ sx={{
58
+ display: 'flex',
59
+ width: '500px',
60
+ flexDirection: 'column',
61
+ overflow: 'hidden',
62
+ }}
63
+ >
64
+ <SettingsForm />
65
+ <DialogActions sx={{ padding: 0 }}>
66
+ <SettingsButtons onReset={handleReset} />
67
+ </DialogActions>
68
+ </DialogContent>
69
+ </form>
70
+ </FormProvider>
71
+ </Dialog>
72
+ </div>
73
+ );
74
+ };
@@ -0,0 +1,163 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import {
3
+ getDefaultConfiguration,
4
+ isConfigurationValid,
5
+ Configuration,
6
+ } from './definitions';
7
+ import { mergeDeep } from './utils/merge-deep';
8
+
9
+ export const ConfigurationContext = React.createContext<{
10
+ configuration: Configuration;
11
+ sharedUrl: string;
12
+ clearConfiguration: () => void;
13
+ updateConfiguration: (configuration: Configuration) => void;
14
+ createSharedUrl: (options?: {
15
+ baseUrl?: string;
16
+ customConfiguration?: Configuration;
17
+ }) => string;
18
+ ConfigurationType: ConfigurationTypes;
19
+ refreshConfiguration: () => void;
20
+ }>(null as any);
21
+
22
+ export const getConfigurationFromQueryParams = (
23
+ configurationKey: string
24
+ ): Configuration | null => {
25
+ const search = window.location.search;
26
+ const params = new URLSearchParams(search);
27
+
28
+ if (!params || !configurationKey || !params.get(configurationKey))
29
+ return null;
30
+
31
+ const configuration = window.atob(params.get(configurationKey) || '');
32
+ try {
33
+ return JSON.parse(configuration);
34
+ } catch (e: any) {
35
+ console.warn(`Failed to parse login query string ${e.message} ${e.stack}`);
36
+ return null;
37
+ }
38
+ };
39
+
40
+ export enum ConfigurationTypes {
41
+ None,
42
+ Querystring,
43
+ Localstorage,
44
+ }
45
+
46
+ const { hostname, port, protocol } = window.location;
47
+ export const hosterAppUrl = `${protocol}//${hostname}${port ? `:${port}` : ''}`;
48
+
49
+ const getConfigurationFromLocalStorage = (
50
+ configurationKey: string
51
+ ): Configuration | null => {
52
+ const data = localStorage.getItem(configurationKey);
53
+ return data ? JSON.parse(data) : null;
54
+ };
55
+
56
+ const loginParamsToBase64 = (configuration: Configuration | null): string => {
57
+ if (!configuration) {
58
+ return '';
59
+ }
60
+ try {
61
+ return window.btoa(JSON.stringify(configuration));
62
+ } catch (e) {
63
+ return '';
64
+ }
65
+ };
66
+
67
+ export const ConfigurationProvider: React.FC<
68
+ React.PropsWithChildren<{
69
+ configurationKey: string;
70
+ }>
71
+ > = (props) => {
72
+ const { configurationKey } = props;
73
+ const [ConfigurationType] = useState<ConfigurationTypes>(() => {
74
+ return getConfigurationFromQueryParams(configurationKey)
75
+ ? ConfigurationTypes.Querystring
76
+ : ConfigurationTypes.Localstorage;
77
+ });
78
+ const [configuration, setConfiguration] = useState<Configuration | null>(
79
+ () => {
80
+ const configuration = mergeDeep(
81
+ {},
82
+ getDefaultConfiguration(),
83
+ ConfigurationType === ConfigurationTypes.Querystring
84
+ ? getConfigurationFromQueryParams(configurationKey)
85
+ : getConfigurationFromLocalStorage(configurationKey)
86
+ );
87
+
88
+ return configuration && isConfigurationValid(configuration)
89
+ ? configuration
90
+ : getDefaultConfiguration();
91
+ }
92
+ );
93
+
94
+ const providerValue = useMemo(() => {
95
+ const createSharedUrl = (options?: {
96
+ baseUrl?: string;
97
+ customConfiguration?: Configuration;
98
+ }) => {
99
+ const { baseUrl = '', customConfiguration } = options || {};
100
+
101
+ if (!options?.customConfiguration && !configuration) {
102
+ return '';
103
+ }
104
+
105
+ return `${
106
+ baseUrl || hosterAppUrl
107
+ }/configuration?${configurationKey}=${loginParamsToBase64(
108
+ customConfiguration || configuration
109
+ )}`;
110
+ };
111
+
112
+ const saveToLocalStorage = (params: Configuration) => {
113
+ if (ConfigurationType === ConfigurationTypes.Querystring) {
114
+ const sharedUrl = createSharedUrl({
115
+ customConfiguration: params,
116
+ });
117
+
118
+ if (sharedUrl) {
119
+ window.location.href = sharedUrl;
120
+ }
121
+
122
+ return;
123
+ }
124
+
125
+ localStorage.setItem(
126
+ configurationKey,
127
+ params ? JSON.stringify(params) : ''
128
+ );
129
+ setConfiguration(params || null);
130
+ };
131
+
132
+ const clearLocalStorage = () => {
133
+ localStorage.removeItem(configurationKey);
134
+ setConfiguration(null);
135
+ };
136
+
137
+ const refreshConfiguration = () => {
138
+ setConfiguration((prev) => {
139
+ if (!prev) {
140
+ return prev;
141
+ }
142
+ return {
143
+ ...prev,
144
+ };
145
+ });
146
+ };
147
+ return {
148
+ configuration: configuration || getDefaultConfiguration(),
149
+ ConfigurationType,
150
+ refreshConfiguration,
151
+ updateConfiguration: saveToLocalStorage,
152
+ clearConfiguration: clearLocalStorage,
153
+ createSharedUrl,
154
+ sharedUrl: createSharedUrl(),
155
+ };
156
+ }, [configuration]);
157
+
158
+ return (
159
+ <ConfigurationContext.Provider value={providerValue}>
160
+ {props.children}
161
+ </ConfigurationContext.Provider>
162
+ );
163
+ };
@@ -0,0 +1,11 @@
1
+ export declare const inProduction: boolean;
2
+ export interface Configuration {
3
+ _schemaVersion: string;
4
+ unisphereServerUrl: string;
5
+ language: string;
6
+ theme: 'light' | 'dark';
7
+ settings: string;
8
+ }
9
+ export declare const getDefaultConfiguration: () => Configuration;
10
+ export declare const isConfigurationValid: (config: any) => config is Configuration;
11
+ //# sourceMappingURL=definitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../../../../../../src/generators/add-application/templates/interactive-playground/src/app/definitions.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,SAAwC,CAAC;AAGlE,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,uBAAuB,QAAO,aAMzC,CAAC;AAGH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,GAAG,KAAG,MAAM,IAAI,aAE5D,CAAC"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isConfigurationValid = exports.getDefaultConfiguration = exports.inProduction = void 0;
4
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
5
+ // @ts-ignore
6
+ exports.inProduction = process.env.NODE_ENV === 'production';
7
+ const getDefaultConfiguration = () => ({
8
+ unisphereServerUrl: 'https://unisphere.nvq2.ovp.kaltura.com/v1',
9
+ language: 'en',
10
+ _schemaVersion: '1',
11
+ theme: 'light',
12
+ settings: '',
13
+ });
14
+ exports.getDefaultConfiguration = getDefaultConfiguration;
15
+ // todo set here the logic needed to validate configuration
16
+ const isConfigurationValid = (config) => {
17
+ return config?.unisphereServerUrl && config?.settings;
18
+ };
19
+ exports.isConfigurationValid = isConfigurationValid;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Simple object check.
3
+ * @param item
4
+ * @returns {boolean}
5
+ */
6
+ export declare function isObject(item: any): any;
7
+ /**
8
+ * Deep merge two objects.
9
+ * @param target
10
+ * @param ...sources
11
+ */
12
+ export declare function mergeDeep(target: any, ...sources: any[]): any;
13
+ //# sourceMappingURL=merge-deep.d.ts.map