nxsetup: more improvements

Signed-off-by: Varun Patil <radialapps@gmail.com>
pull/807/merge
Varun Patil 2023-10-02 13:33:26 -07:00
parent aa867f1a49
commit 33d99ee310
5 changed files with 122 additions and 15 deletions

View File

@ -110,6 +110,10 @@
> >
{{ folder.name }} {{ folder.name }}
</NcCheckboxRadioSwitch> </NcCheckboxRadioSwitch>
<NcButton @click="runNxSetup()" type="secondary">
{{ t('memories', 'Run initial device setup') }}
</NcButton>
</NcAppSettingsSection> </NcAppSettingsSection>
<NcAppSettingsSection id="folders-settings" :title="t('memories', 'Folders')"> <NcAppSettingsSection id="folders-settings" :title="t('memories', 'Folders')">
@ -296,6 +300,10 @@ export default defineComponent({
nativex.setLocalFolders(this.localFolders); nativex.setLocalFolders(this.localFolders);
}, },
runNxSetup() {
this.$router.replace('/nxsetup');
},
async logout() { async logout() {
if ( if (
await utils.confirmDestructive({ await utils.confirmDestructive({

View File

@ -28,7 +28,36 @@
</div> </div>
</div> </div>
<div class="setup-section" v-else-if="step === 2"> <div class="setup-section" v-if="step === 2">
{{
t(
'memories',
'Memories can show local media on your device alongside the media on your server. This requires access to the media on this device.'
)
}}
<br /><br />
{{
hasMediaPermission
? t('memories', 'Access to media has been granted.')
: t(
'memories',
'Access to media is not available yet. If the button below does not work, grant the permission through settings.'
)
}}
<div class="buttons">
<NcButton type="secondary" class="button" @click="grantMediaPermission" v-if="!hasMediaPermission">
{{ t('memories', 'Grant permissions') }}
</NcButton>
<NcButton type="primary" class="button" @click="step += hasMediaPermission ? 1 : 2">
{{ hasMediaPermission ? t('memories', 'Continue') : t('memories', 'Skip this step') }}
</NcButton>
</div>
</div>
<div class="setup-section" v-else-if="step === 3">
{{ t('memories', 'Choose the folders on this device to show on your timeline.') }} {{ t('memories', 'Choose the folders on this device to show on your timeline.') }}
{{ {{
t( t(
@ -41,6 +70,12 @@
<br /> <br />
<div id="folder-list"> <div id="folder-list">
<div v-if="syncStatus != -1">
{{ t('memories', 'Synchronizing local files ({n} done).', { n: syncStatus }) }}
<br />
{{ t('memories', 'This may take a while. Do not close this window.') }}
</div>
<template v-else>
<NcCheckboxRadioSwitch <NcCheckboxRadioSwitch
v-for="folder in localFolders" v-for="folder in localFolders"
:key="folder.id" :key="folder.id"
@ -50,6 +85,7 @@
> >
{{ folder.name }} {{ folder.name }}
</NcCheckboxRadioSwitch> </NcCheckboxRadioSwitch>
</template>
</div> </div>
<div class="buttons"> <div class="buttons">
@ -82,17 +118,23 @@ export default defineComponent({
data: () => ({ data: () => ({
banner, banner,
hasMediaPermission: false,
step: util.uid ? 1 : 0, step: util.uid ? 1 : 0,
localFolders: [] as nativex.LocalFolderConfig[], localFolders: [] as nativex.LocalFolderConfig[],
syncStatus: -1,
syncStatusWatch: 0,
}), }),
watch: { watch: {
step() { step() {
switch (this.step) { switch (this.step) {
case 2: case 2:
this.localFolders = nativex.getLocalFolders(); this.hasMediaPermission = nativex.configHasMediaPermission();
break; break;
case 3: case 3:
this.localFolders = nativex.getLocalFolders();
break;
case 4:
this.$router.push('/'); this.$router.push('/');
break; break;
} }
@ -110,16 +152,36 @@ export default defineComponent({
// set nativex theme // set nativex theme
nativex.setTheme(getComputedStyle(document.body).getPropertyValue('--color-background-plain')); nativex.setTheme(getComputedStyle(document.body).getPropertyValue('--color-background-plain'));
// set up sync status watcher
this.syncStatusWatch = window.setInterval(() => {
if (this.hasMediaPermission && this.step === 3) {
const newStatus = nativex.nativex.getSyncStatus();
// Refresh local folders if newly reached state -1
if (newStatus === -1 && this.syncStatus !== -1) {
this.localFolders = nativex.getLocalFolders();
}
this.syncStatus = newStatus;
}
}, 500);
}, },
beforeDestroy() { beforeDestroy() {
nativex.setTheme(); // reset theme nativex.setTheme(); // reset theme
window.clearInterval(this.syncStatusWatch);
}, },
methods: { methods: {
updateDeviceFolders() { updateDeviceFolders() {
nativex.setLocalFolders(this.localFolders); nativex.setLocalFolders(this.localFolders);
}, },
async grantMediaPermission() {
await nativex.configAllowMedia();
this.hasMediaPermission = nativex.configHasMediaPermission();
},
}, },
}); });
</script> </script>

View File

@ -77,6 +77,13 @@ export const NAPI = {
* @returns {void} * @returns {void}
*/ */
SHARE_LOCAL: (auid: number) => `${BASE_URL}/api/share/local/${auid}`, SHARE_LOCAL: (auid: number) => `${BASE_URL}/api/share/local/${auid}`,
/**
* Allow usage of local media (permissions request)
* @param val Allow or disallow media
* @returns
*/
CONFIG_ALLOW_MEDIA: (val: boolean) => `${BASE_URL}/api/config/allow_media/${val ? '1' : '0'}`,
}; };
/** NativeX synchronous API. */ /** NativeX synchronous API. */
@ -159,6 +166,18 @@ export type NativeX = {
* @returns JSON-encoded array of LocalFolderConfig * @returns JSON-encoded array of LocalFolderConfig
*/ */
configGetLocalFolders: () => string; configGetLocalFolders: () => string;
/**
* Check if the user has allowed media access.
* @returns Whether the user has allowed media access.
*/
configHasMediaPermission: () => boolean;
/**
* Get the current sync status.
* @returns number of file synced or -1
*/
getSyncStatus: () => number;
}; };
/** The native interface is a global object that is injected by the native app. */ /** The native interface is a global object that is injected by the native app. */

View File

@ -1,4 +1,4 @@
import { nativex } from './api'; import { NAPI, nativex } from './api';
/** Setting of whether a local folder is enabled */ /** Setting of whether a local folder is enabled */
export type LocalFolderConfig = { export type LocalFolderConfig = {
@ -21,3 +21,17 @@ export function setLocalFolders(config: LocalFolderConfig[]) {
export function getLocalFolders() { export function getLocalFolders() {
return JSON.parse(nativex?.configGetLocalFolders?.() ?? '[]') as LocalFolderConfig[]; return JSON.parse(nativex?.configGetLocalFolders?.() ?? '[]') as LocalFolderConfig[];
} }
/**
* Check if the user has allowed media access.
*/
export function configHasMediaPermission() {
return nativex?.configHasMediaPermission?.() ?? false;
}
/**
* Allow access to media.
*/
export async function configAllowMedia(val: boolean = true) {
return await fetch(NAPI.CONFIG_ALLOW_MEDIA(val));
}

View File

@ -1,4 +1,3 @@
import axios from '@nextcloud/axios';
import { NAPI } from './api'; import { NAPI } from './api';
import { API } from '../services/API'; import { API } from '../services/API';
import { has } from './basic'; import { has } from './basic';
@ -70,6 +69,11 @@ export async function deleteLocalPhotos(photos: IPhoto[], dry: boolean = false):
if (!has()) return 0; if (!has()) return 0;
const auids = photos.map((p) => p.auid).filter((a) => !!a) as number[]; const auids = photos.map((p) => p.auid).filter((a) => !!a) as number[];
const res = await axios.get(API.Q(NAPI.IMAGE_DELETE(auids), { dry }));
return res.data.confirms ? res.data.count : 0; // Delete local photos
const res = await fetch(API.Q(NAPI.IMAGE_DELETE(auids), { dry }));
if (!res.ok) throw new Error('Failed to delete photos');
const data = await res.json();
return data.confirms ? data.count : 0;
} }