Bladeren bron

Providing the example with Jitsi app (default config)

develop
Kyrylo Stepanov 2 jaren geleden
bovenliggende
commit
0d1a8b2655

+ 0
- 43
example/assets/index.html Bestand weergeven

@@ -1,43 +0,0 @@
1
-<!DOCTYPE html>
2
-<html>
3
-
4
-<head>
5
-    <meta charset="utf-8" />
6
-    <title>Demo Application</title>
7
-</head>
8
-
9
-<body>
10
-    <div class="container">
11
-        <div class="sign-in">
12
-            <label for="account">Account: </label>
13
-            <input type="text" id="account_id" name="account">
14
-            <button id="login_id">Login</button>
15
-        </div>
16
-
17
-        <div>
18
-            <label for="login_id">Init meeting with participants:</label>
19
-            <input type="text" id="inp_participants" name="meeting">
20
-            <button id="start_meeting_id">Start Meeting</button>
21
-        </div>
22
-
23
-        <div>
24
-            <label>
25
-                Meeting ID: <span id="meeting_id_label">...</span>
26
-            </label>
27
-        </div>
28
-
29
-        <div>
30
-            <label for="meeting_id">Meeting ID: </label>
31
-            <input type="text" id="inp_meeting_id" name="meeting">
32
-            <button id="join_meeting_id">Join Meeting</button>
33
-        </div>
34
-
35
-        <div>
36
-            <label>Secret: <span id="secret_id_label">...</span></label>
37
-        </div>
38
-    </div>
39
-</body>
40
-
41
-</html>
42
-
43
-<script src="main.js"></script>

+ 0
- 54
example/assets/style.scss Bestand weergeven

@@ -1,54 +0,0 @@
1
-body {
2
-    font-family: "Haas Grot Text R Web", "Helvetica Neue", Helvetica, Arial, sans-serif;
3
-    background: rgb(102, 69, 142);
4
-    color: white;
5
-}
6
-
7
-.container {
8
-    display: flex;
9
-    justify-content: center;
10
-    align-items: center;
11
-    text-align: center;
12
-    min-height: 90vh;
13
-    flex-direction: column;
14
-
15
-    div {
16
-        margin: 5px;
17
-        width: 1500px;
18
-        display: flex;
19
-        align-items: center;
20
-        width: 40em;
21
-
22
-        label, button {
23
-            margin-right: 5px;
24
-            margin-left: 5px;
25
-            span {
26
-                margin-left: 5px;
27
-            }
28
-        }
29
-    }
30
-}
31
-
32
-
33
-/* CSS */
34
-button {
35
-  background-color: #8b186f;
36
-  border-radius: 8px;
37
-  border-style: none;
38
-  box-sizing: border-box;
39
-  color: #FFFFFF;
40
-  cursor: pointer;
41
-  display: inline-block;
42
-  list-style: none;
43
-  margin: 0;
44
-  outline: none;
45
-  padding: 10px 16px;
46
-  position: relative;
47
-  text-align: center;
48
-  text-decoration: none;
49
-  transition: color 100ms;
50
-  vertical-align: baseline;
51
-  user-select: none;
52
-  -webkit-user-select: none;
53
-  touch-action: manipulation;
54
-}

+ 26645
- 6144
example/package-lock.json
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 7
- 1
example/package.json Bestand weergeven

@@ -13,6 +13,8 @@
13 13
   "license": "ISC",
14 14
   "devDependencies": {
15 15
     "@types/lodash": "^4.14.184",
16
+    "@types/react": "^18.0.25",
17
+    "@types/react-dom": "^18.0.8",
16 18
     "@wasm-tool/wasm-pack-plugin": "^1.6.0",
17 19
     "copy-webpack-plugin": "^11.0.0",
18 20
     "css-loader": "^6.7.1",
@@ -28,9 +30,13 @@
28 30
     "webpack-dev-server": "^4.10.0"
29 31
   },
30 32
   "dependencies": {
33
+    "@jitsi/react-sdk": "^1.3.0",
31 34
     "assert": "^2.0.0",
32 35
     "buffer": "^6.0.3",
33 36
     "lodash": "^4.17.21",
34
-    "near-api-js": "^0.45.1"
37
+    "near-api-js": "^0.45.1",
38
+    "react": "^18.2.0",
39
+    "react-dom": "^18.2.0",
40
+    "react-scripts": "5.0.1"
35 41
   }
