feeds-fun
Version:
Frontend for the Feeds Fun — web-based news reader
250 lines (195 loc) • 7.23 kB
text/typescript
import _ from "lodash";
import {ref, computed} from "vue";
import {defineStore} from "pinia";
import {computedAsync} from "@vueuse/core";
import {useGlobalState} from "@/stores/globalState";
import * as e from "@/logic/enums";
import * as t from "@/logic/types";
import * as api from "@/logic/api";
export const useGlobalSettingsStore = defineStore("globalSettings", () => {
const globalState = useGlobalState();
// General
const mainPanelMode = ref(e.MainPanelMode.Entries);
const dataVersion = ref(0);
function updateDataVersion() {
dataVersion.value++;
}
// TODO: We may want to remove this API and move user_id to the user settings API
const info = computedAsync(async () => {
if (!globalState.isLoggedIn) {
return null;
}
// force refresh
dataVersion.value;
return await api.getInfo();
}, null);
///////////////////////////////////////////////////////////
// Functionality for interaction with backend side settings
///////////////////////////////////////////////////////////
const _userSettings = computedAsync(async () => {
if (!globalState.isLoggedIn) {
return null;
}
dataVersion.value;
let settings = await api.getUserSettings();
settingsOverrides.value = {};
return settings;
}, null);
const userSettingsPresent = computed(() => {
return _userSettings.value !== null && _userSettings.value !== undefined;
});
function userSettingInfo(kind: string) {
return computed(() => {
if (!_userSettings.value || !(kind in _userSettings.value)) {
return null;
}
const setting = _userSettings.value[kind];
let value = setting.value;
if (kind in settingsOverrides.value) {
value = settingsOverrides.value[kind];
}
return {
kind: setting.kind,
name: setting.name,
type: setting.type,
value: value
} as t.UserSetting;
});
}
function _backgroundSetUserSetting(kind: string, value: t.UserSettingsValue) {
api.setUserSetting({kind: kind, value: value}).catch((error) => {
console.error(`Error in API call setUserSetting for kind "${kind}":`, error);
});
}
// This dict is used for two purposes:
// - To store settings that anonymous user changes while using the site.
// - To close fast reactive loop after calling backendSettings.set.
// Without this, setting a setting will cause weired and complex chain
// of (re)loading data from the backend.
var settingsOverrides = ref<{[key in keyof any]: t.UserSettingsValue}>({});
function setUserSettings(kind: string, newValue: t.UserSettingsValue) {
settingsOverrides.value[kind] = newValue;
if (globalState.isLoggedIn) {
_backgroundSetUserSetting(kind, newValue);
}
if (_userSettings.value) {
_userSettings.value[kind].value = newValue;
}
// We do not call updateDataVersion() here
// Because it causes request of the user settings from the backen
// which is not required
// All reactive code should be triggered by changes in settingsOverrides
}
function backendSettings(kind: string, validator: any, defaultValue: t.UserSettingsValue) {
return computed({
get() {
if (kind in settingsOverrides.value) {
return settingsOverrides.value[kind];
}
if (!globalState.isLoggedIn) {
return defaultValue;
}
if (!_userSettings.value) {
return null;
}
const setting = _userSettings.value[kind];
if (!validator(setting.value)) {
_backgroundSetUserSetting(kind, defaultValue);
return defaultValue;
}
return setting.value;
},
set(newValue) {
if (newValue === null || newValue === undefined) {
console.warn(`Setting "${kind}" is set to null or undefined. This is not allowed.`);
return;
}
setUserSettings(kind, newValue);
}
});
}
function boolBackendSettings(kind: string, defaultValue: t.UserSettingsValue) {
return backendSettings(
kind,
(rawValue: t.UserSettingsValue) => {
return typeof rawValue === "boolean";
},
defaultValue
);
}
function enumBackendSettings(kind: string, enumProperties: any) {
const defaultEntry = _.find([...enumProperties], ([, prop]) => prop.default);
if (!defaultEntry) {
throw new Error(`No default entry found for enum "${kind}"`);
}
let defaultValue = defaultEntry[0];
return backendSettings(
kind,
(rawValue: t.UserSettingsValue) => {
return enumProperties.has(rawValue);
},
defaultValue
);
}
///////////////////////
// News filter settings
///////////////////////
const lastEntriesPeriod = enumBackendSettings("view_news_filter_interval", e.LastEntriesPeriodProperties);
const entriesOrder = enumBackendSettings("view_news_filter_sort_by", e.EntriesOrderProperties);
const minTagCount = enumBackendSettings("view_news_filter_min_tags_count", e.MinNewsTagCountProperties);
const showRead = boolBackendSettings("view_news_filter_show_read", true);
const entriesOrderProperties = computed(() => {
return e.EntriesOrderProperties.get(entriesOrder.value as any);
});
////////////////////////
// Feeds filter settings
////////////////////////
const showFeedsDescriptions = boolBackendSettings("view_feeds_show_feed_descriptions", true);
const feedsOrder = enumBackendSettings("view_feeds_feed_order", e.FeedsOrderProperties);
const failedFeedsFirst = boolBackendSettings("view_feeds_failed_feeds_first", false);
////////////////////////
// Rules filter settings
////////////////////////
const rulesOrder = enumBackendSettings("view_rules_order", e.RulesOrderProperties);
///////////////////
// Sidebar settings
///////////////////
const showSidebar = boolBackendSettings("show_sidebar", true);
const showSidebarPoint = ref(false);
////////////////
// User settings
////////////////
const hide_message_about_setting_up_key = userSettingInfo("hide_message_about_setting_up_key");
const hide_message_about_adding_collections = userSettingInfo("hide_message_about_adding_collections");
const hide_message_check_your_feed_urls = userSettingInfo("hide_message_check_your_feed_urls");
const openai_api_key = userSettingInfo("openai_api_key");
const gemini_api_key = userSettingInfo("gemini_api_key");
const max_tokens_cost_in_month = userSettingInfo("max_tokens_cost_in_month");
const process_entries_not_older_than = userSettingInfo("process_entries_not_older_than");
return {
mainPanelMode,
lastEntriesPeriod,
entriesOrder,
minTagCount,
showRead,
entriesOrderProperties,
dataVersion,
updateDataVersion,
showFeedsDescriptions,
userSettingsPresent,
info,
feedsOrder,
failedFeedsFirst,
rulesOrder,
showSidebar,
showSidebarPoint,
setUserSettings,
hide_message_about_setting_up_key,
hide_message_about_adding_collections,
hide_message_check_your_feed_urls,
openai_api_key,
gemini_api_key,
max_tokens_cost_in_month,
process_entries_not_older_than
};
});