Skip to content
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

Favorite pages #1545

Merged
merged 3 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
'requirements' => ['id' => '\d+']],
['name' => 'collectiveUserSettings#showRecentPages', 'url' => '/_api/{id}/_userSettings/showRecentPages', 'verb' => 'PUT',
'requirements' => ['id' => '\d+']],
['name' => 'collectiveUserSettings#favoritePages', 'url' => '/_api/{id}/_userSettings/favoritePages', 'verb' => 'PUT',
'requirements' => ['id' => '\d+']],

// share API
['name' => 'share#getCollectiveShares', 'url' => '/_api/{collectiveId}/shares', 'verb' => 'GET',
Expand Down
26 changes: 26 additions & 0 deletions cypress/e2e/page-list.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,32 @@ describe('Page list', function() {
})
})

describe('Page favorites', function() {
it('Allows to add and remove pages from favorites', function() {
cy.get('.page-list .page-list-favorites')
.should('not.exist')

cy.openPageMenu('Day 1')
cy.clickMenuButton('Add to favorites')
cy.get('.page-list .page-list-favorites')
.should('contain', 'Day 1')

cy.get('.page-list-favorites-list')
.should('be.visible')
cy.get('.page-list-favorites .toggle-favorites-button')
.click()
cy.get('.page-list-favorites-list')
.should('not.be.visible')
cy.get('.page-list-favorites .toggle-favorites-button')
.click()

cy.openPageMenu('Day 1')
cy.clickMenuButton('Remove from favorites')
cy.get('.page-list .page-list-favorites')
.should('not.exist')
})
})

