diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 7185bddf..d0fb2a6e 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
-custom: https://www.paypal.com/donate/?cmd=_s-xclick&hosted_button_id=FTP9DXUR7LA7Q&source=url
+custom: https://www.paypal.com/donate/?hosted_button_id=MG8GV7YCYT352
diff --git a/README.md b/README.md
index 1806dd8f..ad520d99 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,21 @@
[Snapdrop](https://snapdrop.net): local file sharing in your browser. Inspired by Apple's Airdrop.
+## Snapdrop is now LimeWire
+Dear Snapdrop community,
+Snapdrop has been acquired by LimeWire, a leading file sharing platform with integrated AI tools. You can continue to share any files between devices, while benefitting from:
+* sharing files between devices in the same network
+* anonymous up- & downloads
+* end-to-end encryption
+* up to 40GB storage for free users
+* integrated AI tools for signed-up users
-#### Snapdrop is built with the following awesome technologies:
+Visit [snapdrop.net](https://snapdrop.net) or [limewire.com](https://limewire.com)
+
+The Github repository will stay as-is and you can still go ahead and download and run the classic Snapdrop on your own infrastructure.
+
+
+## Classic Snapdrop is built with the following awesome technologies
* Vanilla HTML5 / ES6 / CSS3 frontend
* [WebRTC](http://webrtc.org/) / [WebSockets](http://www.websocket.org/)
* [NodeJS](https://nodejs.org/en/) backend
@@ -13,20 +26,3 @@
Have any questions? Read our [FAQ](/docs/faq.md).
You can [host your own instance with Docker](/docs/local-dev.md).
-
-
-## Support the Snapdrop Community
-Snapdrop is free. Still, we have to pay for the server. If you want to contribute, please use PayPal:
-
-[ ](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FTP9DXUR7LA7Q&source=url)
-
-or Bitcoin:
-
-[ ](https://coins.github.io/thx/#1K9zQ8f4iTyhKyHWmiDKt21cYX2QSDckWB?label=Snapdrop&message=Thanks!%20Your%20contribution%20helps%20to%20keep%20Snapdrop%20free%20for%20everybody!)
-
-Alternatively, you can become a [Github Sponsor](https://github.com/sponsors/RobinLinus).
-
-Thanks a lot for supporting free and open software!
-
-
-
diff --git a/client/index.html b/client/index.html
index e4752dbe..5f88912f 100644
--- a/client/index.html
+++ b/client/index.html
@@ -43,11 +43,6 @@
-
-
-
-
-
@@ -207,19 +202,15 @@ Snapdrop
-
-
-
-
-
-
+
+
diff --git a/client/manifest.json b/client/manifest.json
index 2cdcc36f..9fbabfc9 100644
--- a/client/manifest.json
+++ b/client/manifest.json
@@ -25,7 +25,6 @@
"type": "image/png"
}],
"background_color": "#efefef",
- "start_url": "/",
"display": "minimal-ui",
"theme_color": "#3367d6",
"share_target": {
diff --git a/client/scripts/network.js b/client/scripts/network.js
index eb599014..e1383f3d 100644
--- a/client/scripts/network.js
+++ b/client/scripts/network.js
@@ -258,7 +258,6 @@ class RTCPeer extends Peer {
ordered: true,
reliable: true // Obsolete. See https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/reliable
});
- channel.binaryType = 'arraybuffer';
channel.onopen = e => this._onChannelOpened(e);
this._conn.createOffer().then(d => this._onDescription(d)).catch(e => this._onError(e));
}
@@ -295,6 +294,7 @@ class RTCPeer extends Peer {
_onChannelOpened(event) {
console.log('RTC: channel opened with', this._peerId);
const channel = event.channel || event.target;
+ channel.binaryType = 'arraybuffer';
channel.onmessage = e => this._onMessage(e.data);
channel.onclose = e => this._onChannelClosed();
this._channel = channel;
@@ -448,7 +448,8 @@ class FileChunker {
this._offset += chunk.byteLength;
this._partitionSize += chunk.byteLength;
this._onChunk(chunk);
- if (this._isPartitionEnd() || this.isFileEnd()) {
+ if (this.isFileEnd()) return;
+ if (this._isPartitionEnd()) {
this._onPartitionEnd(this._offset);
return;
}
@@ -512,6 +513,10 @@ class Events {
static on(type, callback) {
return window.addEventListener(type, callback, false);
}
+
+ static off(type, callback) {
+ return window.removeEventListener(type, callback, false);
+ }
}
diff --git a/client/scripts/theme.js b/client/scripts/theme.js
deleted file mode 100644
index e452ffb7..00000000
--- a/client/scripts/theme.js
+++ /dev/null
@@ -1,37 +0,0 @@
-(function(){
-
- // Select the button
- const btnTheme = document.getElementById('theme');
- // Check for dark mode preference at the OS level
- const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
-
- // Get the user's theme preference from local storage, if it's available
- const currentTheme = localStorage.getItem('theme');
- // If the user's preference in localStorage is dark...
- if (currentTheme == 'dark') {
- // ...let's toggle the .dark-theme class on the body
- document.body.classList.toggle('dark-theme');
- // Otherwise, if the user's preference in localStorage is light...
- } else if (currentTheme == 'light') {
- // ...let's toggle the .light-theme class on the body
- document.body.classList.toggle('light-theme');
- }
-
- // Listen for a click on the button
- btnTheme.addEventListener('click', function() {
- // If the user's OS setting is dark and matches our .dark-theme class...
- if (prefersDarkScheme.matches) {
- // ...then toggle the light mode class
- document.body.classList.toggle('light-theme');
- // ...but use .dark-theme if the .light-theme class is already on the body,
- var theme = document.body.classList.contains('light-theme') ? 'light' : 'dark';
- } else {
- // Otherwise, let's do the same thing, but for .dark-theme
- document.body.classList.toggle('dark-theme');
- var theme = document.body.classList.contains('dark-theme') ? 'dark' : 'light';
- }
- // Finally, let's save the current preference to localStorage to keep using it
- localStorage.setItem('theme', theme);
- });
-
-})();
\ No newline at end of file
diff --git a/client/scripts/ui.js b/client/scripts/ui.js
index 0c159841..94bd8351 100644
--- a/client/scripts/ui.js
+++ b/client/scripts/ui.js
@@ -420,7 +420,7 @@ class Notifications {
});
}
- _notify(message, body, closeTimeout = 20000) {
+ _notify(message, body) {
const config = {
body: body,
icon: '/images/logo_transparent_128x128.png',
@@ -435,27 +435,35 @@ class Notifications {
}
// Notification is persistent on Android. We have to close it manually
- if (closeTimeout) {
- setTimeout(_ => notification.close(), closeTimeout);
- }
+ const visibilitychangeHandler = () => {
+ if (document.visibilityState === 'visible') {
+ notification.close();
+ Events.off('visibilitychange', visibilitychangeHandler);
+ }
+ };
+ Events.on('visibilitychange', visibilitychangeHandler);
return notification;
}
_messageNotification(message) {
- if (isURL(message)) {
- const notification = this._notify(message, 'Click to open link');
- this._bind(notification, e => window.open(message, '_blank', null, true));
- } else {
- const notification = this._notify(message, 'Click to copy text');
- this._bind(notification, e => this._copyText(message, notification));
+ if (document.visibilityState !== 'visible') {
+ if (isURL(message)) {
+ const notification = this._notify(message, 'Click to open link');
+ this._bind(notification, e => window.open(message, '_blank', null, true));
+ } else {
+ const notification = this._notify(message, 'Click to copy text');
+ this._bind(notification, e => this._copyText(message, notification));
+ }
}
}
_downloadNotification(message) {
- const notification = this._notify(message, 'Click to download');
- if (!window.isDownloadSupported) return;
- this._bind(notification, e => this._download(notification));
+ if (document.visibilityState !== 'visible') {
+ const notification = this._notify(message, 'Click to download');
+ if (!window.isDownloadSupported) return;
+ this._bind(notification, e => this._download(notification));
+ }
}
_download(notification) {
@@ -540,7 +548,7 @@ const snapdrop = new Snapdrop();
if ('serviceWorker' in navigator) {
- navigator.serviceWorker.register('/service-worker.js')
+ navigator.serviceWorker.register('service-worker.js')
.then(serviceWorker => {
console.log('Service Worker registered');
window.serviceWorker = serviceWorker
@@ -588,7 +596,7 @@ Events.on('load', () => {
function drawCircle(radius) {
ctx.beginPath();
- let color = Math.round(255 * (1 - radius / Math.max(w, h)));
+ let color = Math.round(197 * (1 - radius / Math.max(w, h)));
ctx.strokeStyle = 'rgba(' + color + ',' + color + ',' + color + ',0.1)';
ctx.arc(x0, y0, radius, 0, 2 * Math.PI);
ctx.stroke();
diff --git a/client/service-worker.js b/client/service-worker.js
index 9dae0801..f20e895b 100644
--- a/client/service-worker.js
+++ b/client/service-worker.js
@@ -6,7 +6,6 @@ var urlsToCache = [
'scripts/network.js',
'scripts/ui.js',
'scripts/clipboard.js',
- 'scripts/theme.js',
'sounds/blop.mp3',
'images/favicon-96x96.png'
];
diff --git a/client/styles.css b/client/styles.css
index 0a9aca98..0f90aae4 100644
--- a/client/styles.css
+++ b/client/styles.css
@@ -4,7 +4,9 @@
--icon-size: 24px;
--primary-color: #4285f4;
--peer-width: 120px;
- color-scheme: light dark;
+ --text-color: #333;
+ --bg-color: #fafafa;
+ --bg-color-secondary: #f1f3f4;
}
/* Layout */
@@ -546,7 +548,7 @@ button::-moz-focus-inner {
}
#about:target x-background {
- transform: scale(10);
+ transform: scale(12);
}
#about .row a {
@@ -691,17 +693,10 @@ screen and (min-width: 1100px) {
/* Default colors */
body {
--text-color: #333;
- --bg-color: #fff;
+ --bg-color: #fafafa;
--bg-color-secondary: #f1f3f4;
}
-/* Dark theme colors */
-body.dark-theme {
- --text-color: #eee;
- --bg-color: #121212;
- --bg-color-secondary: #333;
-}
-
/* Colored Elements */
body {
color: var(--text-color);
@@ -719,31 +714,12 @@ x-dialog x-paper {
}
/* Image Preview */
#img-preview{
+ max-width: 100%;
max-height: 50vh;
margin: auto;
display: block;
}
-
-/* Styles for users who prefer dark mode at the OS level */
-@media (prefers-color-scheme: dark) {
-
- /* defaults to dark theme */
- body {
- --text-color: #eee;
- --bg-color: #121212;
- --bg-color-secondary: #333;
- }
-
- /* Override dark mode with light mode styles if the user decides to swap */
- body.light-theme {
- --text-color: #333;
- --bg-color: #fafafa;
- --bg-color-secondary: #f1f3f4;
- }
-}
-
-
/*
Edge specific styles
*/
diff --git a/docs/faq.md b/docs/faq.md
index 7d7c7c54..4a5934e0 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -38,6 +38,21 @@ If you want to learn more about simplicity you can read [Insanely Simple: The Ob
* Do security analysis and suggestions
+## "Inofficial" Instances
+Here's a list of other people hosting inofficial instances of Snapdrop:
+- https://pairdrop.net/
+- https://snapdrop.k26.ch/
+- https://snapdrop.9pfs.repl.co/
+- https://filedrop.codext.de/
+- https://s.hoothin.com/
+- https://www.wulingate.com/
+- https://snapdrop.fairysoft.net/
+- https://airtransferer.web.app/
+- https://drop.wuyuan.dev
+- https://share.jck.cx
+
+DISCLAIMER: WE ARE NOT IN ANY WAY AFFILIATED WITH THE PEOPLE WHO RUN THESE INSTANCES. WE DO NOT KNOW THEM. WE CANNOT VERIFY THE CODE THEY ARE RUNNING!
+
## Third-Party Apps
Here's a list of some third-party Snapdrop apps:
diff --git a/server/index.js b/server/index.js
index 38fa399b..c47feb1e 100644
--- a/server/index.js
+++ b/server/index.js
@@ -30,6 +30,7 @@ class SnapdropServer {
_onConnection(peer) {
this._joinRoom(peer);
peer.socket.on('message', message => this._onMessage(peer, message));
+ peer.socket.on('error', console.error);
this._keepAlive(peer);
// send displayName
@@ -288,4 +289,4 @@ Object.defineProperty(String.prototype, 'hashCode', {
}
});
-const server = new SnapdropServer(process.env.PORT || 3000);
\ No newline at end of file
+const server = new SnapdropServer(process.env.PORT || 3000);