Prechádzať zdrojové kódy

Merge branch 'upstream' into implement-file-engine

Evan Almloff 2 rokov pred
rodič
commit
e487cddd1e

+ 1 - 0
Cargo.toml

@@ -23,6 +23,7 @@ members = [
     "packages/signals",
     "packages/hot-reload",
     "docs/guide",
+    "examples/PWA-example",
 ]
 
 # This is a "virtual package"

+ 17 - 0
examples/PWA-example/Cargo.toml

@@ -0,0 +1,17 @@
+[package]
+name = "dioxus-pwa-example"
+version = "0.1.0"
+authors = ["Antonio Curavalea <one.kyonblack@gmail.com>"]
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+dioxus = { path = "../../packages/dioxus", version = "^0.3.0"}
+dioxus-web = { path = "../../packages/web", version = "^0.3.0"}
+
+log = "0.4.6"
+
+# WebAssembly Debug
+wasm-logger = "0.2.0"
+console_error_panic_hook = "0.1.7"

+ 42 - 0
examples/PWA-example/Dioxus.toml

@@ -0,0 +1,42 @@
+[application]
+
+# App (Project) Name
+name = "dioxus-pwa-example"
+
+# Dioxus App Default Platform
+# desktop, web, mobile, ssr
+default_platform = "web"
+
+# `build` & `serve` dist path
+out_dir = "dist"
+
+# resource (public) file folder
+asset_dir = "public"
+
+[web.app]
+
+# HTML title tag content
+title = "dioxus | ⛺"
+
+[web.watcher]
+
+# when watcher trigger, regenerate the `index.html`
+reload_html = true
+
+# which files or dirs will be watcher monitoring
+watch_path = ["src", "public"]
+
+# include `assets` in web platform
+[web.resource]
+
+# CSS style file
+style = []
+
+# Javascript code file
+script = []
+
+[web.resource.dev]
+
+# Javascript code file
+# serve: [dev-server] only
+script = []

+ 21 - 0
examples/PWA-example/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Dioxus
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 44 - 0
examples/PWA-example/README.md

