Fix user labels storage issue.
This commit is contained in:
parent
e03fcec09c
commit
5fb151807e
|
@ -40,6 +40,9 @@
|
||||||
"xo": {
|
"xo": {
|
||||||
"extends": "@bauke/eslint-config",
|
"extends": "@bauke/eslint-config",
|
||||||
"prettier": true,
|
"prettier": true,
|
||||||
|
"rules": {
|
||||||
|
"no-await-in-loop": "off"
|
||||||
|
},
|
||||||
"space": true
|
"space": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import debounce from "debounce";
|
import debounce from "debounce";
|
||||||
import {Component, render} from "preact";
|
import {Component, render} from "preact";
|
||||||
import {type Value} from "@holllo/webextension-storage";
|
import {type UserLabelsData, saveUserLabels} from "../../storage/common.js";
|
||||||
import {type UserLabelsData} from "../../storage/common.js";
|
|
||||||
import {
|
import {
|
||||||
createElementFromString,
|
createElementFromString,
|
||||||
isColorBright,
|
isColorBright,
|
||||||
|
@ -13,7 +12,7 @@ import {
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
anonymizeUsernamesEnabled: boolean;
|
anonymizeUsernamesEnabled: boolean;
|
||||||
userLabels: Value<UserLabelsData>;
|
userLabels: UserLabelsData;
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
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
|
// Sort the labels by priority or alphabetically, so 2 labels with the same
|
||||||
// priority will be sorted alphabetically.
|
// priority will be sorted alphabetically.
|
||||||
const sortedLabels = userLabels.value.sort((a, b): number => {
|
const sortedLabels = userLabels.sort((a, b): number => {
|
||||||
if (inTopicListing) {
|
if (inTopicListing) {
|
||||||
// If we're in the topic listing sort with highest priority first.
|
// If we're in the topic listing sort with highest priority first.
|
||||||
if (a.priority !== b.priority) {
|
if (a.priority !== b.priority) {
|
||||||
|
@ -97,7 +96,7 @@ export class UserLabelsFeature extends Component<Props, State> {
|
||||||
|
|
||||||
const userLabels = sortedLabels.filter(
|
const userLabels = sortedLabels.filter(
|
||||||
(value) =>
|
(value) =>
|
||||||
value.username === username &&
|
value.username.toLowerCase() === username &&
|
||||||
(onlyID === undefined ? true : value.id === onlyID),
|
(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) {
|
if (this.state.target === target && !this.state.hidden) {
|
||||||
this.hide();
|
this.hide();
|
||||||
} else {
|
} else {
|
||||||
const label = this.props.userLabels.value.find(
|
const label = this.props.userLabels.find((value) => value.id === id);
|
||||||
(value) => value.id === id,
|
|
||||||
);
|
|
||||||
if (label === undefined) {
|
if (label === undefined) {
|
||||||
log(
|
log(
|
||||||
"User Labels: Tried to edit label with ID that could not be found.",
|
"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 no ID is present then save a new label otherwise edit the existing one.
|
||||||
if (id === undefined) {
|
if (id === undefined) {
|
||||||
let newId = 1;
|
let newId = 1;
|
||||||
if (userLabels.value.length > 0) {
|
if (userLabels.length > 0) {
|
||||||
newId = userLabels.value.sort((a, b) => b.id - a.id)[0].id + 1;
|
newId = userLabels.sort((a, b) => b.id - a.id)[0].id + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
userLabels.value.push({
|
userLabels.push({
|
||||||
color,
|
color,
|
||||||
id: newId,
|
id: newId,
|
||||||
priority,
|
priority,
|
||||||
|
@ -266,9 +263,9 @@ export class UserLabelsFeature extends Component<Props, State> {
|
||||||
|
|
||||||
this.addLabelsToUsernames(querySelectorAll(".link-user"), newId);
|
this.addLabelsToUsernames(querySelectorAll(".link-user"), newId);
|
||||||
} else {
|
} else {
|
||||||
const index = userLabels.value.findIndex((value) => value.id === id);
|
const index = userLabels.findIndex((value) => value.id === id);
|
||||||
userLabels.value.splice(index, 1);
|
userLabels.splice(index, 1);
|
||||||
userLabels.value.push({
|
userLabels.push({
|
||||||
id,
|
id,
|
||||||
color,
|
color,
|
||||||
priority,
|
priority,
|
||||||
|
@ -289,7 +286,7 @@ export class UserLabelsFeature extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await userLabels.save();
|
await saveUserLabels(userLabels);
|
||||||
this.props.userLabels = userLabels;
|
this.props.userLabels = userLabels;
|
||||||
this.hide();
|
this.hide();
|
||||||
};
|
};
|
||||||
|
@ -303,7 +300,7 @@ export class UserLabelsFeature extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const {userLabels} = this.props;
|
const {userLabels} = this.props;
|
||||||
const index = userLabels.value.findIndex((value) => value.id === id);
|
const index = userLabels.findIndex((value) => value.id === id);
|
||||||
if (index === undefined) {
|
if (index === undefined) {
|
||||||
log(
|
log(
|
||||||
`User Labels: Tried to remove label with ID ${id} that could not be found.`,
|
`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();
|
value.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
userLabels.value.splice(index, 1);
|
userLabels.splice(index, 1);
|
||||||
await userLabels.save();
|
await saveUserLabels(userLabels);
|
||||||
this.props.userLabels = userLabels;
|
this.props.userLabels = userLabels;
|
||||||
this.hide();
|
this.hide();
|
||||||
};
|
};
|
||||||
|
|
|
@ -102,7 +102,7 @@ async function initialize() {
|
||||||
<AutocompleteFeature
|
<AutocompleteFeature
|
||||||
anonymizeUsernamesEnabled={anonymizeUsernamesEnabled}
|
anonymizeUsernamesEnabled={anonymizeUsernamesEnabled}
|
||||||
knownGroups={knownGroups.value}
|
knownGroups={knownGroups.value}
|
||||||
userLabels={userLabels.value}
|
userLabels={userLabels}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
type UserLabel,
|
type UserLabel,
|
||||||
fromStorage,
|
fromStorage,
|
||||||
Feature,
|
Feature,
|
||||||
|
saveUserLabels,
|
||||||
} from "../storage/common.js";
|
} from "../storage/common.js";
|
||||||
import "../scss/index.scss";
|
import "../scss/index.scss";
|
||||||
import "../scss/user-label-editor.scss";
|
import "../scss/user-label-editor.scss";
|
||||||
|
@ -21,7 +22,7 @@ window.addEventListener("load", async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
userLabels: Value<UserLabelsData>;
|
userLabels: UserLabelsData;
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -37,7 +38,7 @@ class App extends Component<Props, State> {
|
||||||
this.state = {
|
this.state = {
|
||||||
hasUnsavedChanges: false,
|
hasUnsavedChanges: false,
|
||||||
newLabelUsername: "",
|
newLabelUsername: "",
|
||||||
userLabels: props.userLabels.value,
|
userLabels: props.userLabels,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,8 +102,8 @@ class App extends Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
saveUserLabels = () => {
|
saveUserLabels = () => {
|
||||||
this.props.userLabels.value = this.state.userLabels;
|
this.props.userLabels = this.state.userLabels;
|
||||||
void this.props.userLabels.save();
|
void saveUserLabels(this.props.userLabels);
|
||||||
this.setState({hasUnsavedChanges: false});
|
this.setState({hasUnsavedChanges: false});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
.group {
|
.group {
|
||||||
border: 1px solid var(--blue);
|
border: 1px solid var(--blue);
|
||||||
|
overflow: scroll;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
|
|
|
@ -45,6 +45,50 @@ export type UsernameColor = {
|
||||||
|
|
||||||
export type UsernameColorsData = 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 = {
|
export const storageValues = {
|
||||||
[Data.EnabledFeatures]: createValue({
|
[Data.EnabledFeatures]: createValue({
|
||||||
deserialize: (input) => new Set(JSON.parse(input) as Feature[]),
|
deserialize: (input) => new Set(JSON.parse(input) as Feature[]),
|
||||||
|
@ -79,13 +123,8 @@ export const storageValues = {
|
||||||
},
|
},
|
||||||
storage: browser.storage.sync,
|
storage: browser.storage.sync,
|
||||||
}),
|
}),
|
||||||
[Feature.UserLabels]: createValue({
|
// eslint-disable-next-line unicorn/prefer-top-level-await
|
||||||
deserialize: (input) => JSON.parse(input) as UserLabelsData,
|
[Feature.UserLabels]: collectUserLabels(),
|
||||||
serialize: (input) => JSON.stringify(Array.from(input)),
|
|
||||||
key: Feature.UserLabels,
|
|
||||||
value: [],
|
|
||||||
storage: browser.storage.sync,
|
|
||||||
}),
|
|
||||||
[Feature.UsernameColors]: createValue({
|
[Feature.UsernameColors]: createValue({
|
||||||
deserialize: (input) => JSON.parse(input) as UsernameColorsData,
|
deserialize: (input) => JSON.parse(input) as UsernameColorsData,
|
||||||
serialize: (input) => JSON.stringify(Array.from(input)),
|
serialize: (input) => JSON.stringify(Array.from(input)),
|
||||||
|
|
Loading…
Reference in New Issue