Skip to content

Commit

Permalink
Merge pull request #11403 from rtibbles/how_refreshing
Browse files Browse the repository at this point in the history
Update device settings page to better handle server reload and force page refresh for settings changes
  • Loading branch information
marcellamaki authored Oct 17, 2023
2 parents 340b75e + 94a2a22 commit 994fc31
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 43 deletions.
12 changes: 10 additions & 2 deletions kolibri/plugins/device/assets/src/composables/useDeviceRestart.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@
*/

import { ref } from 'kolibri.lib.vueCompositionApi';
import heartbeat from 'kolibri.heartbeat';
import client from 'kolibri.client';
import clientFactory from 'kolibri.utils.clientFactory';
import urls from 'kolibri.urls';
import plugin_data from 'plugin_data';

// The refs are defined in the outer scope so they can be used as a shared store
const restarting = ref(false);
const canRestart = plugin_data.canRestart;

// Use this for checking if the device is restarting to avoid triggering
// the connection error detection.
const baseClient = clientFactory();

// POST to /api/device/devicerestart
export function restartDevice() {
return client({
Expand All @@ -20,7 +26,7 @@ export function restartDevice() {
}

export function isDeviceRestarting() {
return client({
return baseClient({
url: urls['kolibri:core:devicerestart'](),
})
.then(resp => Boolean(resp.data))
Expand All @@ -32,6 +38,7 @@ function restart() {
return Promise.reject('Device restart is not supported with current server configuration');
}
restarting.value = true;
heartbeat.stopPolling();
let statusPromise = restartDevice();
const checkStatus = expectedStatus => {
return statusPromise.then(status => {
Expand All @@ -48,7 +55,8 @@ function restart() {
// First wait for the device to be restarting
return checkStatus(true).then(() => {
// Then wait for it to have finished restarting
checkStatus(false).then(() => {
return checkStatus(false).then(() => {
heartbeat.startPolling();
restarting.value = false;
});
});
Expand Down
3 changes: 0 additions & 3 deletions kolibri/plugins/device/assets/src/modules/deviceInfo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ export default {
getDataLoading(state) {
return state.dataLoading;
},
canRestart() {
return plugin_data.canRestart;
},
isRemoteContent() {
return plugin_data.isRemoteContent;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

<KModal
:title="$tr('serverRestart')"
:submitText="coreString('continueAction')"
:cancelText="coreString('cancelAction')"
:submitText="restarting ? null : coreString('continueAction')"
:cancelText="restarting ? null : coreString('cancelAction')"
@submit="handleSubmit"
@cancel="$emit('cancel')"
>
Expand All @@ -13,6 +13,11 @@
<p class="description">
{{ getMessage() }}
</p>
<template v-if="restarting">
&nbsp;
<KCircularLoader />
&nbsp;
</template>
<div v-if="changedSetting === 'add' && path.writable === true">
<KCheckbox
:checked="confirmationChecked"
Expand All @@ -37,13 +42,17 @@
props: {
changedSetting: {
type: String, // primary, remove, add, plugins
required: true,
default: null,
},
path: {
type: Object,
required: false,
default: null,
},
restarting: {
type: Boolean,
default: false,
},
},
data() {
return {
Expand All @@ -52,6 +61,9 @@
},
methods: {
getMessage() {
if (!this.changedSetting) {
return this.$tr('serverRestartDescription');
}
let message = '';
switch (this.changedSetting) {
case 'primary':
Expand Down
46 changes: 33 additions & 13 deletions kolibri/plugins/device/assets/src/views/DeviceSettingsPage/api.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,60 @@
import isEqual from 'lodash/isEqual';
import pickBy from 'lodash/pickBy';
import client from 'kolibri.client';
import urls from 'kolibri.urls';

const url = urls['kolibri:core:devicesettings']();

const _dataCache = {};

export function getDeviceSettings() {
return client({ url }).then(({ data }) => {
Object.assign(_dataCache, data);
return {
languageId: data.language_id,
landingPage: data.landing_page,
allowGuestAccess: data.allow_guest_access,
allowLearnerUnassignedResourceAccess: data.allow_learner_unassigned_resource_access,
allowPeerUnlistedChannelImport: data.allow_peer_unlisted_channel_import,
allowOtherBrowsersToConnect: data.allow_other_browsers_to_connect,
extraSettings: data.extra_settings,
extraSettings: {
// Destructure the extra_settings object
// to ensure we are returning a novel object
// from the one stored in the _dataCache
...data.extra_settings,
},
primaryStorageLocation: data.primary_storage_location,
secondaryStorageLocations: data.secondary_storage_locations,
// Spread the secondary storage locations array to ensure
// we are returning a novel array from the one stored in the _dataCache
secondaryStorageLocations: [...data.secondary_storage_locations],
};
});
}

// PATCH to /api/device/devicesettings with a new settings
export function saveDeviceSettings(settings) {
const serverSettings = {
language_id: settings.languageId,
landing_page: settings.landingPage,
allow_guest_access: settings.allowGuestAccess,
allow_learner_unassigned_resource_access: settings.allowLearnerUnassignedResourceAccess,
allow_peer_unlisted_channel_import: settings.allowPeerUnlistedChannelImport,
allow_other_browsers_to_connect: settings.allowOtherBrowsersToConnect,
extra_settings: settings.extraSettings,
primary_storage_location: settings.primaryStorageLocation,
secondary_storage_locations: settings.secondaryStorageLocations,
};
const data = pickBy(serverSettings, (value, key) => !isEqual(value, _dataCache[key]));
if (Object.keys(data).length === 0) {
return Promise.resolve(false);
}
return client({
url,
method: 'PATCH',
data: {
language_id: settings.languageId,
landing_page: settings.landingPage,
allow_guest_access: settings.allowGuestAccess,
allow_learner_unassigned_resource_access: settings.allowLearnerUnassignedResourceAccess,
allow_peer_unlisted_channel_import: settings.allowPeerUnlistedChannelImport,
allow_other_browsers_to_connect: settings.allowOtherBrowsersToConnect,
extra_settings: settings.extraSettings,
primary_storage_location: settings.primaryStorageLocation,
secondary_storage_locations: settings.secondaryStorageLocations,
},
data,
}).then(response => {
Object.assign(_dataCache, response.data); // Update the cache
return true;
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,22 +144,22 @@
:text="$tr('changeLocation')"
:primary="true"
appearance="basic-link"
:disabled="!multipleWritablePaths || isRemoteContent"
:disabled="!multipleWritablePaths || isRemoteContent || !canRestart"
:class="{ 'disabled': !multipleWritablePaths }"
@click="showChangePrimaryLocationModal = true"
/>
</p>
<KButton
v-if="browserLocationMatchesServerURL && (secondaryStorageLocations.length === 0)"
v-if="secondaryStorageLocations.length === 0"
:text="$tr('addLocation')"
:disabled="isRemoteContent"
:disabled="isRemoteContent || !canRestart"
appearance="raised-button"
secondary
@click="showAddStorageLocationModal = true"
/>
</div>

<div v-show="browserLocationMatchesServerURL && (secondaryStorageLocations.length > 0)">
<div v-show="secondaryStorageLocations.length > 0">
<h2>
{{ $tr('secondaryStorage') }}
</h2>
Expand All @@ -173,7 +173,7 @@
hasDropdown
secondary
appearance="raised-button"
:disabled="isRemoteContent"
:disabled="isRemoteContent || !canRestart"
:text="coreString('optionsLabel')"
>
<template #menu>
Expand Down Expand Up @@ -336,6 +336,11 @@
@submit="handleServerRestart"
/>

<ServerRestartModal
v-if="restarting"
:restarting="true"
/>

</KPageContainer>
</DeviceAppBarPage>

Expand Down Expand Up @@ -391,7 +396,7 @@
},
mixins: [commonCoreStrings, commonDeviceStrings],
setup() {
const { restart } = useDeviceRestart();
const { canRestart, restart, restarting } = useDeviceRestart();
const { plugins, fetchPlugins, togglePlugin } = usePlugins();
const dataPlugins = ref(null);
Expand Down Expand Up @@ -419,7 +424,14 @@
return !unchanged;
}
return { restart, dataPlugins, checkPluginChanges, checkAndTogglePlugins };
return {
canRestart,
restart,
restarting,
dataPlugins,
checkPluginChanges,
checkAndTogglePlugins,
};
},
data() {
return {
Expand Down Expand Up @@ -458,8 +470,8 @@
};
},
computed: {
...mapGetters(['isAppContext', 'isPageLoading']),
...mapGetters('deviceInfo', ['canRestart', 'isRemoteContent']),
...mapGetters(['isAppContext', 'isPageLoading', 'snackbarIsVisible']),
...mapGetters('deviceInfo', ['isRemoteContent']),
pageTitle() {
return this.deviceString('deviceManagementTitle');
},
Expand Down Expand Up @@ -488,12 +500,6 @@
storageLocationOptions() {
return [this.$tr('addStorageLocation'), this.$tr('removeStorageLocation')];
},
browserLocationMatchesServerURL() {
return (
window.location.hostname.includes('127.0.0.1') ||
window.location.hostname.includes('localhost')
);
},
notEnoughFreeSpace() {
return this.freeSpace === 0;
},
Expand Down Expand Up @@ -761,6 +767,8 @@
} = this.getContentSettings();
this.getExtraSettings();
const pluginsChanged = this.checkPluginChanges();
this.checkAndTogglePlugins();
this.saveDeviceSettings({
Expand All @@ -774,15 +782,36 @@
secondaryStorageLocations: this.secondaryStorageLocations,
primaryStorageLocation: this.primaryStorageLocation,
})
.then(() => {
this.$store.dispatch('createSnackbar', this.$tr('saveSuccessNotification'));
this.showRestartModal = false;
if (this.restartSetting !== null) {
this.restart();
this.restartSetting = null;
.then(didSave => {
didSave = didSave || pluginsChanged;
if (didSave) {
this.$store.commit('CORE_CREATE_SNACKBAR', {
text: this.$tr('saveSuccessNotification'),
autoDismiss: true,
duration: 2000,
});
this.showRestartModal = false;
if (this.canRestart && this.restartSetting !== null) {
this.restartSetting = null;
return this.restart().then(() => didSave);
}
}
return didSave;
})
.then(shouldReload => {
if (shouldReload) {
if (this.snackbarIsVisible) {
const unwatch = this.$watch('snackbarIsVisible', () => {
unwatch && unwatch();
window.location.reload();
});
} else {
window.location.reload();
}
}
})
.catch(() => {
.catch(err => {
console.error(err);
this.$store.dispatch('createSnackbar', this.$tr('saveFailureNotification'));
});
},
Expand Down

0 comments on commit 994fc31

Please sign in to comment.