Преглед на файлове

Merge pull request #77 from Relayz-io/kyrylo/jitsi-api-integration

kyrylo/jitsi-api-integration
develop
Kyrylo Stepanov преди 2 години
родител
ревизия
7971f6dc76
No account linked to committer's email address

+ 11
- 0
example/.babelrc Целия файл

@@ -0,0 +1,11 @@
1
+{
2
+    "presets": [
3
+        "@babel/preset-env",
4
+        [
5
+            "@babel/preset-react",
6
+            {
7
+                "runtime": "automatic"
8
+            }
9
+        ]
10
+    ]
11
+}

+ 0
- 43
example/assets/index.html Целия файл

@@ -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 Целия файл

@@ -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
-}

+ 14589
- 6695
example/package-lock.json
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 12
- 3
example/package.json Целия файл

@@ -3,6 +3,7 @@
3 3
   "version": "1.0.0",
4 4
   "description": "",
5 5
   "private": true,
6
+  "main": "index.js",
6 7
   "scripts": {
7 8
     "test": "echo \"Error: no test specified\" && exit 1",
8 9
     "build": "webpack",
@@ -12,8 +13,14 @@
12 13
   "author": "",
13 14
   "license": "ISC",
14 15
   "devDependencies": {
16
+    "@babel/core": "^7.20.5",
17
+    "@babel/preset-env": "^7.20.2",
18
+    "@babel/preset-react": "^7.18.6",
15 19
     "@types/lodash": "^4.14.184",
20
+    "@types/react": "^18.0.25",
21
+    "@types/react-dom": "^18.0.8",
16 22
     "@wasm-tool/wasm-pack-plugin": "^1.6.0",
23
+    "babel-loader": "^9.1.0",
17 24
     "copy-webpack-plugin": "^11.0.0",
18 25
     "css-loader": "^6.7.1",
19 26
     "html-webpack-plugin": "^5.5.0",
@@ -21,16 +28,18 @@
21 28
     "sass": "^1.55.0",
22 29
     "sass-loader": "^13.0.2",
23 30
     "style-loader": "^3.3.1",
24
-    "ts-loader": "^9.3.1",
25
-    "typescript": "^4.8.2",
26 31
     "webpack": "^5.74.0",
27 32
     "webpack-cli": "^4.10.0",
28 33
     "webpack-dev-server": "^4.10.0"
29 34
   },
30 35
   "dependencies": {
36
+    "@jitsi/react-sdk": "^1.3.0",
31 37
     "assert": "^2.0.0",
32 38
     "buffer": "^6.0.3",
33 39
     "lodash": "^4.17.21",
34
-    "near-api-js": "^0.45.1"
40
+    "near-api-js": "^0.45.1",
41
+    "react": "^18.2.0",
42
+    "react-dom": "^18.2.0",
43
+    "react-scripts": "5.0.1"
35 44
   }
36 45
 }

+ 27
- 0
example/public/index.html Целия файл

@@ -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 Целия файл

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

+ 44
- 0
example/src/App.jsx Целия файл

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

+ 54
- 0
example/src/components/Meeting.jsx Целия файл

