A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Service worker improvements

+76 -53
+2 -1
CHANGELOG.md
··· 3 3 ## 3.1.0 4 4 5 5 - **Removes usage of the Web Audio API, just uses `<audio>` elements now so that Diffuse can be played in the background on iOS.** _Sadly this also means the equalizer has been removed, but a better volume control has been added instead, one that doesn't move away from the tracks view._ 6 - - **Added a command palette.** 6 + - **Adds a command palette.** 7 7 - Correctly scrobbles repeated tracks with Last.fm 8 8 - Disables single-audio-node mode on Safari & iOS (ie. preloading now works) 9 9 - Fixes Fission integration 10 + - Improves detection of new versions and upgrading in the background (ie. service worker) 10 11 - Improves launcher design (called Alfred internally) 11 12 - Improves key bindings 12 13 - Small UI improvements
+4 -8
src/Javascript/Workers/service.js
··· 6 6 // so it can be used offline. 7 7 8 8 9 - importScripts("version.js") 10 - 11 - 12 9 const KEY = 13 - "diffuse-" + self.VERSION 10 + "diffuse-{{VERSION}}" 14 11 15 12 16 - const exclude = 13 + const EXCLUDE = 17 14 [ "_headers" 18 15 , "_redirects" 19 16 , "CORS" ··· 25 22 26 23 27 24 self.addEventListener("activate", event => { 28 - event.waitUntil(self.clients.claim()) 29 - 30 25 // Remove all caches except the one with the currently used `KEY` 31 26 caches.keys().then(keys => { 32 27 keys.forEach(k => { ··· 41 36 const promise = fetch("tree.json") 42 37 .then(response => response.json()) 43 38 .then(tree => { 44 - const filteredTree = tree.filter(t => !exclude.find(u => u === t)) 39 + const filteredTree = tree.filter(t => !EXCLUDE.find(u => u === t)) 45 40 const whatToCache = [ href, `${href.replace(/\/+$/, "")}/about/` ].concat(filteredTree) 46 41 return caches.open(KEY).then(c => Promise.all(whatToCache.map(x => c.add(x)))) 47 42 }) 43 + // TODO: Remove? 48 44 .then(_ => self.skipWaiting()) 49 45 50 46 event.waitUntil(promise)
+44 -16
src/Javascript/index.js
··· 26 26 location.href = location.href.replace("http://", "https://") 27 27 failure("Just a moment, redirecting to HTTPS.") 28 28 29 - // Secure context & Service worker 29 + // Not a secure context 30 30 } else if (!self.isSecureContext) { 31 31 failure(` 32 32 This app only works on a <a class="underline" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#When_is_a_context_considered_secure">secure context</a>, HTTPS & localhost, and modern browsers. 33 33 `) 34 34 35 + // Service worker 35 36 } else if ("serviceWorker" in navigator) { 36 - navigator.serviceWorker.register("service-worker.js").then( 37 - initialise, 38 - err => { 39 - const isFirefox = navigator.userAgent.toLowerCase().includes("firefox") 37 + window.addEventListener("load", () => { 38 + navigator.serviceWorker 39 + .register("service-worker.js") 40 + .then(initialise) 41 + .catch(err => { 42 + const isFirefox = navigator.userAgent.toLowerCase().includes("firefox") 40 43 41 - console.error(err) 42 - return failure( 43 - location.protocol === "https:" || location.hostname === "localhost" 44 - ? "Failed to start the service worker." + (isFirefox ? " Make sure the setting <strong>Delete cookies and site data when Firefox is closed</strong> is off, or Diffuse's domain is added as an exception." : "") 45 - : "Failed to start the service worker, try using HTTPS." 46 - ) 47 - } 48 - ) 44 + console.error(err) 45 + return failure( 46 + location.protocol === "https:" || location.hostname === "localhost" 47 + ? "Failed to start the service worker." + (isFirefox ? " Make sure the setting <strong>Delete cookies and site data when Firefox is closed</strong> is off, or Diffuse's domain is added as an exception." : "") 48 + : "Failed to start the service worker, try using HTTPS." 49 + ) 50 + }) 51 + }) 49 52 50 53 } 51 54 ··· 81 84 wire.backdrop() 82 85 wire.clipboard() 83 86 wire.covers() 87 + wire.serviceWorker(reg) 84 88 wire.webnative() 85 89 86 90 // Other ports 87 91 app.ports.openUrlOnNewPage.subscribe(url => { 88 92 window.open(url, "_blank") 89 93 }) 90 - 91 - // Check for service worker updates every hour 92 - setInterval(() => reg.update(), 1 * 1000 * 60 * 60) 93 94 } 94 95 95 96 ··· 830 831 event.stopPropagation() 831 832 } 832 833 }) 834 + 835 + 836 + 837 + // Service worker 838 + // -------------- 839 + 840 + wire.serviceWorker = async (reg) => { 841 + if (reg.installing) console.log("🧑‍✈️ Service worker is installing") 842 + const initialInstall = reg.installing 843 + 844 + reg.addEventListener("updatefound", () => { 845 + const newWorker = reg.installing 846 + 847 + // No worker was installed yet, so we'll only want to track the state changes 848 + if (newWorker !== initialInstall) { 849 + console.log("🧑‍✈️ A new version of Diffuse is available") 850 + } 851 + 852 + newWorker.addEventListener("statechange", (e) => { 853 + console.log("🧑‍✈️ Service worker is", e.target.state) 854 + }) 855 + }) 856 + 857 + // Check for service worker updates and every hour after that 858 + reg.update() 859 + setInterval(() => reg.update(), 1 * 1000 * 60 * 60) 860 + } 833 861 834 862 835 863
-8
src/Javascript/indexed-db.js
··· 13 13 const WAITING_MSG = "Waiting for database" 14 14 15 15 16 - self.importScripts && importScripts("version.js") 17 - 18 - 19 16 const indexedDB = 20 17 self.indexedDB || 21 18 self.webkitIndexedDB || ··· 40 37 41 38 idx.onsuccess = _ => { 42 39 db = idx.result 43 - 44 - setInIndex({ 45 - key: "VERSION", 46 - data: self.VERSION 47 - }) 48 40 } 49 41 50 42 idx.onerror = event => {
+26 -20
system/Build/Main.hs
··· 14 14 import qualified Data.ByteString.Lazy as BSL (toStrict) 15 15 import qualified Data.Char as Char 16 16 import qualified Data.List as List 17 + import qualified Data.Text as Text 17 18 import qualified Data.Text.Encoding as Text 18 19 import qualified Data.Text.IO as Text 19 20 ··· 21 22 -- | (• ◡•)| (❍ᴥ❍ʋ) 22 23 23 24 24 - main :: IO Dictionary 25 + main :: IO () 25 26 main = 26 27 do 27 28 de <- dependencies ··· 32 33 let dictionary = List.concatMap (flow de) se 33 34 34 35 -- Write everything to disk 35 - dictionary 36 - |> insertVersion (de !~> "timestamp") 37 - |> write "../build" 36 + write "../build" dictionary 38 37 39 38 -- Make a file tree 40 39 build <- list "../build/**/*.*" ··· 42 41 build 43 42 |> makeTree 44 43 |> write "../build" 44 + 45 + -- Inject version timestamp 46 + insertVersion (de !~> "timestamp") build 47 + 48 + -- Fin 49 + return () 45 50 46 51 47 52 list :: [Char] -> IO Dictionary ··· 155 160 defs 156 161 157 162 158 - insertVersion :: Text -> Dictionary -> Dictionary 159 - insertVersion version dict = 160 - let 161 - versionContent = 162 - Text.encodeUtf8 ("self.VERSION = \"" <> version <> "\"") 163 + insertVersion :: Text -> Dictionary -> IO () 164 + insertVersion version dict = do 165 + let sw = List.filter 166 + (\def -> localPath def == "service-worker.js") 167 + dict 163 168 164 - defs = 165 - case headMay dict of 166 - Just def -> 167 - def 168 - |> forkDefinition "version.js" 169 - |> wrap 170 - |> setContent versionContent 169 + case headMay sw of 170 + Just def -> 171 + def 172 + |> content 173 + |> fmap Text.decodeUtf8 174 + |> fmap (Text.replace "{{VERSION}}" version) 175 + |> fmap Text.encodeUtf8 176 + |> (\c -> def { content = c }) 177 + |> writeDef "../build" 178 + |> fmap (\_ -> ()) 171 179 172 - Nothing -> 173 - [] 174 - in 175 - dict <> defs 180 + Nothing -> 181 + return () 176 182 177 183 178 184