@whereby.com/browser-sdk 1.8.0 → 2.0.0-alpha

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 (64) hide show
  1. package/.eslintrc +4 -2
  2. package/.storybook/main.cjs +13 -2
  3. package/README.md +60 -7
  4. package/jest.config.js +2 -0
  5. package/package.json +38 -12
  6. package/rollup.config.js +37 -13
  7. package/src/lib/RoomConnection.ts +516 -0
  8. package/src/lib/RoomParticipant.ts +77 -0
  9. package/src/lib/__tests__/{index.unit.js → embed.unit.ts} +3 -9
  10. package/src/lib/api/ApiClient.ts +111 -0
  11. package/src/lib/api/Credentials.ts +45 -0
  12. package/src/lib/api/HttpClient.ts +95 -0
  13. package/src/lib/api/MultipartHttpClient.ts +53 -0
  14. package/src/lib/api/OrganizationApiClient.ts +64 -0
  15. package/src/lib/api/Response.ts +34 -0
  16. package/src/lib/api/credentialsService/index.ts +159 -0
  17. package/src/lib/api/credentialsService/test/index.spec.ts +181 -0
  18. package/src/lib/api/deviceService/index.ts +42 -0
  19. package/src/lib/api/deviceService/tests/index.spec.ts +74 -0
  20. package/src/lib/api/extractUtils.ts +160 -0
  21. package/src/lib/api/index.ts +8 -0
  22. package/src/lib/api/localStorageWrapper/index.ts +15 -0
  23. package/src/lib/api/models/Account.ts +48 -0
  24. package/src/lib/api/models/Meeting.ts +42 -0
  25. package/src/lib/api/models/Organization.ts +186 -0
  26. package/src/lib/api/models/Room.ts +44 -0
  27. package/src/lib/api/models/account/EmbeddedFreeTierStatus.ts +34 -0
  28. package/src/lib/api/models/tests/Account.spec.ts +128 -0
  29. package/src/lib/api/models/tests/Organization.spec.ts +161 -0
  30. package/src/lib/api/models/tests/Room.spec.ts +74 -0
  31. package/src/lib/api/modules/AbstractStore.ts +18 -0
  32. package/src/lib/api/modules/ChromeStorageStore.ts +44 -0
  33. package/src/lib/api/modules/LocalStorageStore.ts +57 -0
  34. package/src/lib/api/modules/tests/ChromeStorageStore.spec.ts +67 -0
  35. package/src/lib/api/modules/tests/LocalStorageStore.spec.ts +79 -0
  36. package/src/lib/api/modules/tests/__mocks__/storage.ts +24 -0
  37. package/src/lib/api/organizationService/index.ts +284 -0
  38. package/src/lib/api/organizationService/tests/index.spec.ts +781 -0
  39. package/src/lib/api/organizationServiceCache/index.ts +28 -0
  40. package/src/lib/api/organizationServiceCache/tests/index.spec.ts +101 -0
  41. package/src/lib/api/parameterAssertUtils.ts +166 -0
  42. package/src/lib/api/roomService/index.ts +310 -0
  43. package/src/lib/api/roomService/tests/index.spec.ts +668 -0
  44. package/src/lib/api/test/ApiClient.spec.ts +139 -0
  45. package/src/lib/api/test/HttpClient.spec.ts +120 -0
  46. package/src/lib/api/test/MultipartHttpClient.spec.ts +145 -0
  47. package/src/lib/api/test/OrganizationApiClient.spec.ts +132 -0
  48. package/src/lib/api/test/extractUtils.spec.ts +357 -0
  49. package/src/lib/api/test/helpers.ts +41 -0
  50. package/src/lib/api/test/parameterAssertUtils.spec.ts +265 -0
  51. package/src/lib/api/types.ts +6 -0
  52. package/src/lib/{index.js → embed.ts} +53 -27
  53. package/src/lib/index.ts +3 -0
  54. package/src/lib/react/VideoElement.tsx +16 -0
  55. package/src/lib/react/index.ts +3 -0
  56. package/src/lib/react/useLocalMedia.ts +25 -0
  57. package/src/lib/react/useRoomConnection.ts +92 -0
  58. package/src/lib/reducer.ts +142 -0
  59. package/src/stories/custom-ui.stories.tsx +133 -0
  60. package/src/stories/prebuilt-ui.stories.tsx +131 -0
  61. package/src/stories/styles.css +74 -0
  62. package/src/types.d.ts +175 -0
  63. package/tsconfig.json +30 -0
  64. package/src/index.stories.js +0 -105
