-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add import/export settings buttons #9123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Changes from 12 commits
bffdca3
043a659
df0d50d
55022f4
4d60589
220c332
76b37a7
45728a1
28b35dc
8e3b64a
49cdf75
fc2288e
2b13802
81806b8
c5924e2
ade92a4
8a54701
55065e5
64d0642
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,6 +65,24 @@ | |
| @click="showExportSearchHistoryPrompt = true" | ||
| /> | ||
| </FtFlexBox> | ||
| <h4 class="groupTitle"> | ||
| {{ t('Settings') }} | ||
| <FtTooltip | ||
| class="selectTooltip" | ||
| position="top" | ||
| :tooltip="t('Settings.Data Settings.Settings Tooltip')" | ||
| /> | ||
| </h4> | ||
| <FtFlexBox class="box"> | ||
| <FtButton | ||
| :label="t('Settings.Data Settings.Import Settings')" | ||
| @click="importSettings" | ||
| /> | ||
| <FtButton | ||
| :label="t('Settings.Data Settings.Export Settings')" | ||
| @click="exportSettings" | ||
| /> | ||
| </FtFlexBox> | ||
| <FtPrompt | ||
| v-if="showExportSubscriptionsPrompt" | ||
| :label="$t('Settings.Data Settings.Select Export Type')" | ||
|
|
@@ -86,6 +104,13 @@ | |
| :option-values="WATCH_SEARCH_HISTORY_PROMPT_VALUES" | ||
| @click="exportSearchHistory" | ||
| /> | ||
| <FtPrompt | ||
| v-if="showRestartPrompt" | ||
| :label="$t('Settings[\'The app needs to restart for changes to take effect. Restart and apply change?\']')" | ||
| :option-names="[$t('Yes, Restart'), $t('Cancel')]" | ||
| :option-values="['restart', 'cancel']" | ||
| @click="handleRestart" | ||
| /> | ||
| </FtSettingsSection> | ||
| </template> | ||
|
|
||
|
|
@@ -98,8 +123,10 @@ import FtButton from '../FtButton/FtButton.vue' | |
| import FtFlexBox from '../ft-flex-box/ft-flex-box.vue' | ||
| import FtPrompt from '../FtPrompt/FtPrompt.vue' | ||
| import FtSettingsSection from '../FtSettingsSection/FtSettingsSection.vue' | ||
| import FtTooltip from '../FtTooltip/FtTooltip.vue' | ||
|
|
||
| import store from '../../store/index' | ||
| import { defaultUpdaterId, settingsNeedingRestart } from '../../store/modules/settings' | ||
|
|
||
| import { MAIN_PROFILE_ID } from '../../../constants' | ||
| import { calculateColorLuminance, getRandomColor } from '../../helpers/colors' | ||
|
|
@@ -1458,6 +1485,110 @@ async function exportYouTubeSearchHistory() { | |
| } | ||
|
|
||
| // #endregion search history | ||
|
|
||
| // #region settings | ||
|
|
||
| /** @type {import('vue').ComputedRef<object>} */ | ||
| const exportableSettings = computed(() => { | ||
| return store.getters.getExportableSettings | ||
| }) | ||
|
|
||
| const showRestartPrompt = ref(false) | ||
|
|
||
| const pendingSettings = new Map() | ||
|
|
||
| async function importSettings() { | ||
| let response | ||
| try { | ||
| response = await readFileWithPicker( | ||
| t('Settings.Data Settings.Settings File'), | ||
| { | ||
| 'application/x-freetube-db': '.db', | ||
| 'application/json': '.json' | ||
| }, | ||
| IMPORT_DIRECTORY_ID, | ||
| START_IN_DIRECTORY | ||
| ) | ||
| } catch (err) { | ||
| const message = t('Settings.Data Settings.Unable to read file') | ||
| showToast(`${message}: ${err}`) | ||
| return | ||
| } | ||
|
|
||
| if (response === null) { | ||
| return | ||
| } | ||
|
|
||
| const { content } = response | ||
| const importedSettings = JSON.parse(content) | ||
| const currentSettings = exportableSettings.value | ||
|
|
||
| for (const [importedKey, importedValue] of Object.entries(importedSettings)) { | ||
| if (!Object.hasOwn(currentSettings, importedKey)) { | ||
| const message = `${t('Settings.Data Settings.Unknown setting key')}: ${importedKey}` | ||
| showToast(message) | ||
| continue | ||
| } | ||
|
|
||
| const currentValue = currentSettings[importedKey] | ||
| const areValuesEqual = currentValue === importedValue || | ||
| (typeof importedValue === 'object' && JSON.stringify(currentValue) === JSON.stringify(importedValue)) | ||
| if (areValuesEqual) { | ||
| continue | ||
| } | ||
|
|
||
| if (settingsNeedingRestart.has(importedKey)) { | ||
| pendingSettings.set(importedKey, importedValue) | ||
| showRestartPrompt.value = true | ||
|
Shadorc marked this conversation as resolved.
Outdated
|
||
| } else { | ||
| const updaterId = defaultUpdaterId(importedKey) | ||
| await store.dispatch(updaterId, importedValue) | ||
| } | ||
| } | ||
|
|
||
| showToast(t('Settings.Data Settings.All settings have been successfully imported')) | ||
| } | ||
|
|
||
| async function exportSettings() { | ||
| const dateStr = getTodayDateStrLocalTimezone() | ||
| const exportFileName = `freetube-settings-${dateStr}.db` | ||
| const settingsContent = JSON.stringify(exportableSettings.value) | ||
|
|
||
| await promptAndWriteToFile( | ||
| exportFileName, | ||
| settingsContent, | ||
| t('Settings.Data Settings.Settings File'), | ||
| 'application/x-freetube-db', | ||
| '.db', | ||
| t('Settings.Data Settings.All settings have been successfully exported') | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * @param {'restart' | 'cancel' | null} value | ||
| */ | ||
| function handleRestart(value) { | ||
| showRestartPrompt.value = false | ||
|
|
||
| if (value === null || value === 'cancel') { | ||
| pendingSettings.clear() | ||
| return | ||
| } | ||
|
|
||
| if (process.env.IS_ELECTRON) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this mean the prompt is shown for web build (even though not officially supported) and will do nothing?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've asked myself the same question, but I've used the same template as everywhere else
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Shadorc The difference is that in the other places it will never even get to that code, the electron check is just there to ensure that the electron only code gets removed properly during the build (as deadcode removal isn't guaranteed with Vue in the mix) and makes it clearer to people reading the code that it is electron only. E.g. for the smooth scrolling switch is not shown outside of electron: https://github.com/FreeTubeApp/FreeTube/blob/development/src/renderer/components/ThemeSettings.vue#L20 and the entire experimental settings component is Electron only: https://github.com/FreeTubeApp/FreeTube/blob/development/src/renderer/views/Settings/Settings.vue#L174-L181 So you can either make the entire settings import and export feature electron only or write it in a way that also has a non-Electron restart and flexible enough that an Android specific restart could be added in downstream FreeTubeAndroid.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the clarification. Concerning your suggestions, I see one more option. It would not remove a lot of settings because, according to my searches, here are the settings depending on If we remove settings mentioned here, we get: The last three depend on So, making the whole feature electron-only would only be useful to support the smooth scrolling feature but would prevent users from importing and exporting settings on mobile. I will update my MR to change the |
||
| Promise.all( | ||
| Array.from(pendingSettings, ([settingKey, settingValue]) => { | ||
| const updaterId = defaultUpdaterId(settingKey) | ||
| return store.dispatch(updaterId, settingValue) | ||
| }) | ||
| ).then(() => { | ||
| window.ftElectron.relaunch() | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // #endregion settings | ||
|
|
||
| </script> | ||
|
|
||
| <style scoped src="./DataSettings.css" /> | ||
Uh oh!
There was an error while loading. Please reload this page.