diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index f4540344..00000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1 +0,0 @@
-custom: ['https://www.buymeacoffee.com/AkashHamirwasia']
diff --git a/README.md b/README.md
index a7e116ce..d7efddb4 100644
--- a/README.md
+++ b/README.md
@@ -140,8 +140,8 @@ Blaze app can now be accessed at port `8080` :tada:
## Privacy and Analytics
- Blaze server does not track or record the files that are being shared both by WebSockets and WebTorrent.
- Any user related data like nickname, room names are always stored on device, and are only shared with the server when the user joins a room for file sharing.
-- Blaze client uses Google Analytics to record the following:
- - [Basic visit data](https://developers.google.com/analytics/devguides/collection/analyticsjs#what_data_does_the_google_analytics_tag_capture) as recorded by [Google Analytics](https://support.google.com/analytics/answer/6004245?ref_topic=2919631)
+- Blaze client uses Google Analytics 4 to record the following:
+ - Part of [Basic visit data](https://support.google.com/analytics/answer/9234069?hl=en) - page views, scrolls and outbound clicks, rest are disabled.
- If Blaze PWA is installed on the device, and whether files are shared using share targets.
## Contributing
diff --git a/client/package-lock.json b/client/package-lock.json
index e1610719..ed979358 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -13,7 +13,6 @@
"classnames": "^2.3.2",
"copy-to-clipboard": "^3.3.3",
"date-fns": "^2.29.3",
- "downloadjs": "^1.4.7",
"nanoid": "^3.3.4",
"preact": "^10.3.2",
"preact-feather": "^4.2.1",
@@ -6373,11 +6372,6 @@
"node": ">=12"
}
},
- "node_modules/downloadjs": {
- "version": "1.4.7",
- "resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz",
- "integrity": "sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw="
- },
"node_modules/duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
diff --git a/client/package.json b/client/package.json
index e7c9b843..dffc8e21 100644
--- a/client/package.json
+++ b/client/package.json
@@ -23,7 +23,6 @@
"classnames": "^2.3.2",
"copy-to-clipboard": "^3.3.3",
"date-fns": "^2.29.3",
- "downloadjs": "^1.4.7",
"nanoid": "^3.3.4",
"preact": "^10.3.2",
"preact-feather": "^4.2.1",
diff --git a/client/src/routes/App/FileTransfer/FileTransfer.js b/client/src/routes/App/FileTransfer/FileTransfer.js
index 727aee96..b3006296 100644
--- a/client/src/routes/App/FileTransfer/FileTransfer.js
+++ b/client/src/routes/App/FileTransfer/FileTransfer.js
@@ -1,5 +1,4 @@
import { h, createRef } from 'preact';
-import download from 'downloadjs';
import { route } from 'preact-router';
import { PureComponent, forwardRef, memo } from 'preact/compat';
import { ArrowLeft, CheckCircle, Home, Plus, Image, Film, Box, Music, File, Zap, Share2, Send } from 'preact-feather';
@@ -17,6 +16,7 @@ import Visualizer from '../../../utils/visualizer';
import formatSize from '../../../utils/formatSize';
import pluralize from '../../../utils/pluralize';
import urls from '../../../utils/urls';
+import { multiDownload } from '../../../utils/download';
import constants from '../../../../../common/constants';
import roomsDispatch from '../../../reducers/rooms';
@@ -292,17 +292,10 @@ class FileTransfer extends PureComponent {
isSelectorEnabled: false,
});
},
- onDone: (file, meta) => {
- if (file !== undefined) {
- if (Array.isArray(file)) {
- file.forEach(file => {
- file.getBlob((err, blob) => download(blob, file.name));
- });
- }
- else {
- download(file, meta.name, meta.type);
- }
- }
+ onDone: (files) => {
+ if (files === undefined) return;
+
+ multiDownload(files);
this.resetState();
},
});
diff --git a/client/src/routes/App/layouts/AppLanding/AppLanding.js b/client/src/routes/App/layouts/AppLanding/AppLanding.js
index 9d8e7f08..63638a69 100644
--- a/client/src/routes/App/layouts/AppLanding/AppLanding.js
+++ b/client/src/routes/App/layouts/AppLanding/AppLanding.js
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { memo } from 'preact/compat';
-import { ArrowDownCircle, Gift, Grid, PieChart, Settings } from 'preact-feather';
+import { ArrowDownCircle, Gift, Grid, Settings } from 'preact-feather';
import { Link } from 'preact-router/match';
import { useContext } from 'preact/hooks';
import { PWAInstall } from '../../contexts/PWAInstall';
@@ -36,9 +36,7 @@ function AppLanding({ children, title, subtitle }) {
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
- window.ga('send', 'event', {
- eventCategory: 'pwa-install',
- eventAction: 'promo-shown',
+ window.gtag('event', 'pwa-install-prompt', {
outcome,
});
};
diff --git a/client/src/template.html b/client/src/template.html
index 77dbe194..d1c0ff8e 100644
--- a/client/src/template.html
+++ b/client/src/template.html
@@ -35,13 +35,13 @@
+
-
<% preact.headEnd %>
diff --git a/client/src/utils/download.js b/client/src/utils/download.js
new file mode 100644
index 00000000..b02c3651
--- /dev/null
+++ b/client/src/utils/download.js
@@ -0,0 +1,41 @@
+// Adapted from https://github.com/sindresorhus/multi-download/blob/v4.0.0/index.js
+// to take File as input https://developer.mozilla.org/en-US/docs/Web/API/File
+
+/**
+ * Creates a promise that resolves after the specified number of milliseconds
+ */
+const delay = milliseconds => new Promise(resolve => {
+ setTimeout(resolve, milliseconds);
+});
+
+/**
+ * Downloads a single file
+ * @param file - An instance of the File type representing the file to download
+ */
+const download = async (file) => {
+ const a = document.createElement('a');
+ const url = URL.createObjectURL(file);
+ a.download = file.name;
+ a.href = url;
+ a.style.display = 'none';
+ document.body.append(a);
+ a.click();
+ await delay(100); // for Chrome
+ a.remove();
+ URL.revokeObjectURL(url);
+};
+
+/**
+ * Initiates multiple file downloads with a constant delay between each one
+ * @param files - An array of instances of the File type representing the files to download
+ */
+export const multiDownload = async (files) => {
+ if (!files) {
+ throw new Error('`files` required');
+ };
+
+ for (const file of files) {
+ download(file);
+ await delay(1000);
+ }
+};
diff --git a/client/src/utils/fileShare.js b/client/src/utils/fileShare.js
index 056a1971..acc5a90a 100644
--- a/client/src/utils/fileShare.js
+++ b/client/src/utils/fileShare.js
@@ -50,7 +50,9 @@ class FileShare {
this.socket.listen(constants.FILE_INIT, (data) => {
if (data.end) {
if (fileParts.length) {
- onDone(new Blob(fileParts), metaData.meta[0]);
+ onDone([
+ new File(fileParts, metaData.meta[0].name, {type: metaData.meta[0].type})
+ ]);
fileParts = [];
size = 0;
statProg = 0.25;
@@ -106,8 +108,20 @@ class FileShare {
torrent.on('upload', update);
torrent.on('download', update);
- torrent.on('done', () => {
- onDone(torrent.files);
+ torrent.on('done', async () => {
+ const files = await Promise.all(
+ torrent.files.map(
+ (file) =>
+ new Promise((resolve, reject) => {
+ // make regular File from webtorrent File https://github.com/webtorrent/webtorrent/blob/v1.9.7/lib/file.js#L13
+ file.getBlob((err, blob) => {
+ if (err) reject(err);
+ resolve(new File([blob], file.name, { type: file.type }));
+ });
+ })
+ )
+ );
+ onDone(files);
});
}
@@ -227,4 +241,4 @@ class FileShare {
}
-export default FileShare;
\ No newline at end of file
+export default FileShare;
diff --git a/docker-compose.yml b/docker-compose.yml
index 3700fa27..409efca1 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,7 +5,7 @@ services:
context: .
dockerfile: ./server/Dockerfile
environment:
- TRUST_PROXY: true
+ TRUST_PROXY: 1
ports:
- 3030:3030