2019年7月5日 星期五

My first PWA Progressive Web Application Google Part 04 Offline

主要參考:https://codelabs.developers.google.com/codelabs/your-first-pwapp/#4

之前做好的WebApp在Python Flask沒有運作時候首先會進入Loading畫面等待10秒圈圈旋轉了10次后進入全屏幕白色:








離綫時候爲了依靠Service Worker將Web Shell緩存,所以要在index.html注冊service-worker.js

// CODELAB: Register service worker.
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
        .then((reg) => {
          console.log('Service worker registered.', reg);
        });
  });
}

而且要在service-worker.js寫明需要緩存的檔案,意思是說安裝之後就從打開static-v1緩存加入offline.html:

self.addEventListener('install', (evt) => {
  console.log('[ServiceWorker] Install');
  // CODELAB: Precache static resources here.
  evt.waitUntil(
    caches.open('static-cache-v1').then((cache) => {
      console.log('[ServiceWorker] Pre-caching offline page');
      return cache.addAll(['/offline.html']);
    })
  );
  self.skipWaiting();
});

真正運行到addAll()時候,瀏覽器會在install安裝時候向服務器發送所有request,如果其中任何一個或以上的request失敗,那麽整個安裝將會失敗,因此整個流程可以保證安裝完畢之後的WebApp已經完整地緩存了一次。

這時候如果你遇到找不到luxon-1.11.4.js.map,那麽在Chrome的DevTool settings裏面,需要取消"Enable JavaScript source maps"以及"Enable CSS source maps"。

這時候如果看見http://127.0.0.1/service-worker.js下載出現錯誤,可能要加上favicon.ico。

這時候如果看見service-worker不能啓動,那麽需要確保service-worker.js的mime-type是application/javascript,而使用Flask要這樣寫send_from_directory("templates", “service-worker.js”, mimetype="application/javascript")。

這時候確保service worker activated and is running:

























Service除了install之外,也可以增加activate listener,在activate的時候,如果當前cache的名稱與新的CACHE_NAME名稱不一樣,那麽就將當前的cache從caches移除: caches.delete(key)
// CODELAB: Remove previous cached data from disk.
evt.waitUntil(
    caches.keys().then((keyList) => {
      return Promise.all(keyList.map((key) => {
        if (key !== CACHE_NAME) {
          console.log('[ServiceWorker] Removing old cache', key, 'new cache', CACHE_NAME);
          return caches.delete(key);
        }
      }));
    })
);

這時候將CACHE_NAME從v1改爲v2重啓網頁服務器的話,會見到綠燈的Status #改變,并且看見以下console messages:






























在寫好install及activate之後,最後要寫好fetch能夠讓cache真正工作,這裏大概意思是,請求模式如非轉頁的話就不需要緩存了;如果是轉頁的話就從網絡fetch進來,fetch有exception時候就catch出來處理,處理方法是無論請求什麽頁面也好,統一打開緩存指明的offline.html檔案,而offline.html可以是一個道歉畫面,或者是一個離綫小游戲,或者是一個離綫頁面:
// CODELAB: Add fetch event handler here.
if (evt.request.mode !== 'navigate') {
  // Not a page navigation, bail.
  return;
}
evt.respondWith(
    fetch(evt.request)
        .catch(() => {
          return caches.open(CACHE_NAME)
              .then((cache) => {
                return cache.match('offline.html');
              });
        })
);


現在無論使用本機Chrome或者Firefox都可以在沒有Flask運行情況下見到offline.html










sa






































End

2019年7月4日 星期四

My first PWA Progressive Web Application Google Part 03 Manifest

主要參考:https://codelabs.developers.google.com/codelabs/your-first-pwapp/#3


manifest.json
網頁沒有manifest.json的時候,Chrome裏面的Application Manifest會顯示找不到manifest


在templates裏面增加manifest.json后,然後在index.html裏面加入后,Chrome會顯示裏面的資料:
<link rel="manifest" href="/manifest.json">













Apple iOS
Safari目前再2019年7月的時候依然不支持manifest.json,網頁强行加到主畫面會產生很差的效果,包括缺乏常見的App圖示,而且打開之後沒有全畫面并且會見到Safari頂部網址欄及底部上下頁操作,明顯不像一個App。


因此在我們可以加入<meta>及"apple-mobile-web"相關的資料到index.html裏面:
<!-- CODELAB: Add iOS meta tags and icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Weather PWA">
<link rel="apple-touch-icon" href="/images/icons/icon-152x152.png">

當capable時候的特別之處在於,啓動WebApp后會進入standalone模式,不會顯示頂部網址欄,不會顯示底部上下頁等操控按鈕。而status-bar-style控制ios頂部顯示信號電信商wifi時間電量的部分,可以選填default的白底黑字、black的黑底白字,或者最進取的black-translucent的app底色白字:









上圖中的navbar的顔色在inline.css裏面設定了為#3f51b5,如果將body background從#ececec改成#3f51b5。改完css檔案之後safari很可能已經cache了舊版本的css檔案,那麽最好在index.html裏面加上<link href="/styles/inline.css?v=1">,WebApp做好了。


























Theme Color

在Android Chrome裏面設定了theme-color可以讓status bar也顯示漂亮的顔色

<!-- CODELAB: Add description here -->
<meta name="description" content="A sample weather app">


<!-- CODELAB: Add meta theme-color -->
<meta name="theme-color" content="#2F3BA2" />














End

My first PWA Progressive Web Application Google Part 02 with Python Flask

主要參考:https://codelabs.developers.google.com/codelabs/your-first-pwapp/#1

