Skip to content

Commit

Permalink
feat(favorites): Implement frontend support for page favorites
Browse files Browse the repository at this point in the history
Fixes: #300

Signed-off-by: Jonas <jonas@freesources.org>
  • Loading branch information
mejo- committed Oct 22, 2024
1 parent 0c1fa85 commit de29f8d
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 10 deletions.
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-icon')
.click()
cy.get('.page-list-favorites-list')
.should('not.be.visible')
cy.get('.page-list-favorites .toggle-icon')
.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
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
118 changes: 118 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,50 @@
</NcButton>
</span>
</div>

<!-- Favorites -->
<div v-if="showFavorites" class="page-list-favorites">
<div class="app-content-list-item">
<div class="app-content-list-item-icon" @click="toggleFavorites">
<StarIcon :size="22" fill-color="var(--color-main-text)" />
</div>
<div class="app-content-list-item-line-one" @click="toggleFavorites">
{{ t('collectives', 'Favorites') }}
<div class="toggle-icon">
<ChevronDownIcon :size="22"
:class="{ 'collapsed': !showFavoritesOpen }" />
</div>
</div>
</div>

<div v-show="showFavoritesOpen" class="page-list-favorites-list">
<Item v-for="page in favoritePages"
:key="page.title"
:to="pagePath(page)"
:page-id="page.id"
:parent-id="page.parentId"
:title="page.title"
:timestamp="page.timestamp"
:last-user-id="page.lastUserId"
:last-user-display-name="page.lastUserDisplayName"
:emoji="page.emoji"
:level="2"
:can-edit="currentCollectiveCanEdit"
:in-favorite-list="true"
:filtered-view="false"
@click.native="show('details')" />
</div>
</div>

<!-- 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 +174,8 @@
<SkeletonLoading type="items" :count="3" />
</div>
</div>

<!-- Unfiltered view page list -->
<Draggable v-else
:list="subpages"
:parent-id="rootPage.id"
Expand Down Expand Up @@ -161,6 +204,7 @@ import { usePagesStore } from '../stores/pages.js'
import { useSearchStore } from '../stores/search.js'
import { NcAppNavigationCaption, NcActionButton, NcActions, NcAppContentList, NcButton, NcTextField } from '@nextcloud/vue'
import { showError } from '@nextcloud/dialogs'
import ChevronDownIcon from 'vue-material-design-icons/ChevronDown.vue'
import CloseIcon from 'vue-material-design-icons/Close.vue'
import Draggable from './PageList/Draggable.vue'
import SubpageList from './PageList/SubpageList.vue'
Expand All @@ -169,6 +213,7 @@ import PageTrash from './PageList/PageTrash.vue'
import SortAlphabeticalAscendingIcon from 'vue-material-design-icons/SortAlphabeticalAscending.vue'
import SortAscendingIcon from 'vue-material-design-icons/SortAscending.vue'
import SortClockAscendingOutlineIcon from 'vue-material-design-icons/SortClockAscendingOutline.vue'
import StarIcon from 'vue-material-design-icons/Star.vue'
import PagesTemplateIcon from './Icon/PagesTemplateIcon.vue'
import { scrollToPage } from '../util/scrollToElement.js'
import { pageOrders } from '../util/sortOrders.js'
Expand All @@ -189,6 +234,7 @@ export default {
NcAppContentList,
NcButton,
NcTextField,
ChevronDownIcon,
CloseIcon,
Draggable,
Item,
Expand All @@ -198,6 +244,7 @@ export default {
SortAlphabeticalAscendingIcon,
SortAscendingIcon,
SortClockAscendingOutlineIcon,
StarIcon,
RecycleScroller,
NcAppNavigationCaption,
},
Expand All @@ -214,6 +261,7 @@ export default {
contentFilteredPages: [],
loadingContentFilteredPages: false,
getContentFilteredPagesDebounced: debounce(this.getContentFilteredPages, 700),
showFavoritesOpen: true,
}
},

Expand All @@ -226,9 +274,12 @@ export default {
'currentCollectivePath',
]),
...mapState(usePagesStore, [
'pagePath',
'rootPage',
'templatePage',
'currentPage',
'hasFavoritePages',
'favoritePages',
'keptSortable',
'visibleSubpages',
'sortByOrder',
Expand Down Expand Up @@ -276,6 +327,10 @@ export default {
return (sortOrder) => this.sortByOrder === sortOrder
},

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

isFilteredView() {
return this.filterString !== ''
},
Expand Down Expand Up @@ -404,6 +459,10 @@ export default {
this.loadingContentFilteredPages = false
}
},

toggleFavorites() {
this.showFavoritesOpen = !this.showFavoritesOpen
},
},
}

Expand Down Expand Up @@ -485,6 +544,14 @@ li.toggle-button.selected {
margin-block-end: 8px;
}

.page-list-favorites {
border-bottom: 1px solid var(--color-border);

.page-list-favorites-list {
padding-left: 20px;
}
}

.page-list-filtered {
flex-grow: 1;
max-height: 100%;
Expand Down Expand Up @@ -527,4 +594,55 @@ li.toggle-button.selected {
}
}
}

.app-content-list-item {
box-sizing: border-box;
height: var(--default-clickable-area);
// border-bottom: 4px solid var(--color-main-background);
margin-bottom: 4px;

padding: 0;
border-radius: var(--border-radius-large);

&:hover, &:focus, &:active, &.highlight {
background-color: var(--color-background-hover);

span.item-icon-badge {
background-color: var(--color-background-hover);
}

span.item-icon-favorite {
background-color: var(--color-background-hover);
}
}

.app-content-list-item-icon {
display: flex;
justify-content: center;
align-items: center;

.material-design-icon {
cursor: pointer;
}
}

.app-content-list-item-line-one {
display: flex;
padding-left: 40px;
}

.toggle-icon {
display: flex;
padding-left: 4px;

.material-design-icon {
cursor: pointer;
}

.collapsed {
transition: transform var(--animation-slow);
transform: rotate(-90deg);
}
}
}
</style>
Loading

0 comments on commit de29f8d

Please sign in to comment.