36 42
 }

+ 27
- 0
example/public/index.html Bestand weergeven

@@ -0,0 +1,27 @@
1
+<!DOCTYPE html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="utf-8" />
5
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+    <meta name="theme-color" content="#000000" />
7
+    <meta
8
+      name="description"
9
+      content="Web site created using create-react-app"
10
+    />
11
+    <title>React App</title>
12
+  </head>
13
+  <body>
14
+    <noscript>You need to enable JavaScript to run this app.</noscript>
15
+    <div id="root"></div>
16
+    <!--
17
+      This HTML file is a template.
18
+      If you open it directly in the browser, you will see an empty page.
19
+
20
+      You can add webfonts, meta tags, or analytics to this file.
21
+      The build step will place the bundled scripts into the <body> tag.
22
+
23
+      To begin the development, run `npm start` or `yarn start`.
24
+      To create a production bundle, use `npm run build` or `yarn build`.
25
+    -->
26
+  </body>
27
+</html>

+ 4
- 0
example/src/App.css Bestand weergeven

@@ -0,0 +1,4 @@
1
+.App {
2
+  font-family: sans-serif;
3
+  text-align: center;
4
+}

+ 52
- 0
example/src/App.tsx Bestand weergeven

@@ -0,0 +1,52 @@
1
+import React from 'react';
2
+import './App.css';
3
+
4
+import Meeting from './components/Meeting'
5
+import Wallet from './components/Wallet';
6
+
7
+interface Props {
8
+}
9
+
10
+interface State {
11
+  secretKey: string,
12
+  meetingId: string
13
+}
14
+
15
+class App extends React.Component<Props, State> {
16
+  constructor(props: Props) {
17
+    super(props);
18
+    this.setSecretKey = this.setSecretKey.bind(this);
19
+    this.setMeetingId = this.setMeetingId.bind(this);
20
+
21
+    this.state = {
22
+      secretKey: "",
23
+      meetingId: ""
24
+    }
25
+  }
26
+
27
+  setSecretKey(secretKey: string) {
28
+    this.setState({ secretKey });
29
+  };
30
+  setMeetingId(meetingId: string) {
31
+    this.setState({ meetingId });
32
+  };
33
+
34
+
35
+  render() {
36
+    let isSecretKeySet = this.state.secretKey.length > 0 && this.state.meetingId.length > 0;
37
+    let currentWindow;
38
+    if (isSecretKeySet) {
39
+      currentWindow = <Meeting secretKeyBase64={this.state.secretKey} meetingId={this.state.meetingId} />;
40
+    } else {
41
+      currentWindow = <Wallet setSecretKey={this.setSecretKey} setMeetingId={this.setMeetingId} />;
42
+    }
43
+
44
+    return (
45
+      <div>
46
+        {currentWindow}
47
+      </div>
48
+    );
49
+  }
50
+}
51
+
52
+export default App;

+ 57
- 0
example/src/components/Meeting.tsx Bestand weergeven