Google網頁裏面强烈建議使用Glitch作爲全雲端開發工具,另外一個方法是使用Node.js在自己的電腦運行,然而實際開發中需要使用Python+Flask,於是下載your-first-pwapp-master.zip后,將public文件夾改名成templates,將server.js改寫成main.py及config.json,然後輸入python main.py就會見到Chrome運行網頁

config.json

{
  "Web": [
    {
      "name": "Web01",
      "host": "0.0.0.0",
      "port": 80
    }
  ]
}

main.py

from flask import Flask, request, send_from_directory, render_template, url_for, redirect
import datetime, os, json, logging, webbrowser, requests

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')
    
@app.route('/forecast/<location>')
def forecast(location):
    try:
        BASE_URL = 'https://api.darksky.net/forecast'
        API_KEY = '7cfcae53bb33928e9a7d9a85df825bce'
        url = '/'.join([BASE_URL, API_KEY, location])
        r = requests.get(url)
        return json.dumps(r.json())
    except Exception as e:
        logging.error(e)
        
        fakeForecast = {
          fakeData: True,
          latitude: 0,
          longitude: 0,
          timezone: 'America/New_York',
          currently: {
            time: 0,
            summary: 'Clear',
            icon: 'clear-day',
            temperature: 43.4,
            humidity: 0.62,
            windSpeed: 3.74,
            windBearing: 208,
          },
          daily: {
            data: [
              {
                time: 0,
                icon: 'partly-cloudy-night',
                sunriseTime: 1553079633,
                sunsetTime: 1553123320,
                temperatureHigh: 52.91,
                temperatureLow: 41.35,
              },
              {
                time: 86400,
                icon: 'rain',
                sunriseTime: 1553165933,
                sunsetTime: 1553209784,
                temperatureHigh: 48.01,
                temperatureLow: 44.17,
              },
              {
                time: 172800,
                icon: 'rain',
                sunriseTime: 1553252232,
                sunsetTime: 1553296247,
                temperatureHigh: 50.31,
                temperatureLow: 33.61,
              },
              {
                time: 259200,
                icon: 'partly-cloudy-night',
                sunriseTime: 1553338532,
                sunsetTime: 1553382710,
                temperatureHigh: 46.44,
                temperatureLow: 33.82,
              },
              {
                time: 345600,
                icon: 'partly-cloudy-night',
                sunriseTime: 1553424831,
                sunsetTime: 1553469172,
                temperatureHigh: 60.5,
                temperatureLow: 43.82,
              },
              {
                time: 432000,
                icon: 'rain',
                sunriseTime: 1553511130,
                sunsetTime: 1553555635,
                temperatureHigh: 61.79,
                temperatureLow: 32.8,
              },
              {
                time: 518400,
                icon: 'rain',
                sunriseTime: 1553597430,
                sunsetTime: 1553642098,
                temperatureHigh: 48.28,
                temperatureLow: 33.49,
              },
              {
                time: 604800,
                icon: 'snow',
                sunriseTime: 1553683730,
                sunsetTime: 1553728560,
                temperatureHigh: 43.58,
                temperatureLow: 33.68,
              }
            ]
          }
        }
        
        if location.split(',')==2:
            result['latitude'] = location.split(',')[0]
            result['longitude'] = location.split(',')[1]
        
        return json.dumps(fakeForecast)

@app.route('/<path:path>')
def path(path):
    if path.endswith(('.js', '.css', '.png', '.ico', 'manifest.json', '.svg')):
        return send_from_directory("templates", path)
    return redirect(url_for('index'))
    
def init(name):
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    now = datetime.datetime.utcnow().strftime("%Y%m%d.%H%M%S")
    fileName = '.'.join([name, now, str(os.getpid()), 'log'])
    logPath = 'logs'
    os.makedirs(logPath, exist_ok=True)
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s",
        handlers=[
            logging.FileHandler("{0}/{1}".format('logs', fileName)),
            logging.StreamHandler()
    ])
    
def run_flask():
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    host='127.0.0.1'
    port=80
    with open('config.json', 'r') as infile:
        data = json.load(infile)
        print(data)
        web = data["Web"][0]
        host = web["host"]
        port = web["port"]
    webbrowser.open('http://{}:{}'.format('127.0.0.1' if host=='0.0.0.0' else host, port))
    app.secret_key = os.urandom(12)
    app.run(host=host, port=port)

    
if __name__ == "__main__":
    init('flask')
    run_flask()









End

2019年7月3日 星期三

My first PWA Progressive Web Application Google Part 01


主要參考:
https://codelabs.developers.google.com/codelabs/your-first-pwapp/#0


1. API

首先去darksky.net注冊并且得到一個DARKSKY_API_KEY,我更改了一點后的Key是7cfcae53bb33928e9a7d9a85df825bce,於是可以測試HTTP GET返回的JSON:
https://api.darksky.net/forecast/7cfcae53bb33928e9a7d9a85df825bce/40.7720232,-73.9732319


2. Glitch

去https://glitch.com注冊一個賬號然後Clone from Git Repo,輸入https://github.com/googlecodelabs/your-first-pwapp.git,將Dark Sky API key寫在.env檔案裏面,按下Show Next to the Code,見到很有Google味道的界面以及天氣溫度就算是成功了。
























































3. manifest.json

index.html裏面填上<link rel="manifest" href="/manifest.json">,加入<meta>以及關於apple-mobile-web-app等東西,加入<meta> description,加入<meta> theme-color。

4. 基本離綫體驗

要讓離綫時候畫面有資料并且返回http 200,需要注冊service worker,填上需要緩存的檔案例如offline.html,


End

2023 Promox on Morefine N6000 16GB 512GB

2023 Promox on Morefine N6000 16GB 512GB Software Etcher 100MB (not but can be rufus-4.3.exe 1.4MB) Proxmox VE 7.4 ISO Installer (1st ISO re...