Cache home location and weather across page navigation
This commit is contained in:
+28
-54
@@ -1,5 +1,6 @@
|
||||
const { secure, toast } = require("../../utils/api");
|
||||
const { localDate } = require("../../utils/date");
|
||||
const environment = require("../../utils/environment");
|
||||
function dateLabel() {
|
||||
const now = new Date();
|
||||
return `${now.getMonth() + 1}月${now.getDate()}日 周${"日一二三四五六"[now.getDay()]}`;
|
||||
@@ -19,7 +20,7 @@ Page({
|
||||
error: "",
|
||||
},
|
||||
onShow() {
|
||||
this.updateEnvironmentLabel();
|
||||
this.applyEnvironment(environment.cached());
|
||||
this.load();
|
||||
},
|
||||
onUnload() {
|
||||
@@ -31,62 +32,40 @@ Page({
|
||||
dateLabel() + " · " + (this.weatherLabel || "轻触获取天气"),
|
||||
});
|
||||
},
|
||||
applyEnvironment(value) {
|
||||
if (this.disposed) return;
|
||||
if (value) {
|
||||
this.weatherLabel = value.weatherLabel;
|
||||
this.setData({ locationName: value.locationName });
|
||||
}
|
||||
this.updateEnvironmentLabel();
|
||||
},
|
||||
async refreshEnvironment(event) {
|
||||
if (this.environmentBusy) return;
|
||||
this.environmentBusy = true;
|
||||
const tapped = !!event;
|
||||
let positioned = false;
|
||||
try {
|
||||
const settings = await new Promise((resolve, reject) =>
|
||||
wx.getSetting({ success: resolve, fail: reject }),
|
||||
);
|
||||
if (settings.authSetting["scope.userLocation"] === false) {
|
||||
if (!tapped) throw new Error("location-denied");
|
||||
const enabled = await new Promise((resolve, reject) =>
|
||||
wx.openSetting({ success: resolve, fail: reject }),
|
||||
);
|
||||
if (!enabled.authSetting["scope.userLocation"])
|
||||
throw new Error("location-denied");
|
||||
}
|
||||
const cached = environment.cached();
|
||||
if (!cached) {
|
||||
this.weatherLabel = "正在获取天气";
|
||||
this.updateEnvironmentLabel();
|
||||
const point = await new Promise((resolve, reject) =>
|
||||
wx.getLocation({
|
||||
type: "gcj02",
|
||||
success: resolve,
|
||||
fail: reject,
|
||||
}),
|
||||
);
|
||||
positioned = true;
|
||||
const result = await secure("/environment", "GET", {
|
||||
latitude: point.latitude,
|
||||
longitude: point.longitude,
|
||||
});
|
||||
this.setData({ locationName: result.location_name });
|
||||
this.weatherLabel = result.weather
|
||||
? `${result.weather.description} ${result.weather.temperature}°C`
|
||||
: "天气暂不可用";
|
||||
this.environmentAt = result.weather ? Date.now() : 0;
|
||||
}
|
||||
try {
|
||||
this.applyEnvironment(await environment.refresh(tapped));
|
||||
} catch (error) {
|
||||
const denied = /denied|deny|auth/.test(
|
||||
error.errMsg || error.message || "",
|
||||
);
|
||||
this.setData({
|
||||
locationName: positioned
|
||||
? "位置暂不可用"
|
||||
: denied
|
||||
? "开启定位"
|
||||
: "定位暂不可用",
|
||||
});
|
||||
this.weatherLabel = denied ? "轻触开启定位" : "轻触重试";
|
||||
if (tapped && !denied)
|
||||
wx.showToast({
|
||||
title: positioned ? "位置和天气获取失败,请重试" : "定位失败,请重试",
|
||||
icon: "none",
|
||||
});
|
||||
if (this.disposed) return;
|
||||
if (cached) {
|
||||
this.applyEnvironment(cached);
|
||||
} else {
|
||||
const denied = /denied|deny|auth/.test(
|
||||
error.errMsg || error.message || "",
|
||||
);
|
||||
this.setData({ locationName: denied ? "开启定位" : "定位暂不可用" });
|
||||
this.weatherLabel = denied ? "轻触开启定位" : "轻触重试";
|
||||
this.updateEnvironmentLabel();
|
||||
}
|
||||
if (tapped) toast(error);
|
||||
} finally {
|
||||
this.environmentBusy = false;
|
||||
this.updateEnvironmentLabel();
|
||||
}
|
||||
},
|
||||
async load() {
|
||||
@@ -129,12 +108,7 @@ Page({
|
||||
phase: "ready",
|
||||
});
|
||||
// Location permission and weather never hold up the first usable screen.
|
||||
if (
|
||||
!this.environmentAt ||
|
||||
Date.now() - this.environmentAt > 15 * 60 * 1000
|
||||
) {
|
||||
this.refreshEnvironment();
|
||||
}
|
||||
this.refreshEnvironment();
|
||||
} catch (error) {
|
||||
if (this.disposed) return;
|
||||
this.setData({
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
const { secure } = require("./api");
|
||||
|
||||
const STORAGE_KEY = "pet_environment_v1";
|
||||
let value;
|
||||
let attempted = false;
|
||||
let pending;
|
||||
let failure;
|
||||
|
||||
function cached() {
|
||||
if (value === undefined) {
|
||||
const saved = wx.getStorageSync(STORAGE_KEY);
|
||||
value =
|
||||
saved &&
|
||||
typeof saved.locationName === "string" &&
|
||||
typeof saved.weatherLabel === "string" &&
|
||||
Number.isFinite(saved.updatedAt)
|
||||
? saved
|
||||
: null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async function fetchEnvironment(tapped) {
|
||||
const settings = await new Promise((resolve, reject) =>
|
||||
wx.getSetting({ success: resolve, fail: reject }),
|
||||
);
|
||||
if (settings.authSetting["scope.userLocation"] === false) {
|
||||
if (!tapped) throw new Error("location-denied");
|
||||
const enabled = await new Promise((resolve, reject) =>
|
||||
wx.openSetting({ success: resolve, fail: reject }),
|
||||
);
|
||||
if (!enabled.authSetting["scope.userLocation"])
|
||||
throw new Error("location-denied");
|
||||
}
|
||||
const point = await new Promise((resolve, reject) =>
|
||||
wx.getLocation({ type: "gcj02", success: resolve, fail: reject }),
|
||||
);
|
||||
const result = await secure("/environment", "GET", {
|
||||
latitude: point.latitude,
|
||||
longitude: point.longitude,
|
||||
});
|
||||
if (!result || !result.location_name)
|
||||
throw new Error("位置和天气获取失败,请重试");
|
||||
const previous = cached();
|
||||
value = {
|
||||
locationName: result.location_name,
|
||||
weatherLabel: result.weather
|
||||
? `${result.weather.description} ${result.weather.temperature}°C`
|
||||
: previous && previous.locationName === result.location_name
|
||||
? previous.weatherLabel
|
||||
: "天气暂不可用",
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
// Store display data only; precise coordinates are not persisted here.
|
||||
try {
|
||||
wx.setStorageSync(STORAGE_KEY, value);
|
||||
} catch (_) {}
|
||||
return value;
|
||||
}
|
||||
|
||||
function refresh(tapped = false) {
|
||||
if (pending) return pending;
|
||||
// Module lifetime follows the app, including recreated pages and warm resumes.
|
||||
// Failed automatic attempts also wait for a tap or the next cold start.
|
||||
if (attempted && !tapped)
|
||||
return failure ? Promise.reject(failure) : Promise.resolve(cached());
|
||||
attempted = true;
|
||||
pending = fetchEnvironment(tapped)
|
||||
.then(
|
||||
(result) => {
|
||||
failure = null;
|
||||
return result;
|
||||
},
|
||||
(error) => {
|
||||
failure = error;
|
||||
throw error;
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
pending = null;
|
||||
});
|
||||
return pending;
|
||||
}
|
||||
|
||||
module.exports = { cached, refresh };
|
||||
Reference in New Issue
Block a user