@@ -0,0 +1,57 @@
1
+import React, { PropsWithChildren } from 'react'
2
+import { JitsiMeeting } from "@jitsi/react-sdk";
3
+
4
+interface Props {
5
+    secretKeyBase64: string,
6
+    meetingId: string
7
+}
8
+interface State {
9
+}
10
+
11
+class Meeting extends React.Component<Props, State> {
12
+    render() {
13
+        return(
14
+            <JitsiMeeting
15
+                roomName={this.props.meetingId}
16
+                getIFrameRef = { handleJitsiIFrameRef2 }
17
+                configOverwrite = {{
18
+                    e2ee: {
19
+                    externallyManagedKey: true
20
+                    }
21
+                }}
22
+                onApiReady = { externalApi => {
23
+                    externalApi.on("videoConferenceJoined", async (event) => {
24
+                        let secretKey: Uint8Array = new Uint8Array(Buffer.from(this.props.secretKeyBase64, 'base64'));
25
+
26
+                        let index = 0;
27
+                        externalApi.executeCommand('setMediaEncryptionKey', JSON.stringify({
28
+                            exportedKey: Array.from(secretKey),
29
+                            index
30
+                        }));
31
+
32
+                        externalApi.executeCommand("toggleE2EE", true);
33
+                        console.log('Jitsi Meet External API', externalApi);
34
+                    })
35
+                } }
36
+            />
37
+        )
38
+    }
39
+}
40
+
41
+const handleJitsiIFrameRef2 = (iframeRef: IIframeStyle) => {
42
+  iframeRef.style.marginTop = '10px';
43
+  iframeRef.style.border = '10px #df486f';
44
+  iframeRef.style.padding = '5px';
45
+  iframeRef.style.height = '50em';
46
+};
47
+
48
+interface IIframeStyle {
49
+  style: {
50
+    marginTop: string,
51
+    border: string,
52
+    padding: string,
53
+    height: string
54
+  }
55
+}
56
+
57
+export default Meeting;

+ 213
- 0
example/src/components/Wallet.tsx Bestand weergeven

