diff --git a/.github/actions/deploy/appinfo/info.xml.dist b/.github/actions/deploy/appinfo/info.xml.dist index f6a4c23c7..9d63263d7 100755 --- a/.github/actions/deploy/appinfo/info.xml.dist +++ b/.github/actions/deploy/appinfo/info.xml.dist @@ -20,7 +20,7 @@ https://raw.githubusercontent.com/nextcloud/cookbook/stable/img/screenshot2.png - + diff --git a/CHANGELOG.md b/CHANGELOG.md index 229cff746..946599859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [Unreleased] +### Changed +- Make info block visibility configurable + [1404](https://github.com/nextcloud/cookbook/pull/1404) @MarcelRobitaille + ### Fixed - Make "None" category string translatable [#1323](https://github.com/nextcloud/cookbook/pull/1344) @seyfeb diff --git a/appinfo/info.xml b/appinfo/info.xml index c55368d0a..e1e347ed8 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -20,7 +20,7 @@ https://raw.githubusercontent.com/nextcloud/cookbook/stable/img/screenshot2.png - + diff --git a/lib/Controller/Implementation/ConfigImplementation.php b/lib/Controller/Implementation/ConfigImplementation.php index 25dbb1cf1..d9da0b617 100644 --- a/lib/Controller/Implementation/ConfigImplementation.php +++ b/lib/Controller/Implementation/ConfigImplementation.php @@ -31,6 +31,8 @@ public function __construct( $this->userFolder = $userFolder; } + protected const KEY_VISIBLE_INFO_BLOCKS = 'visibleInfoBlocks'; + /** * Get the current configuration of the app * @@ -43,6 +45,7 @@ public function list() { 'folder' => $this->userFolder->getPath(), 'update_interval' => $this->dbCacheService->getSearchIndexUpdateInterval(), 'print_image' => $this->service->getPrintImage(), + self::KEY_VISIBLE_INFO_BLOCKS => $this->service->getVisibleInfoBlocks(), ], Http::STATUS_OK); } @@ -72,6 +75,10 @@ public function config() { $this->service->setPrintImage((bool)$data['print_image']); } + if (isset($data[self::KEY_VISIBLE_INFO_BLOCKS])) { + $this->service->setVisibleInfoBlocks($data[self::KEY_VISIBLE_INFO_BLOCKS]); + } + $this->dbCacheService->triggerCheck(); return new JSONResponse('OK', Http::STATUS_OK); diff --git a/lib/Helper/UserConfigHelper.php b/lib/Helper/UserConfigHelper.php index 980b1b0c7..860bff502 100644 --- a/lib/Helper/UserConfigHelper.php +++ b/lib/Helper/UserConfigHelper.php @@ -39,6 +39,7 @@ public function __construct( protected const KEY_LAST_INDEX_UPDATE = 'last_index_update'; protected const KEY_UPDATE_INTERVAL = 'update_interval'; protected const KEY_PRINT_IMAGE = 'print_image'; + protected const KEY_VISIBLE_INFO_BLOCKS = 'visible_info_blocks'; protected const KEY_FOLDER = 'folder'; /** @@ -154,6 +155,38 @@ public function setPrintImage(bool $value): void { } } + /** + * Determines which info blocks are displayed next to the recipe + * + * @return array keys: info block ids, values: display state + * @throws UserNotLoggedInException if no user is logged in + */ + public function getVisibleInfoBlocks(): array { + $rawValue = $this->getRawValue(self::KEY_VISIBLE_INFO_BLOCKS); + + if ($rawValue === '') { + return [ + 'preparation-time' => true, + 'cooking-time' => true, + 'total-time' => true, + 'nutrition-information' => true, + 'tools' => true, + ]; + } + + return json_decode($rawValue, true); + } + + /** + * Sets which info blocks are displayed next to the recipe + * + * @param array keys: info block ids, values: display state + * @throws UserNotLoggedInException if no user is logged in + */ + public function setVisibleInfoBlocks(array $visibleInfoBlocks): void { + $this->setRawValue(self::KEY_VISIBLE_INFO_BLOCKS, json_encode($visibleInfoBlocks)); + } + /** * Get the name of the default cookbook. * diff --git a/lib/Service/RecipeService.php b/lib/Service/RecipeService.php index d4020bb56..0ec4c44c9 100755 --- a/lib/Service/RecipeService.php +++ b/lib/Service/RecipeService.php @@ -515,6 +515,22 @@ public function getPrintImage() { return $this->userConfigHelper->getPrintImage(); } + /** + * Sets which info blocks are displayed next to the recipe + * @param array keys: info block ids, values: display state + */ + public function setVisibleInfoBlocks(array $visibleInfoBlocks) { + $this->userConfigHelper->setVisibleInfoBlocks($visibleInfoBlocks); + } + + /** + * Determines which info blocks are displayed next to the recipe + * @return array keys: info block ids, values: display state + */ + public function getVisibleInfoBlocks(): array { + return $this->userConfigHelper->getVisibleInfoBlocks(); + } + /** * Get recipe file contents as an array * diff --git a/src/components/RecipeView.vue b/src/components/RecipeView.vue index 2b24a7ff9..b3537ecd5 100644 --- a/src/components/RecipeView.vue +++ b/src/components/RecipeView.vue @@ -70,19 +70,27 @@
-
+

