forked from abner/for-legacy-web
147 lines
4.0 KiB
TypeScript
147 lines
4.0 KiB
TypeScript
/**
|
|
* This file monitors changes to settings and syncs them to the server.
|
|
*/
|
|
import isEqual from "lodash.isequal";
|
|
import { UserSettings } from "revolt-api/types/Sync";
|
|
import { ClientboundNotification } from "revolt.js/dist/websocket/notifications";
|
|
|
|
import { useContext, useEffect } from "preact/hooks";
|
|
|
|
import { dispatch } from "../../redux";
|
|
import { connectState } from "../../redux/connector";
|
|
import { Notifications } from "../../redux/reducers/notifications";
|
|
import { Settings } from "../../redux/reducers/settings";
|
|
import {
|
|
DEFAULT_ENABLED_SYNC,
|
|
SyncData,
|
|
SyncKeys,
|
|
SyncOptions,
|
|
} from "../../redux/reducers/sync";
|
|
|
|
import { Language } from "../Locale";
|
|
import { AppContext, ClientStatus, StatusContext } from "./RevoltClient";
|
|
|
|
type Props = {
|
|
settings: Settings;
|
|
locale: Language;
|
|
sync: SyncOptions;
|
|
notifications: Notifications;
|
|
};
|
|
|
|
const lastValues: { [key in SyncKeys]?: any } = {};
|
|
|
|
export function mapSync(
|
|
packet: UserSettings,
|
|
revision?: Record<string, number>,
|
|
) {
|
|
const update: { [key in SyncKeys]?: [number, SyncData[key]] } = {};
|
|
for (const key of Object.keys(packet)) {
|
|
const [timestamp, obj] = packet[key];
|
|
if (timestamp < (revision ?? {})[key] ?? 0) {
|
|
continue;
|
|
}
|
|
|
|
let object;
|
|
if (obj[0] === "{") {
|
|
object = JSON.parse(obj);
|
|
} else {
|
|
object = obj;
|
|
}
|
|
|
|
lastValues[key as SyncKeys] = object;
|
|
update[key as SyncKeys] = [timestamp, object];
|
|
}
|
|
|
|
return update;
|
|
}
|
|
|
|
function SyncManager(props: Props) {
|
|
const client = useContext(AppContext);
|
|
const status = useContext(StatusContext);
|
|
|
|
useEffect(() => {
|
|
if (status === ClientStatus.ONLINE) {
|
|
client
|
|
.syncFetchSettings(
|
|
DEFAULT_ENABLED_SYNC.filter(
|
|
(x) => !props.sync?.disabled?.includes(x),
|
|
),
|
|
)
|
|
.then((data) => {
|
|
dispatch({
|
|
type: "SYNC_UPDATE",
|
|
update: mapSync(data),
|
|
});
|
|
});
|
|
|
|
client
|
|
.syncFetchUnreads()
|
|
.then((unreads) => dispatch({ type: "UNREADS_SET", unreads }));
|
|
}
|
|
}, [status]);
|
|
|
|
function syncChange(key: SyncKeys, data: any) {
|
|
const timestamp = +new Date();
|
|
dispatch({
|
|
type: "SYNC_SET_REVISION",
|
|
key,
|
|
timestamp,
|
|
});
|
|
|
|
client.syncSetSettings(
|
|
{
|
|
[key]: data,
|
|
},
|
|
timestamp,
|
|
);
|
|
}
|
|
|
|
const disabled = props.sync.disabled ?? [];
|
|
for (const [key, object] of [
|
|
["appearance", props.settings.appearance],
|
|
["theme", props.settings.theme],
|
|
["locale", props.locale],
|
|
["notifications", props.notifications],
|
|
] as [SyncKeys, any][]) {
|
|
useEffect(() => {
|
|
if (disabled.indexOf(key) === -1) {
|
|
if (typeof lastValues[key] !== "undefined") {
|
|
if (!isEqual(lastValues[key], object)) {
|
|
syncChange(key, object);
|
|
}
|
|
}
|
|
}
|
|
|
|
lastValues[key] = object;
|
|
}, [disabled, object]);
|
|
}
|
|
|
|
useEffect(() => {
|
|
function onPacket(packet: ClientboundNotification) {
|
|
if (packet.type === "UserSettingsUpdate") {
|
|
const update: { [key in SyncKeys]?: [number, SyncData[key]] } =
|
|
mapSync(packet.update, props.sync.revision);
|
|
|
|
dispatch({
|
|
type: "SYNC_UPDATE",
|
|
update,
|
|
});
|
|
}
|
|
}
|
|
|
|
client.addListener("packet", onPacket);
|
|
return () => client.removeListener("packet", onPacket);
|
|
}, [disabled, props.sync]);
|
|
|
|
return null;
|
|
}
|
|
|
|
export default connectState(SyncManager, (state) => {
|
|
return {
|
|
settings: state.settings,
|
|
locale: state.locale,
|
|
sync: state.sync,
|
|
notifications: state.notifications,
|
|
};
|
|
});
|