@@ -0,0 +1,213 @@
1
+import React from "react";
2
+
3
+// import "../assets/style.scss";
4
+import * as _ from "lodash";
5
+import { connect, WalletConnection, keyStores, Near } from 'near-api-js';
6
+import { BrowserLocalStorageKeyStore } from "near-api-js/lib/key_stores";
7
+const { KeyProvisioner, ProvisionerConfig } = await import("../../../pkg/web_client");
8
+
9
+interface State {
10
+    isSignedIn: boolean,
11
+    keyStore: BrowserLocalStorageKeyStore,
12
+    accountId: string | null,
13
+    walletConnection: WalletConnection | null,
14
+    nearConnection: Near | null,
15
+    timeouts: {
16
+        default_ms: number,
17
+        interaction_ms: number
18
+    }
19
+    provisioner: any,
20
+    secretKey: string | null,
21
+    meetingId: string | null,
22
+    participants: string[],
23
+}
24
+
25
+interface Props {
26
+    setSecretKey: (key: string) => void,
27
+    setMeetingId: (id: string) => void
28
+}
29
+
30
+class Wallet extends React.Component<Props, State> {
31
+    constructor(props: Props) {
32
+        super(props);
33
+        this.state = {
34
+            isSignedIn: false,
35
+            keyStore: new keyStores.BrowserLocalStorageKeyStore(),
36
+            accountId: null,
37
+            walletConnection: null,
38
+            nearConnection: null,
39
+            timeouts: {
40
+                default_ms: 7000,
41
+                interaction_ms: 30_000
42
+            },
43
+            provisioner: null,
44
+            secretKey: null,
45
+            meetingId: null,
46
+            participants: [],
47
+        };
48
+    }
49
+
50
+    setStateAsync(state: State) {
51
+        return new Promise((resolve: any) => {
52
+            this.setState(state, resolve)
53
+        });
54
+    }
55
+
56
+    async componentDidMount(): Promise<void> {
57
+        let keyStore = this.state.keyStore;
58
+
59
+        let nearConnectionCall = async () => {
60
+            let headers: { [key: string]: (string | number) };
61
+            let connectionConfig = {
62
+                networkId: "testnet",
63
+                keyStore,
64
+                nodeUrl: "https://rpc.testnet.near.org",
65
+                walletUrl: "https://wallet.testnet.near.org",
66
+                helperUrl: "https://helper.testnet.near.org",
67
+                explorerUrl: "https://explorer.testnet.near.org",
68
+                headers
69
+            };
70
+
71
+            let nearConnection = await connect(connectionConfig);
72
+            return nearConnection;
73
+        };
74
+
75
+        let nearConnection = await nearConnectionCall();
76
+        let walletConnection = new WalletConnection(nearConnection, null);
77
+
78
+        await this.setStateAsync({ ...this.state, keyStore, nearConnection, walletConnection, accountId: walletConnection.getAccountId() })
79
+
80
+        if (this.state.walletConnection.isSignedIn()) {
81
+            await this.setStateAsync({ ...this.state, isSignedIn: true });
82
+            await this.handleWallet();
83
+        }
84
+    }
85
+
86
+    async requestSignIn() {
87
+        if (!this.state.walletConnection.isSignedIn()) {
88
+            await this.state.walletConnection.requestSignIn(this.state.accountId, "Test App");
89
+        }
90
+    }
91
+
92
+    async handleWallet() {
93
+        let accountId: string = this.state.accountId;
94
+        let account = await this.state.nearConnection.account(accountId);
95
+        let accessKeys = await account.getAccessKeys();
96
+        let keypair = await this.state.keyStore.getKey("testnet", accountId);
97
+
98
+        let found = accessKeys.find(key => key.public_key == keypair.getPublicKey().toString());
99
+
100
+        let config = new ProvisionerConfig("relayz-meet.testnet", "https://rpc.testnet.near.org", "https://ks.relayz.io:3000");
101
+
102
+        await this.setStateAsync({
103
+            ...this.state,
104
+            provisioner: new KeyProvisioner(keypair.toString(), BigInt(found.access_key.nonce), accountId, config)
105
+        });
106
+
107
+        console.log("KeyPair: ", keypair.getPublicKey().toString());
108
+        console.log("Account: ", account);
109
+    }
110
+
111
+    async handleMeetingStart() {
112
+        let participants = this.state.participants;
113
+        let provisioner = this.state.provisioner;
114
+
115
+        if (participants.length > 0) {
116
+            console.log("Participants: " + participants);
117
+            provisioner.init_meeting(new Set(participants), this.state.timeouts.default_ms)
118
+                .then((meetingId: string) => {
119
+                    this.props.setMeetingId(meetingId);
120
+                    this.setState({ ...this.state, meetingId });
121
+                    console.log("Meeting Id:", meetingId);
122
+                    provisioner.send_keys(meetingId, this.state.timeouts.interaction_ms)
123
+                        .then((secretKey: string) => {
124
+                            this.setState({ ...this.state, secretKey });
125
+                            this.props.setSecretKey(secretKey);
126
+                            console.log("Secret Key" + secretKey);
127
+                        })
128
+                        .then(() => console.log("Send keys is ok"))
129
+                        .catch((err: any) => {
130
+                            console.log("Error: " + err.err_msg());
131
+                        });
132
+                })
133
+                .then(() => console.log("Init is ok"))
134
+                .catch((err: any) => {
135
+                    console.log("Error: " + err.err_msg());
136
+                });
137
+        }
138
+    }
139
+
140
+    async handleMeetingJoin() {
141
+        if (this.state.meetingId.length == null) {
142
+            return
143
+        }
144
+        let meetingId = this.state.meetingId;
145
+        let provisioner = this.state.provisioner;
146
+        let default_ms = this.state.timeouts.default_ms;
147
+        this.props.setMeetingId(meetingId);
148
+
149
+        provisioner.get_key(meetingId, default_ms).then((secretKey: any) => {
150
+            this.props.setSecretKey(secretKey);
151
+            this.setState({ ...this.state, secretKey });
152
+            console.log("Secret Key" + secretKey);
153
+        }).catch((err: any) => {
154
+            console.log("ErrorType: " + ErrorType[err.err_type()]);
155
+            console.log("Error: " + err.err_msg());
156
+        });
157
+    }
158
+
159
+    render() {
160
+        return (
161
+            <div className="container">
162
+                <div className="moderator-part">
163
+                    <label>Moderator part: </label>
164
+                    <div className="sign-in">
165
+                        <label>Account: </label>
166
+                        <input
167
+                            type="text"
168
+                            disabled={this.state.isSignedIn}
169
+                            onChange={e => this.setState({ accountId: e.target.value })}
170
+                            defaultValue={this.state.accountId}
171
+                        />
172
+                        <button disabled={this.state.isSignedIn} onClick={() => this.requestSignIn()}>
173
+                            Login
174
+                        </button>
175
+                    </div>
176
+
177
+                    <div>
178
+                        <label>Init meeting with participants:</label>
179
+                        <input
180
+                            type="text"
181
+                            onBlur={e => this.setState({ participants: e.target.value.split(',').map((id) => id.trim()) })}
182
+                        />
183
+                        <button onClick={() => this.handleMeetingStart()}>Start Meeting</button>
184
+                    </div>
185
+
186
+                    <div>
187
+                        <label>
188
+                            Meeting ID: {this.state.meetingId}
189
+                        </label>
190
+                    </div>
191
+                </div>
192
+
193
+                <div className="end-user-part">
194
+                    <label>End-user part: </label>
195
+                    <div>
196
+                        <label>Meeting ID: </label>
197
+                        <input
198
+                            type="text" id="inp_meeting_id" name="meeting"
199
+                            onBlur={e => this.setState({ meetingId: e.target.value })}
200
+                        />
201
+                        <button id="join_meeting_id" onClick={() => this.handleMeetingJoin()}>Join Meeting</button>
202
+                    </div>
203
+
204
+                    <div>
205
+                        <label>Secret: {this.state.secretKey}</label>
206
+                    </div>
207
+                </div>
208
+            </div>
209
+        );
210
+    }
211
+}
212
+
213
+export default Wallet;