{{ t("cookbook", "Tools") }}

@@ -446,9 +454,13 @@ export default { return ( this.recipe.nutrition && !(this.recipe.nutrition instanceof Array) && - Object.keys(this.recipe.nutrition).length > 1 + Object.keys(this.recipe.nutrition).length > 1 && + this.visibleInfoBlocks["nutrition-information"] ) }, + visibleInfoBlocks() { + return this.$store.state.config?.visibleInfoBlocks ?? {} + }, }, watch: { recipe(r) { @@ -861,6 +873,7 @@ main { display: grid; grid-template-columns: 1fr 1em 2fr; + grid-template-rows: 100% 100% 100% 1fr; .ingredients { grid-column: 1/2; diff --git a/src/components/SettingsDialog.vue b/src/components/SettingsDialog.vue index 3c7482dee..d5b9af50d 100644 --- a/src/components/SettingsDialog.vue +++ b/src/components/SettingsDialog.vue @@ -81,6 +81,84 @@ + +
+ + {{ + t( + "cookbook", + "Control which blocks of information are shown in the recipe view. If you do not use some features and find them distracting, you may hide them." + ) + }} + +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+
+
@@ -98,6 +176,23 @@ import { showSimpleAlertModal } from "cookbook/js/modals" export const SHOW_SETTINGS_EVENT = "show-settings" +const INFO_BLOCK_KEYS = [ + "preparation-time", + "cooking-time", + "total-time", + "nutrition-information", + "tools", +] + +// The Vue representation of multiple checkboxes is an array of all checked values +// However, the backend representation is an object (map of block ids to booleans) +const visibleInfoBlocksEncode = (arr) => + Object.fromEntries(INFO_BLOCK_KEYS.map((key) => [key, arr.includes(key)])) +const visibleInfoBlocksDecode = (obj) => + Object.entries(obj) + .filter(([, v]) => v) + .map(([k]) => k) + export default { name: "SettingsDialog", components: { @@ -120,6 +215,8 @@ export default { // (the one when config is loaded at page load) resetInterval: true, updateInterval: 0, + visibleInfoBlocks: [...INFO_BLOCK_KEYS], + resetVisibleInfoBlocks: true, } }, watch: { @@ -130,7 +227,7 @@ export default { return } try { - api.config.printImage.update(newVal) + await api.config.printImage.update(newVal) // Should this check the response of the query? To catch some errors that redirect the page } catch { await showSimpleAlertModal( @@ -169,6 +266,27 @@ export default { this.updateInterval = oldVal } }, + async visibleInfoBlocks(newVal, oldVal) { + // Avoid infinite loop on page load and when reseting value after failed submit + if (this.resetVisibleInfoBlocks) { + this.resetVisibleInfoBlocks = false + return + } + try { + const data = visibleInfoBlocksEncode(newVal) + await api.config.visibleInfoBlocks.update(data) + await this.$store.dispatch("refreshConfig") + // Should this check the response of the query? To catch some errors that redirect the page + } catch (err) { + // eslint-disable-next-line no-console + console.error("Error while trying to save info blocks", err) + await showSimpleAlertModal( + t("cookbook", "Could save visible info blocks") + ) + this.resetVisibleInfoBlocks = true + this.visibleInfoBlocks = oldVal + } + }, }, mounted() { this.setup() @@ -218,20 +336,25 @@ export default { */ async setup() { try { - const response = await api.config.get() - const config = response.data + await this.$store.dispatch("refreshConfig") + const { config } = this.$store.state this.resetPrintImage = false + this.resetVisibleInfoBlocks = false if (!config) { throw new Error() } this.printImage = config.print_image + this.visibleInfoBlocks = visibleInfoBlocksDecode( + config.visibleInfoBlocks + ) this.showTagCloudInRecipeList = this.$store.state.localSettings.showTagCloudInRecipeList this.updateInterval = config.update_interval this.recipeFolder = config.folder - } catch { + } catch (err) { + this.$log.error("Error setting up SettingsDialog", err) await showSimpleAlertModal( t("cookbook", "Loading config failed") ) @@ -284,6 +407,10 @@ export default { animation: rotate var(--animation-duration, 0.8s) linear infinite; color: var(--color-loading-dark); } + +.settings-info-blocks__legend { + margin-bottom: 10px; +}