mirror of
https://github.com/wbbaddons/Tims-Chat.git
synced 2024-11-12 15:50:08 +00:00
219 lines
5.1 KiB
JavaScript
219 lines
5.1 KiB
JavaScript
/*
|
||
* Copyright (c) 2010-2024 Tim Düsterhus.
|
||
*
|
||
* Use of this software is governed by the Business Source License
|
||
* included in the LICENSE file.
|
||
*
|
||
* Change Date: 2028-01-13
|
||
*
|
||
* On the date above, in accordance with the Business Source
|
||
* License, use of this software will be governed by version 2
|
||
* or later of the General Public License.
|
||
*/
|
||
|
||
define(['WoltLabSuite/Core/Core', './LocalStorageEmulator'], function (
|
||
Core,
|
||
LocalStorageEmulator
|
||
) {
|
||
'use strict'
|
||
|
||
const DEPENDENCIES = []
|
||
class LocalStorage {
|
||
constructor(subprefix) {
|
||
this.subprefix = subprefix
|
||
this.hasLocalStorage = false
|
||
this.setupStorage()
|
||
}
|
||
|
||
static isQuotaExceeded(error) {
|
||
return (
|
||
error instanceof DOMException &&
|
||
// everything except Firefox
|
||
(error.code === 22 ||
|
||
// Firefox
|
||
error.code === 1014 ||
|
||
// everything except Firefox
|
||
error.name === 'QuotaExceededError' ||
|
||
// Firefox
|
||
error.name === 'NS_ERROR_DOM_QUOTA_REACHED')
|
||
)
|
||
}
|
||
|
||
static isAvailable() {
|
||
try {
|
||
const x = '__storage_test__'
|
||
window.localStorage.setItem(x, x)
|
||
window.localStorage.removeItem(x)
|
||
return true
|
||
} catch (error) {
|
||
return LocalStorage.isQuotaExceeded(error)
|
||
}
|
||
}
|
||
|
||
setupStorage() {
|
||
if (LocalStorage.isAvailable()) {
|
||
this.storage = window.localStorage
|
||
this.hasLocalStorage = true
|
||
} else {
|
||
console.info('Falling back to in-memory local storage emulation')
|
||
this.storage = new LocalStorageEmulator()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Return the prefix to use for the local storage
|
||
*
|
||
* @returns {string} The storage prefix
|
||
*/
|
||
get storagePrefix() {
|
||
let prefix = ''
|
||
|
||
// WSC 3.1
|
||
if (typeof Core.getStoragePrefix === 'function') {
|
||
prefix = Core.getStoragePrefix()
|
||
}
|
||
|
||
return `${prefix}be.bastelstu.Chat.${this.subprefix}`
|
||
}
|
||
|
||
/**
|
||
* Calls listener, whenever key changes.
|
||
*
|
||
* @param {string} key The key to observe.
|
||
* @param {*} listener The listener to call.
|
||
*/
|
||
observe(key, listener) {
|
||
window.addEventListener('storage', (event) => {
|
||
if (event.storageArea !== window.localStorage) return
|
||
if (event.key !== `${this.storagePrefix}${key}`) return
|
||
|
||
listener(event)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* Sets the value of a setting
|
||
*
|
||
* @param {string} key The key of the setting to set
|
||
* @param {string} value The new value of the setting
|
||
* @returns {string}
|
||
*/
|
||
set(key, value) {
|
||
try {
|
||
this.storage.setItem(
|
||
`${this.storagePrefix}${key}`,
|
||
JSON.stringify(value)
|
||
)
|
||
} catch (error) {
|
||
if (!LocalStorage.isQuotaExceeded(error)) throw error
|
||
|
||
console.warn(
|
||
`Your localStorage has exceeded the size quota for this domain`
|
||
)
|
||
console.warn(
|
||
`We are falling back to an in-memory storage, this does not persist data!`
|
||
)
|
||
console.error(error)
|
||
|
||
const storage = new LocalStorageEmulator()
|
||
|
||
// Make a copy of the current localStorage
|
||
for (let i = 0; i < localStorage.length; i++) {
|
||
const key = localStorage.key(i)
|
||
const value = localStorage.getItem(key)
|
||
storage.setItem(key, value)
|
||
}
|
||
|
||
// Replace the localStorage with our in-memory variant
|
||
this.storage = storage
|
||
}
|
||
|
||
return this.get(key)
|
||
}
|
||
|
||
/**
|
||
* Retrieves the value of a setting
|
||
*
|
||
* @param {string} key The key of the setting to retrieve
|
||
* @returns {string} The current value of the setting
|
||
*/
|
||
get(key) {
|
||
const value = this.storage.getItem(`${this.storagePrefix}${key}`)
|
||
|
||
if (value == null) return null
|
||
return JSON.parse(value)
|
||
}
|
||
|
||
/**
|
||
* Returns whether the given setting has a value.
|
||
*
|
||
* @param {string} key The key of the setting to check
|
||
* @returns {boolean}
|
||
*/
|
||
has(key) {
|
||
return this.storage.getItem(`${this.storagePrefix}${key}`) != null
|
||
}
|
||
|
||
/**
|
||
* Removes a single setting
|
||
*
|
||
* @param {string} key The key of the setting to remove
|
||
* @returns {string} The last value of the provided setting
|
||
*/
|
||
remove(key) {
|
||
const value = this.get(key)
|
||
const storageKey = `${this.storagePrefix}${key}`
|
||
|
||
this.storage.removeItem(storageKey)
|
||
|
||
return value
|
||
}
|
||
|
||
/**
|
||
* Removes all of the chat settings with the right prefix
|
||
* and try to use the real localStorage again, if the qouta isn’t exceeded anymore
|
||
*/
|
||
clear() {
|
||
const _clear = (target) => {
|
||
for (let key in target) {
|
||
if (
|
||
!key.startsWith(this.storagePrefix) ||
|
||
!target.hasOwnProperty(key)
|
||
)
|
||
continue
|
||
|
||
target.removeItem(key)
|
||
}
|
||
}
|
||
|
||
if (
|
||
this.hasLocalStorage &&
|
||
this.storage instanceof LocalStorageEmulator
|
||
) {
|
||
try {
|
||
// Try to clear the real localStorage
|
||
_clear(localStorage)
|
||
|
||
// Check if we can use the localStorage again
|
||
const x = '__storage_test__'
|
||
window.localStorage.setItem(x, x)
|
||
window.localStorage.removeItem(x)
|
||
|
||
// It should be safe to use the localStorage again, as the storage
|
||
// of this instance (given by the prefix) has been cleared completely
|
||
this.storage = localStorage
|
||
|
||
console.log('Switched back to using the localStorage')
|
||
} catch (error) {
|
||
/* no we can’t */
|
||
}
|
||
}
|
||
|
||
_clear(this.storage)
|
||
}
|
||
}
|
||
LocalStorage.DEPENDENCIES = DEPENDENCIES
|
||
|
||
return LocalStorage
|
||
})
|