describe('Print view', function() {
it('renders all the pages', function() {
let printStub
Expand Down
12 changes: 12 additions & 0 deletions lib/Controller/CollectiveUserSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,16 @@ public function showRecentPages(int $id, bool $showRecentPages): DataResponse {
return [];
});
}

#[NoAdminRequired]
public function favoritePages(int $id, string $favoritePages): DataResponse {
return $this->prepareResponse(function () use ($id, $favoritePages): array {
$this->service->setFavoritePages(
$id,
$this->getUserId(),
$favoritePages
);
return [];
});
}
}
10 changes: 10 additions & 0 deletions lib/Db/Collective.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class Collective extends Entity implements JsonSerializable {
protected bool $shareEditable = false;
protected int $userPageOrder = Collective::defaultPageOrder;
protected bool $userShowRecentPages = Collective::defaultShowRecentPages;
protected array $userFavoritePages = [];
protected ?bool $canLeave = null;

public function getCircleId(): ?string {
Expand Down Expand Up @@ -188,6 +189,10 @@ public function getUserShowRecentPages(): bool {
return $this->userShowRecentPages;
}

public function getUserFavoritePages(): array {
return $this->userFavoritePages;
}

public function getCanLeave(): ?bool {
return $this->canLeave;
}
Expand All @@ -210,6 +215,10 @@ public function setUserShowRecentPages(bool $userShowRecentPages): void {
$this->userShowRecentPages = $userShowRecentPages;
}

public function setUserFavoritePages(array $userFavoritePages): void {
$this->userFavoritePages = $userFavoritePages;
}

public function getUserPermissions(bool $isShare = false): int {
// Public shares always get permissions of a simple member
if ($isShare) {
Expand Down Expand Up @@ -274,6 +283,7 @@ public function jsonSerialize(): array {
'shareEditable' => $this->canEdit() && $this->shareEditable,
'userPageOrder' => $this->userPageOrder,
'userShowRecentPages' => $this->userShowRecentPages,
'userFavoritePages' => $this->userFavoritePages,
'canLeave' => $this->getCanLeave(),
];
}
Expand Down
12 changes: 12 additions & 0 deletions lib/Db/CollectiveUserSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class CollectiveUserSettings extends Entity implements JsonSerializable {
public const supportedSettings = [
'page_order',
'show_recent_pages',
'favorite_pages',
];

protected ?int $collectiveId = null;
Expand Down Expand Up @@ -86,6 +87,17 @@ public function setShowRecentPages(bool $showRecentPages): void {
$this->setSetting('show_recent_pages', $showRecentPages);
}

/**
* @throws NotPermittedException
* @throws JsonException
*/
public function setFavoritePages(array $favoritePages): void {
if ($favoritePages !== array_filter($favoritePages, 'is_int')) {
throw new NotPermittedException('Invalid favorite pages value.');
}
$this->setSetting('favorite_pages', $favoritePages);
}

public function jsonSerialize(): array {
return [
'id' => $this->id,
Expand Down
1 change: 1 addition & 0 deletions lib/Service/CollectiveHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public function getCollectivesForUser(string $userId, bool $getLevel = true, boo
$settings = $this->collectiveUserSettingsMapper->findByCollectiveAndUser($c->getId(), $userId);
$c->setUserPageOrder(($settings ? $settings->getSetting('page_order') : null) ?? Collective::defaultPageOrder);
$c->setUserShowRecentPages(($settings ? $settings->getSetting('show_recent_pages') : null) ?? Collective::defaultShowRecentPages);
$c->setUserFavoritePages(($settings ? $settings->getSetting('favorite_pages') : null) ?? []);
}
}
return $collectives;
Expand Down
24 changes: 24 additions & 0 deletions lib/Service/CollectiveUserSettingsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,28 @@ public function setShowRecentPages(int $collectiveId, string $userId, bool $show
throw new NotPermittedException($e->getMessage(), 0, $e);
}
}

/**
* @throws NotFoundException
* @throws NotPermittedException
*/
public function setFavoritePages(int $collectiveId, string $userId, string $favoritePages): void {
// Expect an array of
try {
$favoritePagesArray = json_decode($favoritePages, false, 512, JSON_THROW_ON_ERROR);
} catch (\JsonException) {
throw new NotPermittedException('Unsupported favorite pages format (stringified array expected): ' . $favoritePages);
}
if (!is_array($favoritePagesArray)) {
throw new NotPermittedException('Unsupported favorite pages format (stringified array expected): ' . $favoritePages);
}
$settings = $this->initSettings($collectiveId, $userId);
$settings->setFavoritePages($favoritePagesArray);

try {
$this->collectiveUserSettingsMapper->insertOrUpdate($settings);
} catch (Exception $e) {
throw new NotPermittedException($e->getMessage(), 0, $e);
}
}
}
19 changes: 16 additions & 3 deletions src/apis/collectives/userSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { collectivesUrl } from './urls.js'
/**
* Set the page order for the current user
*
* @param {number} collectiveId ID of the colletive to be updated
* @param {number} collectiveId ID of the collective to be updated
* @param {number} pageOrder the desired page order for the current user
*/
export function setCollectiveUserSettingPageOrder(collectiveId, pageOrder) {
Expand All @@ -20,9 +20,9 @@ export function setCollectiveUserSettingPageOrder(collectiveId, pageOrder) {
}

/**
* Set the the `show recent pages` toggle for the current user
* Set the `show recent pages` toggle for the current user
*
* @param {number} collectiveId ID of the colletive to be updated
* @param {number} collectiveId ID of the collective to be updated
* @param {boolean} showRecentPages the desired value
*/
export function setCollectiveUserSettingShowRecentPages(collectiveId, showRecentPages) {
Expand All @@ -31,3 +31,16 @@ export function setCollectiveUserSettingShowRecentPages(collectiveId, showRecent
{ showRecentPages },
)
}

/**
* Set favorite pages for the current user
*
* @param {number} collectiveId ID of the collective to be updated
* @param {Array} favoritePages the desired value
*/
export function setCollectiveUserSettingFavoritePages(collectiveId, favoritePages) {
return axios.put(
collectivesUrl(collectiveId, '_userSettings', 'favoritePages'),
{ favoritePages: JSON.stringify(favoritePages) },
)
}
29 changes: 28 additions & 1 deletion src/components/Page/PageActionMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@
{{ t('collectives', 'Show in Files') }}
</NcActionLink>

<!-- Favor page action: only displayed in page list and not for landing page -->
<NcActionButton v-if="inPageList"
:close-after-click="true"
@click="toggleFavoritePage({ id: currentCollective.id, pageId })">
<template #icon>
<StarOffIcon v-if="isFavoritePage(currentCollective.id, pageId)" :size="20" />
<StarIcon v-else :size="20" />
</template>
{{ toggleFavoriteString }}
</NcActionButton>

<!-- Share page action: only displayed in page list and not for landing page (already in collectives actions there) -->
<NcActionButton v-if="inPageList && currentCollectiveCanShare && !isLandingPage"
:close-after-click="true"
Expand Down Expand Up @@ -143,6 +154,8 @@ import MoveOrCopyModal from './MoveOrCopyModal.vue'
import PagesTemplateIcon from '../Icon/PagesTemplateIcon.vue'
import PageActionLastUser from './PageActionLastUser.vue'
import ShareVariantIcon from 'vue-material-design-icons/ShareVariant.vue'
import StarIcon from 'vue-material-design-icons/Star.vue'
import StarOffIcon from 'vue-material-design-icons/StarOff.vue'
import pageMixin from '../../mixins/pageMixin.js'
import { usePagesStore } from '../../stores/pages.js'

Expand All @@ -165,6 +178,8 @@ export default {
PagesTemplateIcon,
PageActionLastUser,
ShareVariantIcon,
StarIcon,
StarOffIcon,
},

mixins: [
Expand Down Expand Up @@ -228,6 +243,7 @@ export default {
'currentCollectiveCanEdit',
'currentCollectiveCanShare',
'currentCollectiveIsPageShare',
'isFavoritePage',
]),
...mapState(usePagesStore, [
'hasSubpages',
Expand Down Expand Up @@ -264,6 +280,12 @@ export default {
return generateUrl(`/f/${this.currentPage.id}`)
},

toggleFavoriteString() {
return this.isFavoritePage(this.currentCollective.id, this.pageId)
? t('collectives', 'Remove from favorites')
: t('collectives', 'Add to favorites')
},

editTemplateString() {
return this.hasTemplate
? t('collectives', 'Edit template for subpages')
Expand Down Expand Up @@ -310,7 +332,12 @@ export default {
'show',
'toggle',
]),
...mapActions(usePagesStore, ['setFullWidthView']),
...mapActions(useCollectivesStore, [
'toggleFavoritePage',
]),
...mapActions(usePagesStore, [
'setFullWidthView',
]),

onCheckFullWidthView() {
this.setFullWidthView({ pageId: this.currentPage.id, fullWidthView: true })
Expand Down
21 changes: 21 additions & 0 deletions src/components/PageList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<SkeletonLoading type="items" :count="3" />
</div>
<div v-else class="page-list">
<!-- Landing page -->
<Item key="Readme"
:to="currentCollectivePath"
:page-id="rootPage.id"
Expand All @@ -81,6 +82,8 @@
:filtered-view="false"
class="page-list-root-page"
@click.native="show('details')" />

<!-- Sort order container (optional) -->
<div v-if="!sortedBy('byOrder')" class="sort-order-container">
<span class="sort-order-chip">
{{ sortedBy('byTitle') ? t('collectives', 'Sorted by title') : t('collectives', 'Sorted by recently changed') }}
Expand All @@ -94,12 +97,19 @@
</NcButton>
</span>
</div>

<!-- Favorites -->
<PageFavorites v-if="showFavorites" />

<!-- Templates (optional) -->
<SubpageList v-if="templateView"
:key="templateView.id"
:page="templateView"
:level="1"
:filtered-view="isFilteredView"
:is-template="true" />

<!-- Filtered view page list -->
<div v-if="isFilteredView" ref="pageListFiltered" class="page-list-filtered">
<NcAppNavigationCaption v-if="filteredPages.length > 0" :name="t('Collectives','Results in title')" />
<RecycleScroller v-if="filteredPages.length > 0"
Expand Down Expand Up @@ -133,6 +143,8 @@
<SkeletonLoading type="items" :count="3" />
</div>
</div>

<!-- Unfiltered view page list -->
<Draggable v-else
:list="subpages"
:parent-id="rootPage.id"
Expand All @@ -146,6 +158,8 @@
class="page-list-drag-item" />
</Draggable>
</div>

<!-- Page trash -->
<PageTrash v-if="displayTrash" />
</NcAppContentList>
</template>
Expand All @@ -165,6 +179,7 @@ import CloseIcon from 'vue-material-design-icons/Close.vue'
import Draggable from './PageList/Draggable.vue'
import SubpageList from './PageList/SubpageList.vue'
import Item from './PageList/Item.vue'
import PageFavorites from './PageList/PageFavorites.vue'
import PageTrash from './PageList/PageTrash.vue'
import SortAlphabeticalAscendingIcon from 'vue-material-design-icons/SortAlphabeticalAscending.vue'
import SortAscendingIcon from 'vue-material-design-icons/SortAscending.vue'
Expand Down Expand Up @@ -193,6 +208,7 @@ export default {
Draggable,
Item,
PagesTemplateIcon,
PageFavorites,
PageTrash,
SubpageList,
SortAlphabeticalAscendingIcon,
Expand Down Expand Up @@ -229,6 +245,7 @@ export default {
'rootPage',
'templatePage',
'currentPage',
'hasFavoritePages',
'keptSortable',
'visibleSubpages',
'sortByOrder',
Expand Down Expand Up @@ -276,6 +293,10 @@ export default {
return (sortOrder) => this.sortByOrder === sortOrder
},

showFavorites() {
return !this.isFilteredView && this.hasFavoritePages
},

isFilteredView() {
return this.filterString !== ''
},
Expand Down
Loading
Loading