···96969797server:
9898 @echo "> Booting up web server on port 5000"
9999- @devd --port 5000 --all --crossdomain --quiet --notfound=index.html $(BUILD_DIR)
9999+ @devd --port 5000 --all --crossdomain --quiet --notfound=301.html $(BUILD_DIR)
100100101101102102test:
+7-15
README.md
···42424343### Hosting on your own server
44444545-Diffuse is a static web application, which means it's just HTML, CSS and Javascript. No REST API, database, or anything backend-related involved. That said, the app does require a HTTP web server so it can have "clean urls" and use service workers (preferably HTTPS). It also requires one special rule, and that is, no matter which HTML page is requested, it should always render the root `200.html` or `index.html` file. `https://diffuse.sh` uses Netlify, which in turn uses the `_redirects` file for this. You can download a pre-build web-only version of Diffuse on the [releases](https://github.com/icidasset/diffuse/releases) page.
4545+Diffuse is a static web application, which means it's just HTML, CSS and Javascript. No REST API, database, or anything backend-related involved. The app uses a hash, aka. fragment, based routing system, so you don't need any special server rules for routing. You can download a pre-build web-only version of Diffuse on the [releases](https://github.com/icidasset/diffuse/releases) page. Diffuse uses service workers, so you may need HTTPS for it to work smoothly in certain browsers. I should also note that some source services use OAuth, so you'll need to use your own application credentials (eg. google drive client id + secret).
46464747In short:
4848- Diffuse is a static, serverless, web application
4949-- Diffuse requires a HTTP server (prefer HTTPS for service worker)
5050-- Always render the root `200.html` or `index.html` file
4949+- Routing is done using hashes/fragments (eg. `diffuse.sh/#/sources`)
5150- Download a web build on the [releases](https://github.com/icidasset/diffuse/releases) page
5252-5353-```shell
5454-# Example of a nginx configuration
5555-# Disclaimer: I'm not confident this'll actually work,
5656-# but it should be something along these lines.
5757-location ~ .html$ {
5858- try_files $uri /200.html;
5959-}
6060-```
5151+- Uses service workers (use HTTPS if possible)
5252+- May need own OAuth application credentials for some source services
615362546355···67596860### Building it yourself
69617070-For version numbers,
7171-see `.tool-versions` and `stack.yaml`.
6262+For version numbers, see `.tool-versions` and `stack.yaml`.
6363+All of these, except the last one, can be install using [homebrew](https://brew.sh/).
72647365- [Elm](https://elm-lang.org/) programming language
7466- [Haskell](https://docs.haskellstack.org/en/stable/README/) programming language
7567- [Google Closure Compiler](https://github.com/google/closure-compiler#getting-started) minifying assets
7676-- [Elm Proofread](https://github.com/icidasset/elm-proofread) documentation tests (optional)
7768- [Devd](https://github.com/cortesi/devd) web server for development (optional)
7869- [Watchexec](https://github.com/watchexec/watchexec) watching for file changes (optional)
7070+- [Elm Proofread](https://github.com/icidasset/elm-proofread) documentation tests (optional)
797180728173```shell
+33-14
src/Applications/UI.elm
···154154 |> update
155155 (PageChanged page)
156156 |> addCommand
157157- (case maybePage of
158158- Just _ ->
159159- Cmd.none
157157+ (if Maybe.isNothing maybePage then
158158+ resetUrl key url page
160159161161- Nothing ->
162162- Nav.replaceUrl key "/"
160160+ else
161161+ Cmd.none
163162 )
164163165164···383382 |> update (BackdropMsg Backdrop.Default)
384383 |> addCommand (Ports.toBrain <| Alien.trigger Alien.SignOut)
385384 |> addCommand (Ports.activeQueueItemChanged Nothing)
386386- |> addCommand (Nav.pushUrl model.navKey "/")
385385+ |> addCommand (Nav.pushUrl model.navKey "")
387386388387 -----------------------------------------
389388 -- Children
···650649 |> Nav.pushUrl model.navKey
651650 |> returnWithModel model
652651653653- LinkClicked (Browser.Internal url) ->
654654- if url.path == "/about" then
655655- returnWithModel model (Nav.load "/about")
652652+ LinkClicked (Browser.Internal urlWithFragment) ->
653653+ let
654654+ url =
655655+ if urlWithFragment.fragment == Just "/" then
656656+ { urlWithFragment | fragment = Nothing }
657657+658658+ else
659659+ urlWithFragment
660660+ in
661661+ if url.path == "about" then
662662+ returnWithModel model (Nav.load "about")
656663657664 else
658665 returnWithModel model (Nav.pushUrl model.navKey <| Url.toString url)
···660667 LinkClicked (Browser.External href) ->
661668 returnWithModel model (Nav.load href)
662669663663- UrlChanged url ->
664664- case Page.fromUrl url of
665665- Just page ->
670670+ UrlChanged ({ fragment, query } as urlWithQuery) ->
671671+ let
672672+ url =
673673+ { urlWithQuery | query = Nothing }
674674+ in
675675+ case ( query, Page.fromUrl url ) of
676676+ ( Nothing, Just page ) ->
666677 { model | page = page, url = url }
667678 |> return
668679 |> andThen (update <| PageChanged page)
669680670670- Nothing ->
671671- returnWithModel model (Nav.replaceUrl model.navKey "/")
681681+ ( Just _, Just page ) ->
682682+ returnWithModel model (resetUrl model.navKey url page)
683683+684684+ _ ->
685685+ returnWithModel model (resetUrl model.navKey url Page.Index)
672686673687674688updateWithModel : Model -> Msg -> ( Model, Cmd Msg )
675689updateWithModel model msg =
676690 update msg model
691691+692692+693693+resetUrl : Nav.Key -> Url -> Page.Page -> Cmd Msg
694694+resetUrl key url page =
695695+ Nav.replaceUrl key (url.path ++ Page.toString page)
677696678697679698
···44//
55// This worker is responsible for everything non-UI.
6677-importScripts("/vendor/musicmetadata.min.js")
88-importScripts("/vendor/subworkers-polyfill.min.js")
77+importScripts("../vendor/musicmetadata.min.js")
88+importScripts("../vendor/subworkers-polyfill.min.js")
991010-importScripts("/brain.js")
1111-importScripts("/encryption.js")
1212-importScripts("/indexed-db.js")
1313-importScripts("/processing.js")
1414-importScripts("/urls.js")
1010+importScripts("../brain.js")
1111+importScripts("../encryption.js")
1212+importScripts("../indexed-db.js")
1313+importScripts("../processing.js")
1414+importScripts("../urls.js")
151516161717const app = Elm.Brain.init()
+1-1
src/Javascript/Workers/search.js
···44//
55// This worker is responsible for searching through a `Track` collection.
6677-importScripts("/vendor/lunr.min.js")
77+importScripts("../vendor/lunr.min.js")
88991010let index
+1-1
src/Javascript/Workers/service.js
···55// This worker is responsible for caching the application
66// so it can be used offline.
7788-importScripts("/version.js")
88+importScripts("version.js")
9910101111const KEY =
+1-1
src/Javascript/index.js
···2626// Brain
2727// =====
28282929-const brain = new Worker("/workers/brain.js")
2929+const brain = new Worker("workers/brain.js")
30303131app.ports.toBrain.subscribe(thing => {
3232 brain.postMessage(thing)
+1-1
src/Javascript/indexed-db.js
···55// The local database.
66// This is used instead of localStorage.
7788-importScripts("/vendor/text-encoding-polyfill.min.js")
88+importScripts("../vendor/text-encoding-polyfill.min.js")
9910101111const indexedDB =