1
Fork 0

Fix user labels storage issue.

This commit is contained in:
Bauke 2023-06-24 14:00:27 +02:00
parent e03fcec09c
commit 5fb151807e
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
6 changed files with 72 additions and 30 deletions

View File

@ -40,6 +40,9 @@
"xo": {
"extends": "@bauke/eslint-config",
"prettier": true,
"rules": {
"no-await-in-loop": "off"
},
"space": true
}
}

View File

@ -1,7 +1,6 @@
import debounce from "debounce";
import {Component, render} from "preact";
import {type Value} from "@holllo/webextension-storage";
import {type UserLabelsData} from "../../storage/common.js";
import {type UserLabelsData, saveUserLabels} from "../../storage/common.js";
import {
createElementFromString,
isColorBright,
@ -13,7 +12,7 @@ import {
type Props = {
anonymizeUsernamesEnabled: boolean;
userLabels: Value<UserLabelsData>;
userLabels: UserLabelsData;
};
type State = {
@ -70,7 +69,7 @@ export class UserLabelsFeature extends Component<Props, State> {
// Sort the labels by priority or alphabetically, so 2 labels with the same
// priority will be sorted alphabetically.
const sortedLabels = userLabels.value.sort((a, b): number => {
const sortedLabels = userLabels.sort((a, b): number => {
if (inTopicListing) {
// If we're in the topic listing sort with highest priority first.
if (a.priority !== b.priority) {
@ -97,7 +96,7 @@ export class UserLabelsFeature extends Component<Props, State> {
const userLabels = sortedLabels.filter(
(value) =>
value.username === username &&
value.username.toLowerCase() === username &&
(onlyID === undefined ? true : value.id === onlyID),
);
@ -192,9 +191,7 @@ export class UserLabelsFeature extends Component<Props, State> {
if (this.state.target === target && !this.state.hidden) {
this.hide();
} else {
const label = this.props.userLabels.value.find(
(value) => value.id === id,
);
const label = this.props.userLabels.find((value) => value.id === id);
if (label === undefined) {
log(
"User Labels: Tried to edit label with ID that could not be found.",
@ -252,11 +249,11 @@ export class UserLabelsFeature extends Component<Props, State> {
// If no ID is present then save a new label otherwise edit the existing one.
if (id === undefined) {
let newId = 1;
if (userLabels.value.length > 0) {
newId = userLabels.value.sort((a, b) => b.id - a.id)[0].id + 1;
if (userLabels.length > 0) {
newId = userLabels.sort((a, b) => b.id - a.id)[0].id + 1;
}
userLabels.value.push({
userLabels.push({
color,
id: newId,
priority,
@ -266,9 +263,9 @@ export class UserLabelsFeature extends Component<Props, State> {
this.addLabelsToUsernames(querySelectorAll(".link-user"), newId);
} else {
const index = userLabels.value.findIndex((value) => value.id === id);
userLabels.value.splice(index, 1);
userLabels.value.push({
const index = userLabels.findIndex((value) => value.id === id);
userLabels.splice(index, 1);
userLabels.push({
id,
color,
priority,
@ -289,7 +286,7 @@ export class UserLabelsFeature extends Component<Props, State> {
}
}
await userLabels.save();
await saveUserLabels(userLabels);
this.props.userLabels = userLabels;
this.hide();
};
@ -303,7 +300,7 @@ export class UserLabelsFeature extends Component<Props, State> {
}
const {userLabels} = this.props;
const index = userLabels.value.findIndex((value) => value.id === id);
const index = userLabels.findIndex((value) => value.id === id);
if (index === undefined) {
log(
`User Labels: Tried to remove label with ID ${id} that could not be found.`,
@ -316,8 +313,8 @@ export class UserLabelsFeature extends Component<Props, State> {
value.remove();
}
userLabels.value.splice(index, 1);
await userLabels.save();
userLabels.splice(index, 1);
await saveUserLabels(userLabels);
this.props.userLabels = userLabels;
this.hide();
};

View File

@ -102,7 +102,7 @@ async function initialize() {
<AutocompleteFeature
anonymizeUsernamesEnabled={anonymizeUsernamesEnabled}
knownGroups={knownGroups.value}
userLabels={userLabels.value}
userLabels={userLabels}
/>
);
}

View File

@ -10,6 +10,7 @@ import {
type UserLabel,
fromStorage,
Feature,
saveUserLabels,
} from "../storage/common.js";
import "../scss/index.scss";
import "../scss/user-label-editor.scss";
@ -21,7 +22,7 @@ window.addEventListener("load", async () => {
});
type Props = {
userLabels: Value<UserLabelsData>;
userLabels: UserLabelsData;
};
type State = {
@ -37,7 +38,7 @@ class App extends Component<Props, State> {
this.state = {
hasUnsavedChanges: false,
newLabelUsername: "",
userLabels: props.userLabels.value,
userLabels: props.userLabels,
};
}
@ -101,8 +102,8 @@ class App extends Component<Props, State> {
};
saveUserLabels = () => {
this.props.userLabels.value = this.state.userLabels;
void this.props.userLabels.save();
this.props.userLabels = this.state.userLabels;
void saveUserLabels(this.props.userLabels);
this.setState({hasUnsavedChanges: false});
};

View File

@ -33,6 +33,7 @@
.group {
border: 1px solid var(--blue);
overflow: scroll;
padding: 16px;
h2 {
@ -40,6 +41,7 @@
display: flex;
gap: 8px;
margin-bottom: 16px;
overflow: scroll;
}
input {

View File

@ -45,6 +45,50 @@ export type UsernameColor = {
export type UsernameColorsData = UsernameColor[];
/**
* Get all user labels from storage and combine them into a single array.
*/
export async function collectUserLabels(): Promise<UserLabelsData> {
const storage = await browser.storage.sync.get();
const userLabels = [];
for (const [key, value] of Object.entries(storage)) {
if (!key.startsWith(Feature.UserLabels)) {
continue;
}
userLabels.push(JSON.parse(value as string) as UserLabel);
}
return userLabels;
}
/**
* Save all user labels to storage under individual keys.
*
* They are stored under individual keys so that we don't run into storage quota
* limits. If it was stored under a single key we would only be able to fit
* around 80-100 labels before hitting the limit.
* @param userLabels The user labels array to save.
*/
export async function saveUserLabels(
userLabels: UserLabelsData,
): Promise<void> {
const storage = await browser.storage.sync.get();
for (const key of Object.keys(storage)) {
if (!key.startsWith(Feature.UserLabels)) {
continue;
}
await browser.storage.sync.remove(key);
}
for (const label of userLabels) {
await browser.storage.sync.set({
[`${Feature.UserLabels}-${label.id}`]: JSON.stringify(label),
});
}
}
export const storageValues = {
[Data.EnabledFeatures]: createValue({
deserialize: (input) => new Set(JSON.parse(input) as Feature[]),
@ -79,13 +123,8 @@ export const storageValues = {
},
storage: browser.storage.sync,
}),
[Feature.UserLabels]: createValue({
deserialize: (input) => JSON.parse(input) as UserLabelsData,
serialize: (input) => JSON.stringify(Array.from(input)),
key: Feature.UserLabels,
value: [],
storage: browser.storage.sync,
}),
// eslint-disable-next-line unicorn/prefer-top-level-await
[Feature.UserLabels]: collectUserLabels(),
[Feature.UsernameColors]: createValue({
deserialize: (input) => JSON.parse(input) as UsernameColorsData,
serialize: (input) => JSON.stringify(Array.from(input)),