@@ -0,0 +1,54 @@
1
+import React, { PropsWithChildren } from 'react'
2
+import { JitsiMeeting } from "@jitsi/react-sdk";
3
+
4
+class Meeting extends React.Component {
5
+    render() {
6
+        return (
7
+            <JitsiMeeting
8
+                roomName={this.props.meetingId}
9
+                getIFrameRef={handleJitsiIFrameRef2}
10
+                configOverwrite={{
11
+                    e2ee: {
12
+                        externallyManagedKey: true
13
+                    }
14
+                }}
15
+                onApiReady={externalApi => {
16
+                    externalApi.on("videoConferenceJoined", async (event) => {
17
+                        let secretKey = new Uint8Array(Buffer.from(this.props.secretKeyBase64, 'base64'));
18
+                        let index = 0;
19
+
20
+                        let key = await window.crypto.subtle.importKey(
21
+                            "raw",
22
+                            secretKey,
23
+                            "AES-GCM",
24
+                            true,
25
+                            ["encrypt", "decrypt"]
26
+                        );
27
+
28
+                        await externalApi.setMediaEncryptionKey({
29
+                            key,
30
+                            index
31
+                        }).then(() => {
32
+                            console.log("SET MEDIA ENCRYPTION KEY");
33
+                        });
34
+
35
+                        setTimeout(() => {
36
+                            externalApi.toggleE2EE(true);
37
+                        }, 1_000);
38
+
39
+                        console.log('Jitsi Meet External API', externalApi);
40
+                    })
41
+                }}
42
+            />
43
+        )
44
+    }
45
+}
46
+
47
+const handleJitsiIFrameRef2 = (iframeRef) => {
48
+    iframeRef.style.marginTop = '10px';
49
+    iframeRef.style.border = '10px #df486f';
50
+    iframeRef.style.padding = '5px';
51
+    iframeRef.style.height = '50em';
52
+};
53
+
54
+export default Meeting;

+ 190
- 0
example/src/components/Wallet.jsx Целия файл

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

+ 13
- 0
example/src/index.css Целия файл

@@ -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
+}

+ 13
- 0
example/src/index.js Целия файл

@@ -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.jsx';
5
+
6
+const root = ReactDOM.createRoot(
7
+  document.getElementById('root')
8
+);
9
+root.render(
10
+  <React.StrictMode>
11
+    <App />
12
+  </React.StrictMode>
13
+);

+ 0
- 100
example/src/index.ts Целия файл

@@ -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();

+ 0
- 22
example/tsconfig.json Целия файл

@@ -1,22 +0,0 @@
1
-{
2
-    "compilerOptions": {
3
-        "outDir": "dist/",
4
-        "noImplicitAny": true,
5
-        "sourceMap": true,
6
-        "jsx": "react",
7
-        "allowJs": true,
8
-        "moduleResolution": "node",
9
-        "module": "es2022",
10
-        "lib": [
11
-            "es2022", "DOM", "DOM.Iterable"
12
-        ],
13
-        "target": "es2022",
14
-        "preserveConstEnums": true,
15
-        "esModuleInterop": true,
16
-        "paths": {
17
-            "client/*": [
18
-                "../pkg/*"
19
-            ]
20
-        }
21
-    }
22
-}

+ 13
- 16
example/webpack.config.js Целия файл

@@ -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: path.join(__dirname, "src", "index.js"),
8 7
     output: {
9 8
         filename: 'main.js',
10 9
         path: path.resolve(__dirname, 'dist'),
@@ -12,11 +11,14 @@ module.exports = {
12 11
     },
13 12
     resolve: {
14 13
         alias: {
15
-            'client': path.join(__dirname, '../pkg/')
14
+            'client': path.join(__dirname, '../pkg/'),
15
+            'react-dom': path.resolve('./node_modules/react-dom'),
16
+            react: path.resolve('./node_modules/react')
16 17
         },
17 18
         fallback: {
18
-            "buffer": require.resolve("buffer")
19
-        }
19
+            "buffer": require.resolve("buffer"),
20
+        },
21
+        extensions: ['*', '.js', '.jsx'],
20 22
     },
21 23
     mode: "development",
22 24
     devServer: {
@@ -34,19 +36,16 @@ module.exports = {
34 36
             }
35 37
         },
36 38
     },
37
-    resolve: {
38
-        extensions: ['.tsx', '.ts', '.js'],
39
-    },
40 39
     module: {
41 40
         rules: [
42 41
             {
43
-                test: /\.tsx?$/,
44
-                use: 'ts-loader',
42
+                test: /\.(js|jsx)$/,
45 43
                 exclude: /node_modules/,
44
+                use: ["babel-loader"],
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,10 +54,8 @@ 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
61
-        })
57
+            template: path.join(__dirname, "public", "index.html"),
58
+        }),
62 59
     ],
63 60
     experiments: {
64 61
         asyncWebAssembly: true,

Loading…
Отказ
Запис