+ 13
- 0
example/src/index.css Bestand weergeven

@@ -0,0 +1,13 @@
1
+body {
2
+  margin: 0;
3
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4
+    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5
+    sans-serif;
6
+  -webkit-font-smoothing: antialiased;
7
+  -moz-osx-font-smoothing: grayscale;
8
+}
9
+
10
+code {
11
+  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12
+    monospace;
13
+}

+ 0
- 100
example/src/index.ts Bestand weergeven

@@ -1,100 +0,0 @@
1
-import "../assets/style.scss";
2
-import * as _ from "lodash";
3
-import { connect, WalletConnection, keyStores, Near } from 'near-api-js';
4
-import { BrowserLocalStorageKeyStore } from "near-api-js/lib/key_stores";
5
-const { KeyProvisioner, ProvisionerConfig } = await import("../../pkg/web_client");
6
-
7
-async function ui() {
8
-    let keyStore = new keyStores.BrowserLocalStorageKeyStore();
9
-
10
-    let near_connection_call = async () => {
11
-        let headers: { [key: string]: (string | number) }
12
-        const connectionConfig = {
13
-            networkId: "testnet",
14
-            keyStore,
15
-            nodeUrl: "https://rpc.testnet.near.org",
16
-            walletUrl: "https://wallet.testnet.near.org",
17
-            helperUrl: "https://helper.testnet.near.org",
18
-            explorerUrl: "https://explorer.testnet.near.org",
19
-            headers
20
-        };
21
-
22
-        let nearConnection = await connect(connectionConfig);
23
-        return nearConnection;
24
-    };
25
-
26
-    let near_connection = await near_connection_call();
27
-    let wallet_connection = new WalletConnection(near_connection, null);
28
-    let account_id_element = document.getElementById("account_id") as HTMLInputElement | null;
29
-
30
-    document.getElementById("login_id").addEventListener("click", () => {
31
-        if (!wallet_connection.isSignedIn() && account_id_element?.value.length > 0) {
32
-            wallet_connection.requestSignIn(account_id_element?.value, "Sample App");
33
-        }
34
-    });
35
-
36
-    if (wallet_connection.isSignedIn()) {
37
-        let account_id = wallet_connection.getAccountId();
38
-        account_id_element.value = account_id.toString();
39
-        await handle_wallet(wallet_connection, near_connection, keyStore);
40
-    }
41
-
42
-}
43
-
44
-async function handle_wallet(wallet_connection: WalletConnection, near_connection: Near, keyStore: BrowserLocalStorageKeyStore) {
45
-    let account_id = wallet_connection.getAccountId();
46
-    let account = await near_connection.account(account_id.toString());
47
-    let accessKeys = await account.getAccessKeys();
48
-    let keypair = await keyStore.getKey("testnet", account_id.toString());
49
-    let found = accessKeys.find(key => key.public_key.toString() == keypair.getPublicKey().toString());
50
-
51
-    let config = new ProvisionerConfig("relayz-meet.testnet", "https://rpc.testnet.near.org", "http://servtest.relayz.io:3000");
52
-    let provisioner = new KeyProvisioner(keypair.toString(), BigInt(found.access_key.nonce), account_id.toString(), config);
53
-
54
-    console.log("KeyPair: ", keypair.getPublicKey().toString());
55
-    console.log("Account: ", account);
56
-
57
-    let timeout_ms: number = 7000;
58
-    // increase timeout to allow a moderator to add a user
59
-    let interaction_timeout_ms: number = 30000;
60
-
61
-    document.getElementById("start_meeting_id").addEventListener("click", () => {
62
-        let participants = (document.getElementById("inp_participants") as HTMLInputElement | null)?.value;
63
-        if (participants.length > 0) {
64
-            console.log("Participants: " + participants);
65
-            provisioner.init_meeting(new Set(participants.split(",")), timeout_ms)
66
-                .then((meeting) => {
67
-                    console.log("Meeting Id:", meeting.meet_id);
68
-                    console.log("Transaction Id:", meeting.transaction_id);
69
-                    (document.getElementById("meeting_id_label") as HTMLSpanElement).textContent = meeting.meet_id;
70
-                    provisioner.send_keys(meeting.meet_id, interaction_timeout_ms)
71
-                        .then((secret_key) => {
72
-                            (document.getElementById("secret_id_label") as HTMLSpanElement).textContent = secret_key;
73
-                            console.log("Secret Key" + secret_key);
74
-                        })
75
-                        .then(() => console.log("Send keys is ok"))
76
-                        .catch((err) => {
77
-                            console.log("Error: ", err);
78
-                        });
79
-                })
80
-                .then(() => console.log("Init is ok"))
81
-                .catch((err) => {
82
-                    console.log("Error: ", err);
83
-                });
84
-        }
85
-    });
86
-
87
-    document.getElementById("join_meeting_id").addEventListener("click", () => {
88
-        let meeting_id = (document.getElementById("inp_meeting_id") as HTMLInputElement | null)?.value;
89
-        if (meeting_id.length > 0) {
90
-            provisioner.get_key(meeting_id, timeout_ms).then((secret_key) => {
91
-                (document.getElementById("secret_id_label") as HTMLSpanElement).textContent = secret_key;
92
-                console.log("Secret Key" + secret_key);
93
-            }).catch((err) => {
94
-                console.log("Error: ", err);
95
-            });
96
-        }
97
-    });
98
-}
99
-
100
-ui();