@@ -0,0 +1,44 @@
+# Dioxus PWA example
+
+This is a basic example of a progressive web app (PWA) using Dioxus and Dioxus CLI.
+Currently PWA functionality requires the use of a service worker and manifest file, so this isn't 100% Rust yet.
+
+It is also very much usable as a template for your projects, if you're aiming to create a PWA.
+
+## Try the example
+
+Make sure you have Dioxus CLI installed (if you're unsure, run `cargo install dioxus-cli`).
+
+You can run `dioxus serve` in this directory to start the web server locally, or run
+`dioxus build --release` to build the project so you can deploy it on a separate web-server.
+
+## Project Structure
+```
+├── Cargo.toml
+├── Dioxus.toml
+├── index.html // Custom HTML is needed for this, to load the SW and manifest.
+├── LICENSE
+├── public
+│   ├── favicon.ico
+│   ├── logo_192.png
+│   ├── logo_512.png
+│   ├── manifest.json // The manifest file - edit this as you need to.
+│   └── sw.js // The service worker - you must edit this for actual projects.
+├── README.md
+└── src
+    └── main.rs
+```
+
+## Resources
+
+If you're just getting started with PWAs, here are some useful resources:
+
+* [PWABuilder docs](https://docs.pwabuilder.com/#/)
+* [MDN article on PWAs](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps)
+
+For service worker scripting (in JavaScript):
+
+* [Service worker guide from PWABuilder](https://docs.pwabuilder.com/#/home/sw-intro)
+* [Service worker examples, also from PWABuilder](https://github.com/pwa-builder/pwabuilder-serviceworkers)
+
+If you want to stay as close to 100% Rust as possible, you can try using [wasi-worker](https://github.com/dunnock/wasi-worker) to replace the JS service worker file. The JSON manifest will still be required though.

+ 30 - 0
examples/PWA-example/index.html

@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>{app_title}</title>
+  <script>
+    if ('serviceWorker' in navigator) {
+      navigator.serviceWorker.register(
+        '/sw.js'
+      );
+    }
+  </script>
+  <link rel="manifest" href="manifest.json">
+  <meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <meta charset="UTF-8" />
+  {style_include}
+</head>
+<body>
+  <div id="main"></div>
+  <script type="module">
+    import init from "/{base_path}/assets/dioxus/{app_name}.js";
+    init("/{base_path}/assets/dioxus/{app_name}_bg.wasm").then(wasm => {
+      if (wasm.__wbindgen_start == undefined) {
+        wasm.main();
+      }
+    });
+  </script>
+  {script_include}
+</body>
+</html>

BIN
examples/PWA-example/public/favicon.ico


BIN
examples/PWA-example/public/logo_192.png


BIN
examples/PWA-example/public/logo_512.png


+ 34 - 0
examples/PWA-example/public/manifest.json

@@ -0,0 +1,34 @@
+{
+  "name": "Dioxus",
+  "icons": [
+    {
+      "src": "logo_192.png",
+      "type": "image/png",
+      "sizes": "192x192"
+    },
+    {
+      "src": "logo_512.png",
+      "type": "image/png",
+      "sizes": "512x512",
+      "purpose": "any"
+    },
+    {
+      "src": "logo_512.png",
+      "type": "image/png",
+      "sizes": "any",
+      "purpose": "any"
+    }
+  ],
+  "start_url": "/",
+  "id": "/",
+  "display": "standalone",
+  "display_override": ["window-control-overlay", "standalone"],
+  "scope": "/",
+  "theme_color": "#000000",
+  "background_color": "#ffffff",
+  "short_name": "Dioxus",
+  "description": "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust.",
+  "dir": "ltr",
+  "lang": "en",
+  "orientation": "portrait"
+}

+ 198 - 0
examples/PWA-example/public/sw.js

@@ -0,0 +1,198 @@
+"use strict";
+
+//console.log('WORKER: executing.');
+
+/* A version number is useful when updating the worker logic,
+   allowing you to remove outdated cache entries during the update.
+*/
+var version = 'v1.0.0::';
+
+/* These resources will be downloaded and cached by the service worker
+   during the installation process. If any resource fails to be downloaded,
+   then the service worker won't be installed either.
+*/
+var offlineFundamentals = [
+  // add here the files you want to cache
+  'favicon.ico'
+];
+
+/* The install event fires when the service worker is first installed.
+   You can use this event to prepare the service worker to be able to serve
+   files while visitors are offline.
+*/
+self.addEventListener("install", function (event) {
+  //console.log('WORKER: install event in progress.');
+  /* Using event.waitUntil(p) blocks the installation process on the provided
+     promise. If the promise is rejected, the service worker won't be installed.
+  */
+  event.waitUntil(
+    /* The caches built-in is a promise-based API that helps you cache responses,
+       as well as finding and deleting them.
+    */
+    caches
+      /* You can open a cache by name, and this method returns a promise. We use
+         a versioned cache name here so that we can remove old cache entries in
+         one fell swoop later, when phasing out an older service worker.
+      */
+      .open(version + 'fundamentals')
+      .then(function (cache) {
+        /* After the cache is opened, we can fill it with the offline fundamentals.
+           The method below will add all resources in `offlineFundamentals` to the
+           cache, after making requests for them.
+        */
+        return cache.addAll(offlineFundamentals);
+      })
+      .then(function () {
+        //console.log('WORKER: install completed');
+      })
+  );
+});
+
+/* The fetch event fires whenever a page controlled by this service worker requests
+   a resource. This isn't limited to `fetch` or even XMLHttpRequest. Instead, it
+   comprehends even the request for the HTML page on first load, as well as JS and
+   CSS resources, fonts, any images, etc.
+*/
+self.addEventListener("fetch", function (event) {
+  //console.log('WORKER: fetch event in progress.');
+
+  /* We should only cache GET requests, and deal with the rest of method in the
+     client-side, by handling failed POST,PUT,PATCH,etc. requests.
+  */
+  if (event.request.method !== 'GET') {
+    /* If we don't block the event as shown below, then the request will go to
+       the network as usual.
+    */
+    //console.log('WORKER: fetch event ignored.', event.request.method, event.request.url);
+    return;
+  }
+  /* Similar to event.waitUntil in that it blocks the fetch event on a promise.
+     Fulfillment result will be used as the response, and rejection will end in a
+     HTTP response indicating failure.
+  */
+  event.respondWith(
+    caches
+      /* This method returns a promise that resolves to a cache entry matching
+         the request. Once the promise is settled, we can then provide a response
+         to the fetch request.
+      */
+      .match(event.request)
+      .then(function (cached) {
+        /* Even if the response is in our cache, we go to the network as well.
+           This pattern is known for producing "eventually fresh" responses,
+           where we return cached responses immediately, and meanwhile pull
+           a network response and store that in the cache.
+
+           Read more:
+           https://ponyfoo.com/articles/progressive-networking-serviceworker
+        */
+        var networked = fetch(event.request)
+          // We handle the network request with success and failure scenarios.
+          .then(fetchedFromNetwork, unableToResolve)
+          // We should catch errors on the fetchedFromNetwork handler as well.
+          .catch(unableToResolve);
+
+        /* We return the cached response immediately if there is one, and fall
+           back to waiting on the network as usual.
+        */
+        //console.log('WORKER: fetch event', cached ? '(cached)' : '(network)', event.request.url);
+        return cached || networked;
+
+        function fetchedFromNetwork(response) {
+          /* We copy the response before replying to the network request.
+             This is the response that will be stored on the ServiceWorker cache.
+          */
+          var cacheCopy = response.clone();
+
+          //console.log('WORKER: fetch response from network.', event.request.url);
+
+          caches
+            // We open a cache to store the response for this request.
+            .open(version + 'pages')
+            .then(function add(cache) {
+              /* We store the response for this request. It'll later become
+                 available to caches.match(event.request) calls, when looking
+                 for cached responses.
+              */
+              cache.put(event.request, cacheCopy);
+            })
+            .then(function () {
+              //console.log('WORKER: fetch response stored in cache.', event.request.url);
+            });
+
+          // Return the response so that the promise is settled in fulfillment.
+          return response;
+        }
+
+        /* When this method is called, it means we were unable to produce a response
+           from either the cache or the network. This is our opportunity to produce
+           a meaningful response even when all else fails. It's the last chance, so
+           you probably want to display a "Service Unavailable" view or a generic
+           error response.
+        */
+        function unableToResolve() {
+          /* There's a couple of things we can do here.
+             - Test the Accept header and then return one of the `offlineFundamentals`
+               e.g: `return caches.match('/some/cached/image.png')`
+             - You should also consider the origin. It's easier to decide what
+               "unavailable" means for requests against your origins than for requests
+               against a third party, such as an ad provider.
+             - Generate a Response programmaticaly, as shown below, and return that.
+          */
+
+          //console.log('WORKER: fetch request failed in both cache and network.');
+
+          /* Here we're creating a response programmatically. The first parameter is the
+             response body, and the second one defines the options for the response.
+          */
+          return new Response('<h1>Service Unavailable</h1>', {
+            status: 503,
+            statusText: 'Service Unavailable',
+            headers: new Headers({
+              'Content-Type': 'text/html'
+            })
+          });
+        }
+      })
+  );
+});
+
+/* The activate event fires after a service worker has been successfully installed.
+   It is most useful when phasing out an older version of a service worker, as at
+   this point you know that the new worker was installed correctly. In this example,
+   we delete old caches that don't match the version in the worker we just finished
+   installing.
+*/
+self.addEventListener("activate", function (event) {
+  /* Just like with the install event, event.waitUntil blocks activate on a promise.
+     Activation will fail unless the promise is fulfilled.
+  */
+  //console.log('WORKER: activate event in progress.');
+
+  event.waitUntil(
+    caches
+      /* This method returns a promise which will resolve to an array of available
+         cache keys.
+      */
+      .keys()
+      .then(function (keys) {
+        // We return a promise that settles when all outdated caches are deleted.
+        return Promise.all(
+          keys
+            .filter(function (key) {
+              // Filter by keys that don't start with the latest version prefix.
+              return !key.startsWith(version);
+            })
+            .map(function (key) {
+              /* Return a promise that's fulfilled
+                 when each outdated cache is deleted.
+              */
+              return caches.delete(key);
+            })
+        );
+      })
+      .then(function () {
+        //console.log('WORKER: activate completed.');
+      })
+  );
+});

+ 20 - 0
examples/PWA-example/src/main.rs

@@ -0,0 +1,20 @@
+use dioxus::prelude::*;
+
+fn main() {
+    // init debug tool for WebAssembly
+    wasm_logger::init(wasm_logger::Config::default());
+    console_error_panic_hook::set_once();
+
+    dioxus_web::launch(app);
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx! (
+        div {
+            style: "text-align: center;",
+            h1 { "🌗 Dioxus 🚀" }
+            h3 { "Frontend that scales." }
+            p { "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." }
+        }
+    ))
+}

+ 1 - 1
examples/svg.rs

@@ -26,7 +26,7 @@ fn app(cx: Scope) -> Element {
                     onclick: move |_| {
                         use rand::Rng;
                         let mut rng = rand::thread_rng();
-                        val.set(rng.gen_range(1..6));
+                        val.set(rng.gen_range(1..=6));
                     }
                 }
             }