mirror of
https://github.com/photoprism/photoprism.git
synced 2025-09-26 21:01:58 +08:00
Frontend: Use canonical key for localStorage
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -15,19 +15,19 @@
|
||||
{{template "app.gohtml" .}}
|
||||
|
||||
<script>{{ if eq .status "success" }}
|
||||
window.localStorage.setItem("sessionId", {{ .session_id }});
|
||||
window.localStorage.setItem("authToken", {{ .access_token }});
|
||||
window.localStorage.setItem("provider", {{ .provider }});
|
||||
window.localStorage.setItem("user", JSON.stringify({{ .user }}));
|
||||
window.localStorage.removeItem("authError");
|
||||
window.localStorage.setItem("session.id", {{ .session_id }});
|
||||
window.localStorage.setItem("session.token", {{ .access_token }});
|
||||
window.localStorage.setItem("session.provider", {{ .provider }});
|
||||
window.localStorage.setItem("session.user", JSON.stringify({{ .user }}));
|
||||
window.localStorage.removeItem("session.error");
|
||||
{{ else if eq .status "failed" }}
|
||||
window.localStorage.setItem("authError", {{ .error }});
|
||||
window.localStorage.setItem("session.error", {{ .error }});
|
||||
{{ else }}
|
||||
window.localStorage.removeItem("sessionId");
|
||||
window.localStorage.removeItem("authToken");
|
||||
window.localStorage.removeItem("provider");
|
||||
window.localStorage.removeItem("user");
|
||||
window.localStorage.removeItem("authError");
|
||||
window.localStorage.removeItem("session.id");
|
||||
window.localStorage.removeItem("session.token");
|
||||
window.localStorage.removeItem("session.provider");
|
||||
window.localStorage.removeItem("session.user");
|
||||
window.localStorage.removeItem("session.error");
|
||||
{{ end }}
|
||||
window.location.href = {{ .config.LoginUri }};
|
||||
</script>
|
||||
|
@@ -163,14 +163,14 @@ $config.update().finally(() => {
|
||||
// Make scroll-pos-restore compatible with bfcache (required to work in PWA mode on iOS).
|
||||
window.addEventListener("pagehide", (ev) => {
|
||||
if (ev.persisted) {
|
||||
localStorage.setItem("lastScrollPosBeforePageHide", JSON.stringify({ x: window.scrollX, y: window.scrollY }));
|
||||
localStorage.setItem("window.scroll.pos", JSON.stringify({ x: window.scrollX, y: window.scrollY }));
|
||||
}
|
||||
});
|
||||
window.addEventListener("pageshow", (ev) => {
|
||||
if (ev.persisted) {
|
||||
const lastSavedScrollPos = localStorage.getItem("lastScrollPosBeforePageHide");
|
||||
const lastSavedScrollPos = localStorage.getItem("window.scroll.pos");
|
||||
if (lastSavedScrollPos !== undefined && lastSavedScrollPos !== null && lastSavedScrollPos !== "") {
|
||||
window.positionToRestore = JSON.parse(localStorage.getItem("lastScrollPosBeforePageHide"));
|
||||
window.positionToRestore = JSON.parse(localStorage.getItem("window.scroll.pos"));
|
||||
// Wait for other things that set the scroll-pos anywhere in the app to fire.
|
||||
setTimeout(() => {
|
||||
if (window.positionToRestore !== undefined) {
|
||||
@@ -186,7 +186,7 @@ $config.update().finally(() => {
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.removeItem("lastScrollPosBeforePageHide");
|
||||
localStorage.removeItem("window.scroll.pos");
|
||||
});
|
||||
|
||||
// Configure client-side routing.
|
||||
|
@@ -47,7 +47,7 @@ const $api = Axios.create({
|
||||
baseURL: c.apiUri,
|
||||
headers: {
|
||||
common: {
|
||||
"X-Auth-Token": window.localStorage.getItem("authToken"),
|
||||
"X-Auth-Token": window.localStorage.getItem("session.token"),
|
||||
"X-Client-Uri": c.jsUri,
|
||||
"X-Client-Version": c.version,
|
||||
},
|
||||
|
@@ -259,4 +259,4 @@ export class Clipboard {
|
||||
}
|
||||
}
|
||||
|
||||
export const PhotoClipboard = reactive(new Clipboard(window.localStorage, "photo_clipboard"));
|
||||
export const PhotoClipboard = reactive(new Clipboard(window.localStorage, "clipboard.photos"));
|
||||
|
@@ -41,7 +41,7 @@ export default class Session {
|
||||
* @param {object} shared
|
||||
*/
|
||||
constructor(storage, config, shared) {
|
||||
this.storageKey = "sessionStorage";
|
||||
this.storageKey = "session";
|
||||
this.loginRedirect = false;
|
||||
this.config = config;
|
||||
this.provider = "";
|
||||
@@ -56,18 +56,21 @@ export default class Session {
|
||||
}
|
||||
|
||||
// Restore authentication from session storage.
|
||||
if (this.applyAuthToken(this.storage.getItem("authToken")) && this.applyId(this.storage.getItem("sessionId"))) {
|
||||
const dataJson = this.storage.getItem("sessionData");
|
||||
if (
|
||||
this.applyAuthToken(this.storage.getItem(this.storageKey + ".token")) &&
|
||||
this.applyId(this.storage.getItem(this.storageKey + ".id"))
|
||||
) {
|
||||
const dataJson = this.storage.getItem(this.storageKey + ".data");
|
||||
if (dataJson !== "undefined") {
|
||||
this.data = JSON.parse(dataJson);
|
||||
}
|
||||
|
||||
const userJson = this.storage.getItem("user");
|
||||
const userJson = this.storage.getItem(this.storageKey + ".user");
|
||||
if (userJson !== "undefined") {
|
||||
this.user = new User(JSON.parse(userJson));
|
||||
}
|
||||
|
||||
const provider = this.storage.getItem("provider");
|
||||
const provider = this.storage.getItem(this.storageKey + ".provider");
|
||||
if (provider !== null) {
|
||||
this.provider = provider;
|
||||
}
|
||||
@@ -123,7 +126,7 @@ export default class Session {
|
||||
|
||||
setAuthToken(authToken) {
|
||||
if (authToken) {
|
||||
this.storage.setItem("authToken", authToken);
|
||||
this.storage.setItem(this.storageKey + ".token", authToken);
|
||||
if (authToken === PublicAuthToken) {
|
||||
this.setId(PublicSessionID);
|
||||
}
|
||||
@@ -154,7 +157,7 @@ export default class Session {
|
||||
}
|
||||
|
||||
setId(id) {
|
||||
this.storage.setItem("sessionId", id);
|
||||
this.storage.setItem(this.storageKey + ".id", id);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@@ -185,20 +188,20 @@ export default class Session {
|
||||
this.authToken = null;
|
||||
this.provider = "";
|
||||
|
||||
// "sessionId" is the SHA256 hash of the auth token.
|
||||
this.storage.removeItem("sessionId");
|
||||
this.storage.removeItem("authToken");
|
||||
this.storage.removeItem("provider");
|
||||
// "session.id" is the SHA256 hash of the auth token.
|
||||
this.storage.removeItem(this.storageKey + ".id");
|
||||
this.storage.removeItem(this.storageKey + ".token");
|
||||
this.storage.removeItem(this.storageKey + ".provider");
|
||||
|
||||
// The "session_id" storage key is deprecated in favor of "authToken",
|
||||
// The "session_id" storage key is deprecated in favor of "session.token",
|
||||
// but should continue to be removed when logging out:
|
||||
this.storage.removeItem("session_id");
|
||||
this.storage.removeItem(this.storageKey + ".id");
|
||||
|
||||
delete $api.defaults.headers.common[RequestHeader];
|
||||
}
|
||||
|
||||
setProvider(provider) {
|
||||
this.storage.setItem("provider", provider);
|
||||
this.storage.setItem(this.storageKey + ".provider", provider);
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@@ -264,7 +267,7 @@ export default class Session {
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this.storage.setItem("sessionData", JSON.stringify(data));
|
||||
this.storage.setItem(this.storageKey + ".data", JSON.stringify(data));
|
||||
|
||||
if (data.user) {
|
||||
this.setUser(data.user);
|
||||
@@ -293,7 +296,7 @@ export default class Session {
|
||||
}
|
||||
|
||||
this.user = new User(user);
|
||||
this.storage.setItem("user", JSON.stringify(user));
|
||||
this.storage.setItem(this.storageKey + ".user", JSON.stringify(user));
|
||||
this.auth = this.isUser();
|
||||
}
|
||||
|
||||
@@ -378,7 +381,7 @@ export default class Session {
|
||||
|
||||
deleteData() {
|
||||
this.data = null;
|
||||
this.storage.removeItem("sessionData");
|
||||
this.storage.removeItem(this.storageKey + ".data");
|
||||
}
|
||||
|
||||
deleteUser() {
|
||||
@@ -389,8 +392,8 @@ export default class Session {
|
||||
|
||||
deleteClipboard() {
|
||||
this.storage.removeItem("clipboard");
|
||||
this.storage.removeItem("photo_clipboard");
|
||||
this.storage.removeItem("album_clipboard");
|
||||
this.storage.removeItem("clipboard.photos");
|
||||
this.storage.removeItem("clipboard.albums");
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
@@ -999,7 +999,7 @@ export default {
|
||||
featFiles: this.$config.feature("files"),
|
||||
featUsage: canManagePhotos && this.$config.feature("files") && this.$config.values?.usage?.filesTotal,
|
||||
isRestricted: isRestricted,
|
||||
isMini: localStorage.getItem("last_navigation_mode") !== "false" || isRestricted,
|
||||
isMini: localStorage.getItem("navigation.mode") !== "false" || isRestricted,
|
||||
isDemo: isDemo,
|
||||
isPro: isPro,
|
||||
isPublic: isPublic,
|
||||
@@ -1106,7 +1106,7 @@ export default {
|
||||
}
|
||||
|
||||
this.isMini = !this.isMini;
|
||||
localStorage.setItem("last_navigation_mode", `${this.isMini}`);
|
||||
localStorage.setItem("navigation.mode", `${this.isMini}`);
|
||||
},
|
||||
showAccountSettings() {
|
||||
if (this.$config.feature("account")) {
|
||||
|
@@ -250,11 +250,11 @@ export default {
|
||||
},
|
||||
getViewType() {
|
||||
let queryParam = this.$route.query["view"] ? this.$route.query["view"] : "";
|
||||
let defaultType = window.localStorage.getItem("photos_view");
|
||||
let storedType = window.localStorage.getItem("album_photos_view");
|
||||
let defaultType = window.localStorage.getItem("photos.view");
|
||||
let storedType = window.localStorage.getItem("album.photos.view");
|
||||
|
||||
if (queryParam) {
|
||||
window.localStorage.setItem("album_photos_view", queryParam);
|
||||
window.localStorage.setItem("album.photos.view", queryParam);
|
||||
return queryParam;
|
||||
} else if (storedType) {
|
||||
return storedType;
|
||||
@@ -422,7 +422,7 @@ export default {
|
||||
this.settings[key] = value;
|
||||
}
|
||||
|
||||
window.localStorage.setItem("album_photos_" + key, this.settings[key]);
|
||||
window.localStorage.setItem("album.photos." + key, this.settings[key]);
|
||||
}
|
||||
},
|
||||
updateFilter(props) {
|
||||
|
@@ -593,7 +593,7 @@ export default {
|
||||
},
|
||||
sortOrder() {
|
||||
const typeName = this.staticFilter?.type;
|
||||
const keyName = "albums_order_" + typeName;
|
||||
const keyName = "albums.order." + typeName;
|
||||
const queryParam = this.$route.query["order"];
|
||||
const storedType = window.localStorage.getItem(keyName);
|
||||
|
||||
@@ -607,7 +607,7 @@ export default {
|
||||
return this.defaultOrder;
|
||||
},
|
||||
searchCount() {
|
||||
const offset = parseInt(window.localStorage.getItem("albums_offset"));
|
||||
const offset = parseInt(window.localStorage.getItem("albums.offset"));
|
||||
|
||||
if (this.offset > 0 || !offset) {
|
||||
return this.batchSize;
|
||||
@@ -617,7 +617,7 @@ export default {
|
||||
},
|
||||
setOffset(offset) {
|
||||
this.offset = offset;
|
||||
window.localStorage.setItem("albums_offset", offset);
|
||||
window.localStorage.setItem("albums.offset", offset);
|
||||
},
|
||||
share(album) {
|
||||
if (!album || !this.canShare) {
|
||||
@@ -843,7 +843,7 @@ export default {
|
||||
this.settings[key] = value;
|
||||
}
|
||||
|
||||
window.localStorage.setItem("albums_" + key, this.settings[key]);
|
||||
window.localStorage.setItem("albums." + key, this.settings[key]);
|
||||
}
|
||||
},
|
||||
updateFilter(props) {
|
||||
|
@@ -229,10 +229,10 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
const authError = window.localStorage.getItem("authError");
|
||||
const authError = window.localStorage.getItem("session.error");
|
||||
if (authError) {
|
||||
this.$notify.error(authError);
|
||||
window.localStorage.removeItem("authError");
|
||||
window.localStorage.removeItem("session.error");
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@@ -329,7 +329,7 @@ export default {
|
||||
this.dialog.edit = true;
|
||||
},
|
||||
searchCount() {
|
||||
const offset = parseInt(window.localStorage.getItem("labels_offset"));
|
||||
const offset = parseInt(window.localStorage.getItem("labels.offset"));
|
||||
|
||||
if (this.offset > 0 || !offset) {
|
||||
return this.batchSize;
|
||||
@@ -339,7 +339,7 @@ export default {
|
||||
},
|
||||
setOffset(offset) {
|
||||
this.offset = offset;
|
||||
window.localStorage.setItem("labels_offset", offset);
|
||||
window.localStorage.setItem("labels.offset", offset);
|
||||
},
|
||||
toggleLike(ev, index) {
|
||||
if (!this.canManage) {
|
||||
@@ -570,7 +570,7 @@ export default {
|
||||
this.settings[key] = value;
|
||||
}
|
||||
|
||||
window.localStorage.setItem("labels_" + key, this.settings[key]);
|
||||
window.localStorage.setItem("labels." + key, this.settings[key]);
|
||||
}
|
||||
},
|
||||
updateFilter(props) {
|
||||
|
@@ -349,7 +349,7 @@ export default {
|
||||
});
|
||||
},
|
||||
searchCount() {
|
||||
const offset = parseInt(window.localStorage.getItem("subjects_offset"));
|
||||
const offset = parseInt(window.localStorage.getItem("people.recognized.offset"));
|
||||
|
||||
if (this.offset > 0 || !offset) {
|
||||
return this.batchSize;
|
||||
@@ -362,7 +362,7 @@ export default {
|
||||
},
|
||||
setOffset(offset) {
|
||||
this.offset = offset;
|
||||
window.localStorage.setItem("subjects_offset", offset);
|
||||
window.localStorage.setItem("people.recognized.offset", offset);
|
||||
},
|
||||
toggleLike(ev, index) {
|
||||
if (!this.canManage) {
|
||||
@@ -606,7 +606,7 @@ export default {
|
||||
this.settings[key] = value;
|
||||
}
|
||||
|
||||
window.localStorage.setItem("people_" + key, this.settings[key]);
|
||||
window.localStorage.setItem("people.recognized." + key, this.settings[key]);
|
||||
}
|
||||
},
|
||||
updateFilter(props) {
|
||||
|
@@ -302,7 +302,7 @@ export default {
|
||||
return this.$refs?.toolbar?.hideExpansionPanel();
|
||||
},
|
||||
searchCount() {
|
||||
const offset = parseInt(window.localStorage.getItem("photos_offset"));
|
||||
const offset = parseInt(window.localStorage.getItem("photos.offset"));
|
||||
if (this.offset > 0 || !offset) {
|
||||
return this.batchSize;
|
||||
}
|
||||
@@ -310,7 +310,7 @@ export default {
|
||||
},
|
||||
setOffset(offset) {
|
||||
this.offset = offset;
|
||||
window.localStorage.setItem("photos_offset", offset);
|
||||
window.localStorage.setItem("photos.offset", offset);
|
||||
},
|
||||
getViewType() {
|
||||
if (this.embedded) {
|
||||
@@ -318,10 +318,10 @@ export default {
|
||||
}
|
||||
|
||||
let queryParam = this.$route.query["view"] ? this.$route.query["view"] : "";
|
||||
let storedType = window.localStorage.getItem("photos_view");
|
||||
let storedType = window.localStorage.getItem("photos.view");
|
||||
|
||||
if (queryParam) {
|
||||
window.localStorage.setItem("photos_view", queryParam);
|
||||
window.localStorage.setItem("photos.view", queryParam);
|
||||
return queryParam;
|
||||
} else if (storedType) {
|
||||
return storedType;
|
||||
@@ -358,23 +358,23 @@ export default {
|
||||
|
||||
switch (this.getContext()) {
|
||||
case "archive":
|
||||
storageKey = "archive_order";
|
||||
storageKey = "archive.order";
|
||||
defaultOrder = "archived";
|
||||
break;
|
||||
case "favorites":
|
||||
storageKey = "favorites_order";
|
||||
storageKey = "favorites.order";
|
||||
defaultOrder = "newest";
|
||||
break;
|
||||
case "hidden":
|
||||
storageKey = "hidden_order";
|
||||
storageKey = "hidden.order";
|
||||
defaultOrder = "added";
|
||||
break;
|
||||
case "review":
|
||||
storageKey = "review_order";
|
||||
storageKey = "review.order";
|
||||
defaultOrder = "added";
|
||||
break;
|
||||
default:
|
||||
storageKey = "photos_order";
|
||||
storageKey = "photos.order";
|
||||
defaultOrder = "newest";
|
||||
}
|
||||
|
||||
@@ -546,7 +546,7 @@ export default {
|
||||
this.settings[key] = value;
|
||||
}
|
||||
|
||||
window.localStorage.setItem("photos_" + key, this.settings[key]);
|
||||
window.localStorage.setItem("photos." + key, this.settings[key]);
|
||||
}
|
||||
},
|
||||
updateFilter(props) {
|
||||
|
@@ -69,6 +69,10 @@ import Thumb from "model/thumb";
|
||||
import PPagePhotos from "page/photos.vue";
|
||||
import MapStyleControl from "component/places/style-control";
|
||||
|
||||
const ProjectionGlobe = "globe";
|
||||
const ProjectionMercator = "mercator";
|
||||
const ProjectionVertical = "vertical-perspective";
|
||||
|
||||
// Pixels the map pans when the up or down arrow is clicked:
|
||||
const deltaDistance = 100;
|
||||
|
||||
@@ -141,6 +145,7 @@ export default {
|
||||
config: this.$config.values,
|
||||
settings: settings.maps,
|
||||
animate: settings.maps.animate,
|
||||
skyRendered: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -164,7 +169,6 @@ export default {
|
||||
.then(() => {
|
||||
this.renderMap();
|
||||
this.openClusterFromUrl();
|
||||
this.renderSky();
|
||||
})
|
||||
.catch((err) => {
|
||||
this.mapError = err;
|
||||
@@ -179,9 +183,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
renderSky() {
|
||||
if (sky.render && this.$refs.background) {
|
||||
if (!this.skyRendered && sky.render && this.$refs.background) {
|
||||
this.$nextTick(() => {
|
||||
sky.render(this.$refs.background, 320);
|
||||
this.skyRendered = true;
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -244,13 +249,27 @@ export default {
|
||||
|
||||
const currentProjection = this.map.getProjection()?.type;
|
||||
|
||||
let newProjection;
|
||||
|
||||
if (currentProjection === "mercator" || !currentProjection) {
|
||||
newProjection = "globe";
|
||||
this.map.setZoom(3);
|
||||
if (currentProjection === ProjectionMercator || !currentProjection) {
|
||||
this.setProjection(ProjectionGlobe);
|
||||
} else {
|
||||
newProjection = "mercator";
|
||||
this.setProjection(ProjectionMercator);
|
||||
}
|
||||
},
|
||||
setProjection(newProjection) {
|
||||
const currentProjection = this.map.getProjection()?.type;
|
||||
|
||||
if (currentProjection === newProjection) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (newProjection) {
|
||||
case ProjectionGlobe:
|
||||
this.map.setZoom(3);
|
||||
break;
|
||||
case ProjectionMercator:
|
||||
break;
|
||||
case ProjectionVertical:
|
||||
break;
|
||||
}
|
||||
|
||||
this.map.setProjection({ type: newProjection });
|
||||
@@ -263,7 +282,7 @@ export default {
|
||||
|
||||
if (btn && btn instanceof HTMLElement) {
|
||||
switch (newProjection) {
|
||||
case "globe":
|
||||
case ProjectionGlobe:
|
||||
btn.classList.add("maplibregl-ctrl-globe-enabled");
|
||||
btn.classList.remove("maplibregl-ctrl-globe");
|
||||
btn.classList.title = this.map._getUIString("GlobeControl.Disable");
|
||||
@@ -729,6 +748,7 @@ export default {
|
||||
.get("geo", options)
|
||||
.then((response) => {
|
||||
if (!response.data.features || response.data.features.length === 0) {
|
||||
this.initialized = true;
|
||||
this.loading = false;
|
||||
|
||||
this.$notify.warn(this.$gettext("No pictures found"));
|
||||
@@ -756,6 +776,7 @@ export default {
|
||||
this.updateMarkers();
|
||||
})
|
||||
.catch(() => {
|
||||
this.initialized = true;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
@@ -817,6 +838,15 @@ export default {
|
||||
|
||||
this.map.on("load", () => this.onMapLoad());
|
||||
},
|
||||
onProjectionChange(ev) {
|
||||
// Remember last used projection.
|
||||
localStorage.setItem("places.projection", ev.newProjection);
|
||||
|
||||
// Render sky if new project is globe.
|
||||
if (ev.newProjection === ProjectionGlobe) {
|
||||
this.renderSky();
|
||||
}
|
||||
},
|
||||
getClusterFeatures(clusterId, limit, callback) {
|
||||
this.map
|
||||
.getSource("photos")
|
||||
@@ -1019,7 +1049,16 @@ export default {
|
||||
this.map.on("idle", this.updateMarkers);
|
||||
|
||||
// Load pictures.
|
||||
this.search();
|
||||
this.search().finally(() => {
|
||||
// Call this.onProjectionChange when the projection type changes.
|
||||
this.map.on("projectiontransition", (ev) => this.onProjectionChange(ev));
|
||||
|
||||
// Restore globe projection if last used.
|
||||
const projection = localStorage.getItem("places.projection");
|
||||
if (projection === ProjectionGlobe) {
|
||||
this.setProjection(projection);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@@ -245,18 +245,18 @@ describe("common/session", () => {
|
||||
it("should use session storage", () => {
|
||||
const storage = new StorageShim();
|
||||
const session = new Session(storage, $config);
|
||||
assert.equal(storage.getItem("sessionStorage"), null);
|
||||
assert.equal(storage.getItem("session"), null);
|
||||
session.useSessionStorage();
|
||||
assert.equal(storage.getItem("sessionStorage"), "true");
|
||||
assert.equal(storage.getItem("session"), "true");
|
||||
session.deleteData();
|
||||
});
|
||||
|
||||
it("should use local storage", () => {
|
||||
const storage = new StorageShim();
|
||||
const session = new Session(storage, $config);
|
||||
assert.equal(storage.getItem("sessionStorage"), null);
|
||||
assert.equal(storage.getItem("session"), null);
|
||||
session.useLocalStorage();
|
||||
assert.equal(storage.getItem("sessionStorage"), "false");
|
||||
assert.equal(storage.getItem("session"), "false");
|
||||
session.deleteData();
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user