+ 13
- 0
example/src/index.tsx Bestand weergeven

@@ -0,0 +1,13 @@
1
+import React from 'react';
2
+import ReactDOM from 'react-dom/client';
3
+import './index.css';
4
+import App from './App';
5
+
6
+const root = ReactDOM.createRoot(
7
+  document.getElementById('root') as HTMLElement
8
+);
9
+root.render(
10
+  <React.StrictMode>
11
+    <App />
12
+  </React.StrictMode>
13
+);

+ 4
- 7
example/webpack.config.js Bestand weergeven

@@ -3,8 +3,7 @@ const webpack = require('webpack');
3 3
 const HtmlWebpackPlugin = require('html-webpack-plugin');
4 4
 
5 5
 module.exports = {
6
-    // entry: './src/index.js',
7
-    entry: './src/index.ts',
6
+    entry: './src/index.tsx',
8 7
     output: {
9 8
         filename: 'main.js',
10 9
         path: path.resolve(__dirname, 'dist'),
@@ -45,8 +44,8 @@ module.exports = {
45 44
                 exclude: /node_modules/,
46 45
             },
47 46
             {
48
-                test: /\.s[ac]ss$/i,
49
-                use: ["style-loader", "css-loader", "sass-loader"],
47
+                test: /\.css?$/,
48
+                use: 'css-loader',
50 49
             },
51 50
         ],
52 51
     },
@@ -55,9 +54,7 @@ module.exports = {
55 54
             Buffer: ['buffer', 'Buffer'],
56 55
         }),
57 56
         new HtmlWebpackPlugin({
58
-            title: 'My App',
59
-            template: 'assets/index.html',
60
-            inject: false
57
+            template: 'public/index.html',
61 58
         })
62 59
     ],
63 60
     experiments: {

Laden…
Annuleren
Opslaan