diff --git a/appinfo/routes.php b/appinfo/routes.php
index c39b3802..abadaf30 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -84,6 +84,7 @@ return [
// Config
['name' => 'Other#setUserConfig', 'url' => '/api/config/{key}', 'verb' => 'PUT'],
+ ['name' => 'Other#getUserConfig', 'url' => '/api/config', 'verb' => 'GET'],
// Admin
['name' => 'Admin#getSystemStatus', 'url' => '/api/system-status', 'verb' => 'GET'],
diff --git a/lib/Controller/OtherController.php b/lib/Controller/OtherController.php
index 0ee5f9ea..6cbfca64 100644
--- a/lib/Controller/OtherController.php
+++ b/lib/Controller/OtherController.php
@@ -56,6 +56,50 @@ class OtherController extends GenericApiController
});
}
+ /**
+ * @NoAdminRequired
+ *
+ * @NoCSRFRequired
+ *
+ * @PublicPage
+ */
+ public function getUserConfig(): Http\Response
+ {
+ return Util::guardEx(function () {
+ $appManager = \OC::$server->get(\OCP\App\IAppManager::class);
+
+ try {
+ $uid = Util::getUID();
+ } catch (\Exception $e) {
+ $uid = null;
+ }
+
+ $getAppConfig = function ($key, $default) use ($uid) {
+ return $this->config->getUserValue($uid, Application::APPNAME, $key, $default);
+ };
+
+ return new JSONResponse([
+ "version" => $appManager->getAppInfo('memories')['version'],
+ "vod_disable" => Util::getSystemConfig('memories.vod.disable'),
+ "video_default_quality" => Util::getSystemConfig('memories.video_default_quality'),
+ "places_gis" => Util::getSystemConfig('memories.gis_type'),
+
+ "systemtags_enabled" => Util::tagsIsEnabled(),
+ "recognize_enabled" => Util::recognizeIsEnabled(),
+ "albums_enabled" => Util::albumsIsEnabled(),
+ "facerecognition_installed" => Util::facerecognitionIsInstalled(),
+ "facerecognition_enabled" => Util::facerecognitionIsEnabled(),
+
+ "timeline_path" => $getAppConfig('timelinePath', 'EMPTY'),
+ "folders_path" => $getAppConfig('foldersPath', '/'),
+ "show_hidden_folders" => $getAppConfig('showHidden', false) === "true",
+ "sort_folder_month" => $getAppConfig('sortFolderMonth', false) === "true",
+ "sort_album_month" => $getAppConfig('sortAlbumMonth', "true") === "true",
+ "enable_top_memories" => $getAppConfig('enableTopMemories', 'true') === "true",
+ ], Http::STATUS_OK);
+ });
+ }
+
/**
* @NoAdminRequired
*
diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php
index 85c8e3ee..994b89b5 100644
--- a/lib/Controller/PageController.php
+++ b/lib/Controller/PageController.php
@@ -58,35 +58,6 @@ class PageController extends Controller
OCPUtil::addScript($this->appName, 'memories-main');
$this->eventDispatcher->dispatchTyped(new LoadSidebar());
- // Configuration
- $uid = $user->getUID();
- $pi = function ($key, $default) use ($uid) {
- $this->initialState->provideInitialState($key, $this->config->getUserValue(
- $uid,
- Application::APPNAME,
- $key,
- $default
- ));
- };
-
- // User configuration
- $pi('timelinePath', 'EMPTY');
- $pi('foldersPath', '/');
- $pi('showHidden', false);
- $pi('sortFolderMonth', false);
- $pi('sortAlbumMonth', 'true');
- $pi('enableTopMemories', 'true');
-
- // Apps enabled
- $this->initialState->provideInitialState('systemtags', Util::tagsIsEnabled());
- $this->initialState->provideInitialState('recognize', Util::recognizeIsEnabled());
- $this->initialState->provideInitialState('facerecognitionInstalled', Util::facerecognitionIsInstalled());
- $this->initialState->provideInitialState('facerecognitionEnabled', Util::facerecognitionIsEnabled());
- $this->initialState->provideInitialState('albums', Util::albumsIsEnabled());
-
- // Common state
- self::provideCommonInitialState($this->initialState);
-
// Extra translations
if (Util::recognizeIsEnabled()) {
// Auto translation for tags
@@ -138,22 +109,6 @@ class PageController extends Controller
return $policy;
}
- /** Provide initial state for all pages */
- public static function provideCommonInitialState(IInitialState &$initialState)
- {
- $appManager = \OC::$server->get(\OCP\App\IAppManager::class);
-
- // App version
- $initialState->provideInitialState('version', $appManager->getAppInfo('memories')['version']);
-
- // Video configuration
- $initialState->provideInitialState('vod_disable', Util::getSystemConfig('memories.vod.disable'));
- $initialState->provideInitialState('video_default_quality', Util::getSystemConfig('memories.video_default_quality'));
-
- // Geo configuration
- $initialState->provideInitialState('places_gis', Util::getSystemConfig('memories.gis_type'));
- }
-
/**
* @NoAdminRequired
*
diff --git a/lib/Controller/PublicAlbumController.php b/lib/Controller/PublicAlbumController.php
index c4b31970..89dc9e51 100644
--- a/lib/Controller/PublicAlbumController.php
+++ b/lib/Controller/PublicAlbumController.php
@@ -85,7 +85,6 @@ class PublicAlbumController extends Controller
// Scripts
Util::addScript($this->appName, 'memories-main');
- PageController::provideCommonInitialState($this->initialState);
$response = new PublicTemplateResponse($this->appName, 'main');
$response->setHeaderTitle($album['name']);
diff --git a/lib/Controller/PublicController.php b/lib/Controller/PublicController.php
index b27ed854..209a36c4 100644
--- a/lib/Controller/PublicController.php
+++ b/lib/Controller/PublicController.php
@@ -111,7 +111,6 @@ class PublicController extends AuthPublicShareController
// Scripts
\OCP\Util::addScript($this->appName, 'memories-main');
- PageController::provideCommonInitialState($this->initialState);
// Share info
$this->initialState->provideInitialState('no_download', $share->getHideDownload());
diff --git a/src/App.vue b/src/App.vue
index 0642dbc3..7edbe078 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -59,7 +59,7 @@ const NcAppNavigationItem = () => import('@nextcloud/vue/dist/Components/NcAppNa
import { generateUrl } from '@nextcloud/router';
import { translate as t } from '@nextcloud/l10n';
-import { emit } from '@nextcloud/event-bus';
+import { emit, subscribe } from '@nextcloud/event-bus';
import * as utils from './services/Utils';
import UserConfig from './mixins/UserConfig';
@@ -137,11 +137,11 @@ export default defineComponent({
},
recognize(): string | false {
- if (!this.config_recognizeEnabled) {
+ if (!this.config.recognize_enabled) {
return false;
}
- if (this.config_facerecognitionInstalled) {
+ if (this.config.facerecognition_enabled) {
return t('memories', 'People (Recognize)');
}
@@ -149,11 +149,11 @@ export default defineComponent({
},
facerecognition(): string | false {
- if (!this.config_facerecognitionInstalled) {
+ if (!this.config.facerecognition_installed) {
return false;
}
- if (this.config_recognizeEnabled) {
+ if (this.config.recognize_enabled) {
return t('memories', 'People (Face Recognition)');
}
@@ -161,11 +161,15 @@ export default defineComponent({
},
isFirstStart(): boolean {
- return this.config_timelinePath === 'EMPTY';
+ return (
+ this.config.timeline_path === 'EMPTY' &&
+ this.$route.name !== 'folder-share' &&
+ this.$route.name !== 'album-share'
+ );
},
showAlbums(): boolean {
- return this.config_albumsEnabled;
+ return this.config.albums_enabled;
},
removeOuterGap(): boolean {
@@ -197,13 +201,14 @@ export default defineComponent({
window.addEventListener('resize', () => {
utils.setRenewingTimeout(this, 'resizeTimer', onResize, 100);
});
+
+ // Register navigation items on config change
+ subscribe(this.configEventName, this.refreshNav);
},
mounted() {
this.doRouteChecks();
-
- // Populate navigation
- this.navItems = this.navItemsAll().filter((item) => typeof item.if === 'undefined' || Boolean(item.if));
+ this.refreshNav();
// Store CSS variables modified
const root = document.documentElement;
@@ -255,8 +260,8 @@ export default defineComponent({
},
methods: {
- navItemsAll(): NavItem[] {
- return [
+ refreshNav() {
+ const navItems = [
{
name: 'timeline',
icon: ImageMultiple,
@@ -309,7 +314,7 @@ export default defineComponent({
name: 'places',
icon: MarkerIcon,
title: t('memories', 'Places'),
- if: this.config_placesGis > 0,
+ if: this.config.places_gis > 0,
},
{
name: 'map',
@@ -320,9 +325,11 @@ export default defineComponent({
name: 'tags',
icon: TagsIcon,
title: t('memories', 'Tags'),
- if: this.config_tagsEnabled,
+ if: this.config.systemtags_enabled,
},
];
+
+ this.navItems = navItems.filter((item) => typeof item.if === 'undefined' || Boolean(item.if));
},
linkClick() {
diff --git a/src/components/ClusterView.vue b/src/components/ClusterView.vue
index f7720877..bd6cf9c2 100644
--- a/src/components/ClusterView.vue
+++ b/src/components/ClusterView.vue
@@ -53,11 +53,11 @@ export default defineComponent({
},
created() {
- subscribe(this.config_eventName, this.routeChange);
+ subscribe(this.configEventName, this.routeChange);
},
beforeDestroy() {
- unsubscribe(this.config_eventName, this.routeChange);
+ unsubscribe(this.configEventName, this.routeChange);
},
watch: {
@@ -74,7 +74,7 @@ export default defineComponent({
this.loading++;
if (route === 'albums') {
- this.items = await dav.getAlbums(3, this.config_albumListSort);
+ this.items = await dav.getAlbums(3, this.config.album_list_sort);
} else if (route === 'tags') {
this.items = await dav.getTags();
} else if (route === 'recognize' || route === 'facerecognition') {
diff --git a/src/components/FirstStart.vue b/src/components/FirstStart.vue
index cf518ca8..eae497a8 100644
--- a/src/components/FirstStart.vue
+++ b/src/components/FirstStart.vue
@@ -118,8 +118,8 @@ export default defineComponent({
async finish() {
this.show = false;
await new Promise((resolve) => setTimeout(resolve, 500));
- this.config_timelinePath = this.chosenPath;
- await this.updateSetting('timelinePath');
+ this.config.timeline_path = this.chosenPath;
+ await this.updateSetting('timeline_path', 'timelinePath');
},
async chooseFolder(title: string, initial: string) {
diff --git a/src/components/SelectionManager.vue b/src/components/SelectionManager.vue
index cdf1a8a9..623b27f5 100644
--- a/src/components/SelectionManager.vue
+++ b/src/components/SelectionManager.vue
@@ -254,7 +254,7 @@ export default defineComponent({
/** Is album route */
routeIsAlbum() {
- return this.config_albumsEnabled && this.$route.name === 'albums';
+ return this.config.albums_enabled && this.$route.name === 'albums';
},
/** Public route that can't modify anything */
@@ -786,7 +786,7 @@ export default defineComponent({
* Move selected photos to another person
*/
async moveSelectionToPerson(selection: Selection) {
- if (!this.config_showFaceRect) {
+ if (!this.config.show_face_rect) {
showError(this.t('memories', 'You must enable "Mark person in preview" to use this feature'));
return;
}
diff --git a/src/components/Settings.vue b/src/components/Settings.vue
index da374359..3d88ba20 100644
--- a/src/components/Settings.vue
+++ b/src/components/Settings.vue
@@ -30,39 +30,51 @@
>
-
+
-
+
{{ t('memories', 'Square grid mode') }}
{{ t('memories', 'Show past photos on top of timeline') }}
-
+
{{ t('memories', 'Load full size image on zoom') }}
-
+
{{ t('memories', 'Always load full size image (not recommended)') }}
-
+
-
+
{{ t('memories', 'Show hidden folders') }}
@@ -72,7 +84,7 @@
@@ -147,57 +159,57 @@ export default defineComponent({
},
async chooseTimelinePath() {
- (this.$refs.multiPathModal).open(this.config_timelinePath.split(';'));
+ (this.$refs.multiPathModal).open(this.config.timeline_path.split(';'));
},
async saveTimelinePath(paths: string[]) {
if (!paths || !paths.length) return;
const newPath = paths.join(';');
- if (newPath !== this.config_timelinePath) {
- this.config_timelinePath = newPath;
- await this.updateSetting('timelinePath');
+ if (newPath !== this.config.timeline_path) {
+ this.config.timeline_path = newPath;
+ await this.updateSetting('timeline_path', 'timelinePath');
}
},
async chooseFoldersPath() {
let newPath = await this.chooseFolder(
this.t('memories', 'Choose the root for the folders view'),
- this.config_foldersPath
+ this.config.folders_path
);
if (newPath === '') newPath = '/';
- if (newPath !== this.config_foldersPath) {
- this.config_foldersPath = newPath;
- await this.updateSetting('foldersPath');
+ if (newPath !== this.config.folders_path) {
+ this.config.folders_path = newPath;
+ await this.updateSetting('folders_path', 'foldersPath');
}
},
async updateSquareThumbs() {
- await this.updateSetting('squareThumbs');
+ await this.updateSetting('square_thumbs');
},
async updateFullResOnZoom() {
- await this.updateSetting('fullResOnZoom');
+ await this.updateSetting('full_res_on_zoom');
},
async updateFullResAlways() {
- await this.updateSetting('fullResAlways');
+ await this.updateSetting('full_res_always');
},
async updateEnableTopMemories() {
- await this.updateSetting('enableTopMemories');
+ await this.updateSetting('enable_top_memories', 'enableTopMemories');
},
async updateShowHidden() {
- await this.updateSetting('showHidden');
+ await this.updateSetting('show_hidden_folders', 'showHidden');
},
async updateSortFolderMonth() {
- await this.updateSetting('sortFolderMonth');
+ await this.updateSetting('sort_folder_month', 'sortFolderMonth');
},
async updateSortAlbumMonth() {
- await this.updateSetting('sortAlbumMonth');
+ await this.updateSetting('sort_album_month', 'sortAlbumMonth');
},
},
});
diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue
index 7feb30ea..6494efd6 100644
--- a/src/components/Timeline.vue
+++ b/src/components/Timeline.vue
@@ -30,8 +30,8 @@
@@ -204,16 +204,17 @@ export default defineComponent({
},
created() {
- subscribe(this.config_eventName, this.softRefresh);
+ subscribe(this.configEventName, this.softRefresh);
subscribe('files:file:created', this.softRefresh);
subscribe('memories:window:resize', this.handleResizeWithDelay);
},
beforeDestroy() {
- unsubscribe(this.config_eventName, this.softRefresh);
+ unsubscribe(this.configEventName, this.softRefresh);
unsubscribe('files:file:created', this.softRefresh);
unsubscribe('memories:window:resize', this.handleResizeWithDelay);
this.resetState();
+ this.state = 0;
},
computed: {
@@ -234,8 +235,8 @@ export default defineComponent({
return (
this.$route.query.sort === 'album' ||
- (this.config_sortAlbumMonth && (this.$route.name === 'albums' || this.$route.name === 'album-share')) ||
- (this.config_sortFolderMonth && this.$route.name === 'folders')
+ (this.config.sort_album_month && (this.$route.name === 'albums' || this.$route.name === 'album-share')) ||
+ (this.config.sort_folder_month && this.$route.name === 'folders')
);
},
@@ -316,7 +317,7 @@ export default defineComponent({
},
allowBreakout() {
- return globalThis.windowInnerWidth <= 600 && !this.config_squareThumbs;
+ return globalThis.windowInnerWidth <= 600 && !this.config.square_thumbs;
},
/** Create new state */
@@ -412,7 +413,7 @@ export default defineComponent({
this.rowHeight = Math.floor(this.rowWidth / this.numCols);
} else {
// Desktop
- if (this.config_squareThumbs) {
+ if (this.config.square_thumbs) {
this.numCols = Math.max(3, Math.floor(this.rowWidth / DESKTOP_ROW_HEIGHT));
this.rowHeight = Math.floor(this.rowWidth / this.numCols);
} else {
@@ -557,7 +558,7 @@ export default defineComponent({
// Folder
if (this.$route.name === 'folders') {
- const path = utils.getFolderRoutePath(this.config_foldersPath);
+ const path = utils.getFolderRoutePath(this.config.folders_path);
API.DAYS_FILTER(query, DaysFilterType.FOLDER, path);
if (this.$route.query.recursive) {
API.DAYS_FILTER(query, DaysFilterType.RECURSIVE);
@@ -589,7 +590,7 @@ export default defineComponent({
API.DAYS_FILTER(query, filter, `${user}/${name}`);
// Face rect
- if (this.config_showFaceRect) {
+ if (this.config.show_face_rect) {
API.DAYS_FILTER(query, DaysFilterType.FACE_RECT);
}
}
@@ -639,7 +640,7 @@ export default defineComponent({
}
// Get subfolders URL
- const folder = utils.getFolderRoutePath(this.config_foldersPath);
+ const folder = utils.getFolderRoutePath(this.config.folders_path);
const url = API.Q(API.FOLDERS_SUB(), { folder });
// Make API call to get subfolders
@@ -654,7 +655,7 @@ export default defineComponent({
}
// Filter out hidden folders
- if (!this.config_showHidden) {
+ if (!this.config.show_hidden_folders) {
this.folders = this.folders.filter((f) => !f.name.startsWith('.') && f.previews?.length);
}
},
@@ -730,6 +731,8 @@ export default defineComponent({
/** Process the data for days call including folders */
async processDays(data: IDay[]) {
+ if (!data || !this.state) return;
+
const list: typeof this.list = [];
const heads: typeof this.heads = {};
@@ -941,7 +944,7 @@ export default defineComponent({
* @param data photos
*/
processDay(dayId: number, data: IPhoto[]) {
- if (!data) return;
+ if (!data || !this.state) return;
const head = this.heads[dayId];
if (!head) return;
@@ -964,7 +967,7 @@ export default defineComponent({
}
// Force all to square
- const squareMode = this.isMobileLayout() || this.config_squareThumbs;
+ const squareMode = this.isMobileLayout() || this.config.square_thumbs;
// Create justified layout with correct params
const justify = getLayout(
diff --git a/src/components/frame/Folder.vue b/src/components/frame/Folder.vue
index dbb04c3a..133cdf9c 100644
--- a/src/components/frame/Folder.vue
+++ b/src/components/frame/Folder.vue
@@ -70,7 +70,7 @@ export default defineComponent({
.slice(2) as string[];
// Remove base path if present
- const basePath = this.config_foldersPath.split('/').filter((x) => x);
+ const basePath = this.config.folders_path.split('/').filter((x) => x);
if (path.length >= basePath.length && path.slice(0, basePath.length).every((x, i) => x === basePath[i])) {
path.splice(0, basePath.length);
}
diff --git a/src/components/modal/MoveToFolderModal.vue b/src/components/modal/MoveToFolderModal.vue
index dad5cff6..3ebd113d 100644
--- a/src/components/modal/MoveToFolderModal.vue
+++ b/src/components/modal/MoveToFolderModal.vue
@@ -71,7 +71,7 @@ export default defineComponent({
},
async chooseFolderPath() {
- let destination = await this.chooseFolderModal(this.t('memories', 'Choose a folder'), this.config_foldersPath);
+ let destination = await this.chooseFolderModal(this.t('memories', 'Choose a folder'), this.config.folders_path);
// Fails if the target exists, same behavior with Nextcloud files implementation.
const gen = dav.movePhotos(this.photos, destination, false);
this.processing = true;
diff --git a/src/components/modal/ShareModal.vue b/src/components/modal/ShareModal.vue
index e1f59c0f..f7df407a 100644
--- a/src/components/modal/ShareModal.vue
+++ b/src/components/modal/ShareModal.vue
@@ -71,12 +71,12 @@
import { defineComponent } from 'vue';
import { showError } from '@nextcloud/dialogs';
-import { loadState } from '@nextcloud/initial-state';
import axios from '@nextcloud/axios';
import NcListItem from '@nextcloud/vue/dist/Components/NcListItem';
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon';
import Modal from './Modal.vue';
+import UserConfig from '../../mixins/UserConfig';
import { IPhoto } from '../../types';
import { API } from '../../services/API';
@@ -88,9 +88,6 @@ import LargePhotoIcon from 'vue-material-design-icons/ImageArea.vue';
import LinkIcon from 'vue-material-design-icons/LinkVariant.vue';
import FileIcon from 'vue-material-design-icons/File.vue';
-// Is video transcoding enabled?
-const config_vodDisable = loadState('memories', 'vod_disable', true);
-
export default defineComponent({
name: 'ShareModal',
@@ -105,6 +102,8 @@ export default defineComponent({
FileIcon,
},
+ mixins: [UserConfig],
+
data: () => {
return {
photo: null as IPhoto | null,
@@ -128,7 +127,7 @@ export default defineComponent({
},
canShareHighRes() {
- return !this.isVideo || !config_vodDisable;
+ return !this.isVideo || !this.config.vod_disable;
},
canShareLink() {
diff --git a/src/components/top-matter/AlbumTopMatter.vue b/src/components/top-matter/AlbumTopMatter.vue
index 4f4a9720..66094cf8 100644
--- a/src/components/top-matter/AlbumTopMatter.vue
+++ b/src/components/top-matter/AlbumTopMatter.vue
@@ -18,7 +18,7 @@
@@ -29,7 +29,7 @@
@@ -194,8 +194,8 @@ export default defineComponent({
* 1 = date, 2 = name
*/
changeSort(order: 1 | 2) {
- this.config_albumListSort = order;
- this.updateSetting('albumListSort');
+ this.config.album_list_sort = order;
+ this.updateSetting('album_list_sort');
},
},
});
diff --git a/src/components/top-matter/FaceTopMatter.vue b/src/components/top-matter/FaceTopMatter.vue
index 02052d3a..668f8fa9 100644
--- a/src/components/top-matter/FaceTopMatter.vue
+++ b/src/components/top-matter/FaceTopMatter.vue
@@ -25,7 +25,7 @@
{{ t('memories', 'Mark person in preview') }}
@@ -104,10 +104,8 @@ export default defineComponent({
},
changeShowFaceRect() {
- localStorage.setItem('memories_showFaceRect', this.config_showFaceRect ? '1' : '0');
- setTimeout(() => {
- this.$router.go(0); // refresh page
- }, 500);
+ this.updateSetting('show_face_rect');
+ setTimeout(() => this.$router.go(0), 100); // refresh page
},
},
});
diff --git a/src/components/top-matter/FolderTopMatter.vue b/src/components/top-matter/FolderTopMatter.vue
index 6a358f3f..5a40a099 100644
--- a/src/components/top-matter/FolderTopMatter.vue
+++ b/src/components/top-matter/FolderTopMatter.vue
@@ -108,7 +108,7 @@ export default defineComponent({
},
share() {
- globalThis.shareNodeLink(utils.getFolderRoutePath(this.config_foldersPath));
+ globalThis.shareNodeLink(utils.getFolderRoutePath(this.config.folders_path));
},
},
});
diff --git a/src/components/viewer/PsVideo.ts b/src/components/viewer/PsVideo.ts
index a884943b..3bd42991 100644
--- a/src/components/viewer/PsVideo.ts
+++ b/src/components/viewer/PsVideo.ts
@@ -1,5 +1,5 @@
import PhotoSwipe from 'photoswipe';
-import { loadState } from '@nextcloud/initial-state';
+import staticConfig from '../../services/static-config';
import { showError } from '@nextcloud/dialogs';
import { translate as t } from '@nextcloud/l10n';
import { getCurrentUser } from '@nextcloud/auth';
@@ -25,10 +25,6 @@ type PsVideoEvent = PsEvent & {
content: VideoContent;
};
-const config_vodDisable = loadState('memories', 'vod_disable', true);
-
-const config_video_default_quality = Number(loadState('memories', 'video_default_quality', '0') as string);
-
/**
* Check if slide has video content
*/
@@ -155,7 +151,7 @@ class VideoContentSetup {
type: string;
}[] = [];
- if (!config_vodDisable) {
+ if (!staticConfig.getSync('vod_disable')) {
sources.push(this.getHLSsrc(content));
}
@@ -205,7 +201,7 @@ class VideoContentSetup {
directFailed = true;
console.warn('PsVideo: Direct video stream could not be opened.');
- if (!hlsFailed && !config_vodDisable) {
+ if (!hlsFailed && !staticConfig.getSync('vod_disable')) {
console.warn('PsVideo: Trying HLS stream');
vjs.src(this.getHLSsrc(content));
}
@@ -336,7 +332,7 @@ class VideoContentSetup {
// Add quality options
if (qualityNums) {
opts.quality = {
- default: config_video_default_quality,
+ default: Number(staticConfig.getSync('video_default_quality')),
options: qualityNums,
forced: true,
onChange: (quality: number) => {
diff --git a/src/components/viewer/Viewer.vue b/src/components/viewer/Viewer.vue
index c18c9ace..fc01eb17 100644
--- a/src/components/viewer/Viewer.vue
+++ b/src/components/viewer/Viewer.vue
@@ -770,7 +770,7 @@ export default defineComponent({
// Get full image URL
const fullUrl = isvideo ? null : API.IMAGE_DECODABLE(photo.fileid, photo.etag);
- const fullLoadCond = this.config_fullResAlways ? 'always' : this.config_fullResOnZoom ? 'zoom' : 'never';
+ const fullLoadCond = this.config.full_res_always ? 'always' : this.config.full_res_on_zoom ? 'zoom' : 'never';
return {
src: previewUrl,
diff --git a/src/mixins/UserConfig.ts b/src/mixins/UserConfig.ts
index f82d5eee..b34f6cf9 100644
--- a/src/mixins/UserConfig.ts
+++ b/src/mixins/UserConfig.ts
@@ -1,43 +1,30 @@
-import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus';
-import { loadState } from '@nextcloud/initial-state';
import axios from '@nextcloud/axios';
+import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus';
import { API } from '../services/API';
import { defineComponent } from 'vue';
+import { IConfig } from '../types';
+import staticConfig from '../services/static-config';
const eventName = 'memories:user-config-changed';
-const localSettings = ['squareThumbs', 'fullResOnZoom', 'fullResAlways', 'showFaceRect', 'albumListSort'];
+const localSettings: (keyof IConfig)[] = [
+ 'square_thumbs',
+ 'full_res_on_zoom',
+ 'full_res_always',
+ 'show_face_rect',
+ 'album_list_sort',
+];
export default defineComponent({
name: 'UserConfig',
data: () => ({
- config_timelinePath: loadState('memories', 'timelinePath', '') as string,
- config_foldersPath: loadState('memories', 'foldersPath', '/') as string,
-
- config_showHidden: loadState('memories', 'showHidden', 'false') === 'true',
- config_sortFolderMonth: loadState('memories', 'sortFolderMonth', 'false') === 'true',
- config_sortAlbumMonth: loadState('memories', 'sortAlbumMonth', 'true') === 'true',
- config_enableTopMemories: loadState('memories', 'enableTopMemories', 'false') === 'true',
-
- config_tagsEnabled: Boolean(loadState('memories', 'systemtags', '')),
- config_recognizeEnabled: Boolean(loadState('memories', 'recognize', '')),
- config_facerecognitionInstalled: Boolean(loadState('memories', 'facerecognitionInstalled', '')),
- config_facerecognitionEnabled: Boolean(loadState('memories', 'facerecognitionEnabled', '')),
- config_albumsEnabled: Boolean(loadState('memories', 'albums', '')),
-
- config_placesGis: Number(loadState('memories', 'places_gis', '-1')),
-
- config_squareThumbs: localStorage.getItem('memories_squareThumbs') === '1',
- config_fullResOnZoom: localStorage.getItem('memories_fullResOnZoom') !== '0',
- config_fullResAlways: localStorage.getItem('memories_fullResAlways') === '1',
- config_showFaceRect: localStorage.getItem('memories_showFaceRect') === '1',
- config_albumListSort: Number(localStorage.getItem('memories_albumListSort') || 1),
-
- config_eventName: eventName,
+ config: { ...staticConfig.getDefault() },
+ configEventName: eventName,
}),
created() {
subscribe(eventName, this.updateLocalSetting);
+ this.refreshFromConfig();
},
beforeDestroy() {
@@ -45,27 +32,32 @@ export default defineComponent({
},
methods: {
- updateLocalSetting({ setting, value }) {
- this['config_' + setting] = value;
+ async refreshFromConfig() {
+ const config = await staticConfig.getAll();
+ const changed = Object.keys(config).filter((key) => config[key] !== this.config[key]);
+ if (changed.length === 0) return;
+
+ changed.forEach((key) => (this.config[key] = config[key]));
+ emit(eventName, { setting: null, value: null });
},
- async updateSetting(setting: string) {
- const value = this['config_' + setting];
+ updateLocalSetting({ setting, value }) {
+ if (setting) {
+ this.config[setting] = value;
+ }
+ },
- if (localSettings.includes(setting)) {
- if (typeof value === 'boolean') {
- localStorage.setItem('memories_' + setting, value ? '1' : '0');
- } else {
- localStorage.setItem('memories_' + setting, value);
- }
- } else {
- // Long time save setting
- await axios.put(API.CONFIG(setting), {
+ async updateSetting(setting: K, remote?: string) {
+ const value = this.config[setting];
+
+ if (!localSettings.includes(setting)) {
+ await axios.put(API.CONFIG(remote ?? setting), {
value: value.toString(),
});
}
- // Visible elements update setting
+ staticConfig.setLs(setting, value);
+
emit(eventName, { setting, value });
},
},
diff --git a/src/services/API.ts b/src/services/API.ts
index de7638e8..2128ec34 100644
--- a/src/services/API.ts
+++ b/src/services/API.ts
@@ -185,6 +185,10 @@ export class API {
return gen(`${BASE}/config/{setting}`, { setting });
}
+ static CONFIG_GET() {
+ return gen(`${BASE}/config`);
+ }
+
static SYSTEM_CONFIG(setting: string | null) {
return setting ? gen(`${BASE}/system-config/{setting}`, { setting }) : gen(`${BASE}/system-config`);
}
diff --git a/src/services/static-config.ts b/src/services/static-config.ts
new file mode 100644
index 00000000..745f1f37
--- /dev/null
+++ b/src/services/static-config.ts
@@ -0,0 +1,111 @@
+import axios from '@nextcloud/axios';
+import { API } from './API';
+import { IConfig } from '../types';
+
+class StaticConfig {
+ private config: IConfig | null = null;
+ private initPromises: Array<() => void> = [];
+ private default: IConfig | null = null;
+
+ public constructor() {
+ this.init();
+ }
+
+ private async init() {
+ const res = await axios.get(API.CONFIG_GET());
+ this.config = res.data;
+
+ this.getDefault();
+ for (const key in this.config) {
+ this.default![key] = this.config[key];
+ this.setLs(key as keyof IConfig, this.config[key]);
+ }
+
+ this.initPromises.forEach((resolve) => resolve());
+ }
+
+ private async waitForInit() {
+ if (!this.config) {
+ await new Promise((resolve) => {
+ this.initPromises.push(resolve);
+ });
+ }
+ }
+
+ public async getAll() {
+ await this.waitForInit();
+ return this.config!;
+ }
+
+ public async get(key: K) {
+ await this.waitForInit();
+ return this.config![key];
+ }
+
+ public getSync(key: K) {
+ return this.getDefault()[key];
+ }
+
+ public setLs(key: K, value: IConfig[K]) {
+ if (this.default) {
+ this.default[key] = value;
+ }
+
+ if (this.config) {
+ this.config[key] = value;
+ }
+
+ localStorage.setItem(`memories_${key}`, value.toString());
+ }
+
+ public getDefault(): IConfig {
+ if (this.default) {
+ return this.default;
+ }
+
+ const config: IConfig = {
+ version: '',
+ vod_disable: false,
+ video_default_quality: '0',
+ places_gis: -1,
+
+ systemtags_enabled: false,
+ recognize_enabled: false,
+ albums_enabled: false,
+ facerecognition_installed: false,
+ facerecognition_enabled: false,
+
+ timeline_path: '',
+ folders_path: '',
+ show_hidden_folders: false,
+ sort_folder_month: false,
+ sort_album_month: true,
+ enable_top_memories: true,
+
+ square_thumbs: false,
+ full_res_on_zoom: true,
+ full_res_always: false,
+ show_face_rect: false,
+ album_list_sort: 1,
+ };
+
+ for (const key in config) {
+ const val = localStorage.getItem(`memories_${key}`);
+ if (val !== null) {
+ if (typeof config[key] === 'boolean') {
+ config[key] = val === 'true';
+ } else if (typeof config[key] === 'number') {
+ config[key] = Number(val);
+ } else {
+ config[key] = val;
+ }
+ }
+ }
+
+ this.default = config;
+
+ return config;
+ }
+}
+
+export default new StaticConfig();
diff --git a/src/services/strings.ts b/src/services/strings.ts
index ece1e183..d1e7e551 100644
--- a/src/services/strings.ts
+++ b/src/services/strings.ts
@@ -1,8 +1,6 @@
-import { loadState } from '@nextcloud/initial-state';
+import staticConfig from './static-config';
import { translate as t } from '@nextcloud/l10n';
-const config_facerecognitionEnabled = Boolean(loadState('memories', 'facerecognitionEnabled', ''));
-
type RouteNameType = string | null | undefined;
export function emptyDescription(routeName: RouteNameType): string {
@@ -14,7 +12,7 @@ export function emptyDescription(routeName: RouteNameType): string {
case 'thisday':
return t('memories', 'Memories from past years will appear here');
case 'facerecognition':
- return config_facerecognitionEnabled
+ return staticConfig.getSync('facerecognition_enabled')
? t('memories', 'You will find your friends soon. Please be patient')
: t('memories', 'Face Recognition is disabled. Enable in settings to find your friends');
case 'videos':
diff --git a/src/services/utils/cache.ts b/src/services/utils/cache.ts
index 705cd252..b8abf545 100644
--- a/src/services/utils/cache.ts
+++ b/src/services/utils/cache.ts
@@ -1,18 +1,23 @@
import { getCurrentUser } from '@nextcloud/auth';
-import { loadState } from '@nextcloud/initial-state';
+import staticConfig from '../static-config';
/** Cache keys */
-const memoriesVersion: string = loadState('memories', 'version', '');
const uid = getCurrentUser()?.uid || 'guest';
-const cacheName = `memories-${memoriesVersion}-${uid}`;
+
+async function getCacheName() {
+ const memoriesVersion = await staticConfig.get('version');
+ return `memories-${memoriesVersion}-${uid}`;
+}
// Clear all caches except the current one
(async function clearCaches() {
- if (!memoriesVersion || uid === 'guest') return;
+ if (uid === 'guest') return;
const keys = await window.caches?.keys();
if (!keys?.length) return;
+ const cacheName = await getCacheName();
+
for (const key of keys) {
if (key.startsWith('memories-') && key !== cacheName) {
window.caches.delete(key);
@@ -23,10 +28,8 @@ const cacheName = `memories-${memoriesVersion}-${uid}`;
/** Singleton cache instance */
let staticCache: Cache | null = null;
export async function openCache() {
- if (!memoriesVersion) return null;
-
try {
- return (staticCache ??= (await window.caches?.open(cacheName)) ?? null);
+ return (staticCache ??= (await window.caches?.open(await getCacheName())) ?? null);
} catch {
return null;
}
diff --git a/src/types.ts b/src/types.ts
index 7f041a45..34f25cda 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -236,3 +236,29 @@ export type ISelectionAction = {
/** Allow for public routes (default false) */
allowPublic?: boolean;
};
+
+export type IConfig = {
+ version: string;
+ vod_disable: boolean;
+ video_default_quality: string;
+ places_gis: number;
+
+ systemtags_enabled: boolean;
+ recognize_enabled: boolean;
+ albums_enabled: boolean;
+ facerecognition_installed: boolean;
+ facerecognition_enabled: boolean;
+
+ timeline_path: string;
+ folders_path: string;
+ show_hidden_folders: boolean;
+ sort_folder_month: boolean;
+ sort_album_month: boolean;
+ enable_top_memories: boolean;
+
+ square_thumbs: boolean;
+ full_res_on_zoom: boolean;
+ full_res_always: boolean;
+ show_face_rect: boolean;
+ album_list_sort: 1 | 2;
+};
diff --git a/src/vue-globals.d.ts b/src/vue-globals.d.ts
index 3d9f4de2..70c69b26 100644
--- a/src/vue-globals.d.ts
+++ b/src/vue-globals.d.ts
@@ -10,29 +10,6 @@ declare module 'vue' {
c: typeof constants.c;
state_noDownload: boolean;
-
- // UserConfig.ts
- config_timelinePath: string;
- config_foldersPath: string;
- config_showHidden: boolean;
- config_sortFolderMonth: boolean;
- config_sortAlbumMonth: boolean;
- config_tagsEnabled: boolean;
- config_recognizeEnabled: boolean;
- config_facerecognitionInstalled: boolean;
- config_facerecognitionEnabled: boolean;
- config_albumsEnabled: boolean;
- config_placesGis: number;
- config_squareThumbs: boolean;
- config_enableTopMemories: boolean;
- config_fullResOnZoom: boolean;
- config_fullResAlways: boolean;
- config_showFaceRect: boolean;
- config_albumListSort: 1 | 2;
- config_eventName: string;
-
- updateSetting: (setting: string) => Promise;
- updateLocalSetting: (opts: { setting: string; value: any }) => void;
}
}