@@ -0,0 +1,131 @@
1
+ import { Story } from "@storybook/react";
2
+ import React from "react";
3
+ import "../lib/embed";
4
+
5
+ interface WherebyEmbedAttributes {
6
+ audio: boolean;
7
+ avatarUrl: string;
8
+ background: boolean;
9
+ cameraAccess: boolean;
10
+ chat: boolean;
11
+ displayName: string;
12
+ emptyRoomInvitation: string;
13
+ floatSelf: boolean;
14
+ help: boolean;
15
+ leaveButton: boolean;
16
+ logo: boolean;
17
+ people: boolean;
18
+ precallReview: boolean;
19
+ recording: boolean;
20
+ room: string;
21
+ screenshare: boolean;
22
+ video: boolean;
23
+ virtualBackgroundUrl: string;
24
+ }
25
+
26
+ export default {
27
+ title: "Examples/Pre-built UI",
28
+ argTypes: {
29
+ audio: { control: "boolean" },
30
+ avatarUrl: { control: "text", description: "Image url to use for avatar" },
31
+ background: { control: "boolean" },
32
+ cameraAccess: { control: "boolean" },
33
+ chat: { control: "boolean" },
34
+ displayName: { control: "text", description: "The name to use for the local participant" },
35
+ embed: { control: "boolean" },
36
+ emptyRoomInvitation: { control: "boolean" },
37
+ floatSelf: { control: "boolean" },
38
+ help: { control: "boolean" },
39
+ leaveButton: { control: "boolean" },
40
+ locking: { control: "boolean" },
41
+ logo: { control: "boolean" },
42
+ people: { control: "boolean" },
43
+ precallReview: { control: "boolean" },
44
+ recording: { control: "boolean" },
45
+ room: { control: "text" },
46
+ screenshare: { control: "boolean" },
47
+ topToolbar: { control: "boolean" },
48
+ video: { control: "boolean" },
49
+ virtualBackgroundUrl: { control: "text", description: "Image url to use for virtual background" },
50
+ },
51
+ };
52
+
53
+ const offOn = (arg: boolean | string | undefined) => (arg ? "on" : "off");
54
+
55
+ const WherebyEmbed = ({
56
+ audio,
57
+ avatarUrl,
58
+ background,
59
+ cameraAccess,
60
+ chat,
61
+ displayName,
62
+ emptyRoomInvitation,
63
+ floatSelf,
64
+ help,
65
+ leaveButton,
66
+ logo,
67
+ people,
68
+ precallReview,
69
+ recording,
70
+ room,
71
+ screenshare,
72
+ video,
73
+ virtualBackgroundUrl,
74
+ }: Partial<WherebyEmbedAttributes>) => {
75
+ return (
76
+ <p>
77
+ <whereby-embed
78
+ audio={offOn(audio)}
79
+ avatarUrl={avatarUrl}
80
+ background={offOn(background)}
81
+ cameraAccess={offOn(cameraAccess)}
82
+ chat={offOn(chat)}
83
+ displayName={displayName}
84
+ emptyRoomInvitation={emptyRoomInvitation}
85
+ floatSelf={offOn(floatSelf)}
86
+ help={offOn(help)}
87
+ leaveButton={offOn(leaveButton)}
88
+ logo={offOn(logo)}
89
+ people={offOn(people)}
90
+ precallReview={offOn(precallReview)}
91
+ recording={offOn(recording)}
92
+ screenshare={offOn(screenshare)}
93
+ video={offOn(video)}
94
+ virtualBackgroundUrl={virtualBackgroundUrl}
95
+ room={room}
96
+ style={{ height: "100vh", width: "100%" }}
97
+ />
98
+ </p>
99
+ );
100
+ };
101
+
102
+ const Template: Story<Partial<WherebyEmbedAttributes>> = (args) => WherebyEmbed(args);
103
+ export const WherebyEmbedElement = Template.bind({});
104
+
105
+ WherebyEmbedElement.args = {
106
+ audio: true,
107
+ avatarUrl: "",
108
+ background: true,
109
+ cameraAccess: true,
110
+ chat: true,
111
+ displayName: "Your name",
112
+ emptyRoomInvitation: "true",
113
+ floatSelf: false,
114
+ help: true,
115
+ leaveButton: true,
116
+ logo: true,
117
+ people: true,
118
+ precallReview: true,
119
+ room: process.env.STORYBOOK_ROOM,
120
+ screenshare: true,
121
+ video: true,
122
+ virtualBackgroundUrl: "",
123
+ };
124
+
125
+ WherebyEmbedElement.parameters = {
126
+ docs: {
127
+ transformSource: (src: string) => {
128
+ return (src || "").replace(/><iframe(.+)$/, " />");
129
+ },
130
+ },
131
+ };
@@ -0,0 +1,74 @@
1
+ .container {
2
+ display: flex;
3
+ min-height: 290px;
4
+ background-color: pink;
5
+ gap: 10px;
6
+ padding: 10px;
7
+ }
8
+
9
+ .participantWrapper {
10
+ position: relative;
11
+ flex-direction: column;
12
+ display: flex;
13
+ width: 140px;
14
+ }
15
+
16
+ .bouncingball {
17
+ width: 140px;
18
+ height: 140px;
19
+ border-radius: 100%;
20
+ background: #ccc;
21
+ animation: bounce 1s;
22
+ transform: translateY(0px);
23
+ animation-iteration-count: infinite;
24
+ position: absolute;
25
+ top: 25px;
26
+ overflow: hidden;
27
+ }
28
+
29
+ .displayName {
30
+ text-align: center;
31
+ color: #ffffff;
32
+ padding: 2px 4px;
33
+ border-radius: 3px;
34
+ background-color: rgba(0, 0, 0, 0.5);
35
+ }
36
+
37
+ .controls {
38
+ display: flex;
39
+ gap: 10px;
40
+ margin-top: 12px;
41
+ }
42
+
43
+ @keyframes bounce {
44
+ 0% {
45
+ top: 25px;
46
+ -webkit-animation-timing-function: ease-in;
47
+ }
48
+ 40% {
49
+ }
50
+ 50% {
51
+ top: 140px;
52
+ height: 140px;
53
+ -webkit-animation-timing-function: ease-out;
54
+ }
55
+ 55% {
56
+ top: 160px;
57
+ height: 120px;
58
+ -webkit-animation-timing-function: ease-in;
59
+ }
60
+ 65% {
61
+ top: 120px;
62
+ height: 140px;
63
+ -webkit-animation-timing-function: ease-out;
64
+ }
65
+ 95% {
66
+ top: 25px;
67
+ -webkit-animation-timing-function: ease-in;
68
+ }
69
+ 100% {
70
+ top: 25px;
71
+ -webkit-animation-timing-function: ease-in;
72
+ }
73
+ }
74
+
package/src/types.d.ts ADDED
@@ -0,0 +1,175 @@
1
+ declare module "heresy" {
2
+ interface AttrChanged {
3
+ attributeName: string;
4
+ oldValue: string | boolean;
5
+ }
6
+
7
+ interface element {
8
+ observedAttributes?: string[];
9
+ onattributechanged?: (attrChanged: AttrChanged) => void;
10
+ onconnected?: () => void;
11
+ ondisconnected?: () => void;
12
+ oninit?: () => void;
13
+ render: () => void;
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ [x: string]: any;
16
+ }
17
+
18
+ export function define(elementName: string, element: element): void;
19
+ export function ref(): boolean;
20
+ }
21
+
22
+ declare module "@whereby/jslib-media/src/webrtc/RtcManagerDispatcher" {
23
+ enum RtcEventNames {
24
+ rtc_manager_created = "rtc_manager_created",
25
+ stream_added = "stream_added",
26
+ }
27
+
28
+ interface RtcManagerCreatedPayload {
29
+ rtcManager: RtcManager;
30
+ }
31
+
32
+ interface RtcStreamAddedPayload {
33
+ clientId: string;
34
+ stream: MediaStream;
35
+ streamId: string;
36
+ }
37
+
38
+ type RtcEvents = {
39
+ rtc_manager_created: RtcManagerCreatedPayload;
40
+ stream_added: RtcStreamAddedPayload;
41
+ };
42
+
43
+ interface ConstructorArgs {
44
+ emitter: { emit: <K extends keyof RtcEvents>(eventName: K, args: RtcEvents[K]) => void };
45
+ serverSocket: ServerSocket;
46
+ webrtcProvider: {
47
+ getMediaConstraints: () => { audio: boolean; video: boolean };
48
+ deferrable: (clientId: string) => boolean;
49
+ };
50
+ features: {
51
+ lowDataModeEnabled: boolean;
52
+ sfuServerOverrideHost: undefined;
53
+ turnServerOverrideHost: undefined;
54
+ useOnlyTURN: undefined;
55
+ vp9On: boolean;
56
+ h264On: boolean;
57
+ simulcastScreenshareOn: boolean;
58
+ };
59
+ }
60
+ export default class RtcManagerDispatcher {
61
+ constructor(args: ConstructorArgs);
62
+ }
63
+ }
64
+
65
+ declare module "@whereby/jslib-media/src/webrtc/RtcManager" {
66
+ export default interface RtcManager {
67
+ acceptNewStream: ({
68
+ activeBreakout: boolean,
69
+ clientId: string,
70
+ shouldAddLocalVideo: boolean,
71
+ streamId: string,
72
+ }) => void;
73
+ addNewStream: (streamId: string, stream: MediaStream, isAudioEnabled: boolean, isVideoEnabled: boolean) => void;
74
+ disconnect(streamId: string, activeBreakout: boolean): void;
75
+ shouldAcceptStreamsFromBothSides?: () => boolean;
76
+ }
77
+ }
78
+
79
+ declare module "@whereby/jslib-media/src/utils/urls" {
80
+ export function fromLocation({ host }: { host: string }): { subdomain: string };
81
+ }
82
+
83
+ declare module "@whereby/jslib-media/src/utils/ServerSocket" {
84
+ interface SocketConf {
85
+ host?: string;
86
+ path?: string;
87
+ reconnectionDelay?: number;
88
+ reconnectoinDelayMax?: number;
89
+ timeout?: number;
90
+ }
91
+
92
+ interface ClientRole {
93
+ roleName: string;
94
+ }
95
+
96
+ interface SignalClient {
97
+ displayName: string;
98
+ id: string;
99
+ streams: string[];
100
+ isAudioEnabled: boolean;
101
+ isVideoEnabled: boolean;
102
+ role: ClientRole;
103
+ }
104
+
105
+ interface AudioEnabledEvent {
106
+ clientId: string;
107
+ isAudioEnabled: boolean;
108
+ }
109
+
110
+ interface ClientLeftEvent {
111
+ clientId: string;
112
+ }
113
+ interface NewClientEvent {
114
+ client: SignalClient;
115
+ }
116
+
117
+ interface RoomJoinedEvent {
118
+ room: {
119
+ clients: SignalClient[];
120
+ };
121
+ selfId: string;
122
+ }
123
+
124
+ interface VideoEnabledEvent {
125
+ clientId: string;
126
+ isVideoEnabled: boolean;
127
+ }
128
+
129
+ interface ClientMetadataReceivedEvent {
130
+ type: string;
131
+ payload: { clientId: string; displayName: string };
132
+ }
133
+
134
+ interface SignalEvents {
135
+ audio_enabled: AudioEnabledEvent;
136
+ client_left: ClientLeftEvent;
137
+ client_metadata_received: ClientMetadataReceivedEvent;
138
+ connect: void;
139
+ device_identified: void;
140
+ new_client: NewClientEvent;
141
+ room_joined: RoomJoinedEvent;
142
+ room_left: void;
143
+ video_enabled: VideoEnabledEvent;
144
+ }
145
+
146
+ interface IdentifyDeviceRequest {
147
+ deviceCredentials: Credentials;
148
+ }
149
+
150
+ interface JoinRoomRequest {
151
+ config: { isAudioEnabled: boolean; isVideoEnabled: boolean };
152
+ organizationId: string;
153
+ roomName: string;
154
+ displayName?: string;
155
+ }
156
+
157
+ interface SignalRequests {
158
+ enable_audio: { enabled: boolean };
159
+ enable_video: { enabled: boolean };
160
+ identify_device: IdentifyDeviceRequest;
161
+ join_room: JoinRoomRequest;
162
+ leave_room: void;
163
+ send_client_metadata: { type: string; payload: { displayName?: string } };
164
+ }
165
+
166
+ export default class ServerSocket {
167
+ constructor(host: string, conf?: SocketConf);
168
+
169
+ connect(): void;
170
+ disconnect(): void;
171
+ emit<K extends keyof SignalRequests>(eventName: K, payload?: SignalRequests[k]);
172
+ on<K extends keyof SignalEvents>(eventName: K, callback: (args: SignalEvents[K]) => void);
173
+ once<K extends keyof SignalEvents>(eventName: K, callback: (args: SignalEvents[K]) => void);
174
+ }
175
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es6",
4
+ "downlevelIteration": true,
5
+ "importHelpers": true,
6
+ "lib": ["dom", "dom.iterable", "esnext", "es2015.collection", "es2015.iterable"],
7
+ "allowJs": true,
8
+ "checkJs": false,
9
+ "skipLibCheck": true,
10
+ "esModuleInterop": true,
11
+ "allowSyntheticDefaultImports": true,
12
+ "strict": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "noFallthroughCasesInSwitch": true,
15
+ "module": "esnext",
16
+ "moduleResolution": "node",
17
+ "resolveJsonModule": true,
18
+ "isolatedModules": true,
19
+ "noEmit": true,
20
+ "declaration": true,
21
+ "jsx": "react",
22
+ "baseUrl": "./",
23
+ "paths": {
24
+ "~/*": ["src/*"],
25
+ "@tests/*": ["tests/*"],
26
+ "@types": ["src/__types__/index.ts"]
27
+ }
28
+ },
29
+ "include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts", "tests/**/*.tsx"]
30
+ }
@@ -1,105 +0,0 @@
1
- import { html } from "lit-html";
2
- import "./lib";
3
-
4
- export default {
5
- title: "Examples/<whereby-embed>",
6
- argTypes: {
7
- audio: { control: "boolean" },
8
- avatarUrl: { control: "text", description: "Image url to use for avatar" },
9
- background: { control: "boolean" },
10
- cameraAccess: { control: "boolean" },
11
- chat: { control: "boolean" },
12
- displayName: { control: "text", description: "The name to use for the local participant" },
13
- embed: { control: "boolean" },
14
- emptyRoomInvitation: { control: "boolean" },
15
- floatSelf: { control: "boolean" },
16
- help: { control: "boolean" },
17
- leaveButton: { control: "boolean" },
18
- locking: { control: "boolean" },
19
- logo: { control: "boolean" },
20
- people: { control: "boolean" },
21
- precallReview: { control: "boolean" },
22
- recording: { control: "boolean" },
23
- room: { control: "text" },
24
- screenshare: { control: "boolean" },
25
- topToolbar: { control: "boolean" },
26
- video: { control: "boolean" },
27
- virtualBackgroundUrl: { control: "text", description: "Image url to use for virtual background" },
28
- },
29
- };
30
-
31
- const offOn = (arg) => (arg ? "on" : "off");
32
-
33
- const WherebyEmbed = ({
34
- audio,
35
- avatarUrl,
36
- background,
37
- cameraAccess,
38
- chat,
39
- displayName,
40
- emptyRoomInvitation,
41
- floatSelf,
42
- help,
43
- leaveButton,
44
- logo,
45
- people,
46
- precallReview,
47
- recording,
48
- room,
49
- screenshare,
50
- video,
51
- virtualBackgroundUrl,
52
- }) => {
53
- return html`<whereby-embed
54
- audio=${offOn(audio)}
55
- avatarUrl=${avatarUrl}
56
- background=${offOn(background)}
57
- cameraAccess=${offOn(cameraAccess)}
58
- chat=${offOn(chat)}
59
- displayName=${displayName}
60
- emptyRoomInvitation=${emptyRoomInvitation}
61
- floatSelf=${offOn(floatSelf)}
62
- help=${offOn(help)}
63
- leaveButton=${offOn(leaveButton)}
64
- logo=${offOn(logo)}
65
- people=${offOn(people)}
66
- precallReview=${offOn(precallReview)}
67
- recording=${offOn(recording)}
68
- screenshare=${offOn(screenshare)}
69
- video=${offOn(video)}
70
- virtualBackgroundUrl=${virtualBackgroundUrl}
71
- room="${room}"
72
- style="height: 100vh"
73
- />`;
74
- };
75
-
76
- const Template = (args) => WherebyEmbed(args);
77
- export const Primary = Template.bind({});
78
-
79
- Primary.args = {
80
- audio: true,
81
- avatarUrl: "",
82
- background: true,
83
- cameraAccess: true,
84
- chat: true,
85
- displayName: "Your name",
86
- emptyRoomInvitation: true,
87
- floatSelf: false,
88
- help: true,
89
- leaveButton: true,
90
- logo: true,
91
- people: true,
92
- precallReview: true,
93
- room: process.env.STORYBOOK_ROOM,
94
- screenshare: true,
95
- video: true,
96
- virtualBackgroundUrl: "",
97
- };
98
-
99
- Primary.parameters = {
100
- docs: {
101
- transformSource: (src) => {
102
- return (src || "").replace(/><iframe(.+)$/, " />");
103
- },
104
- },
105
- };