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

Implement highlighting of active feed item and more keyboard shortcuts #2677

Merged
merged 5 commits into from
Jun 8, 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ You can also check [on GitHub](https://github.com/nextcloud/news/releases), the
### Changed
- added alternative development environment (#2670)
- Implement `j` and `k` keyboards shortcuts for navigating through feed items (#2671)
- Implement `s`, `i` and `l` keyboards shortcuts for staring current feed item (#2677)
- Implement `o` keyboards shortcut for opening the URL of current feed item (#2677)
- Implement `u` keyboards shortcut for marking current feed item read/unread (#2677)
- Implement highlighting of active feed item (#2677)

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ nextcloud-server:

.PHONY: term
term:
zellij --layout term.kdl attach nextcloud-news -c
zellij --layout term.kdl attach nextcloud-news -cf

.PHONY: term-kill
term-kill:
Expand Down
24 changes: 24 additions & 0 deletions src/components/feed-display/FeedItemDisplay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
</NcActionButton>
</NcActions>
<StarIcon :class="{'starred': item.starred }" @click="toggleStarred(item)" />
<EyeIcon v-if="item.unread" @click="toggleRead(item)" />
<EyeCheckIcon v-if="!item.unread" @click="toggleRead(item)" />
<CloseIcon @click="clearSelected()" />
<button v-shortkey="{s: ['s'], l: ['l'], i: ['i']}" class="hidden" @shortkey="toggleStarred(item)" />
<button v-shortkey="['o']" class="hidden" @shortkey="openUrl(item)" />
<button v-shortkey="['u']" class="hidden" @shortkey="toggleRead(item)" />
</div>
<div class="article">
<div class="heading">
Expand Down Expand Up @@ -109,10 +114,14 @@ import ShareItem from '../ShareItem.vue'
import { Feed } from '../../types/Feed'
import { FeedItem } from '../../types/FeedItem'
import { ACTIONS, MUTATIONS } from '../../store'
import EyeIcon from 'vue-material-design-icons/Eye.vue'
import EyeCheckIcon from 'vue-material-design-icons/EyeCheck.vue'

export default Vue.extend({
name: 'FeedItemDisplay',
components: {
EyeCheckIcon,
EyeIcon,
CloseIcon,
StarIcon,
ShareVariant,
Expand Down Expand Up @@ -170,6 +179,21 @@ export default Vue.extend({
this.$store.dispatch(item.starred ? ACTIONS.UNSTAR_ITEM : ACTIONS.STAR_ITEM, { item })
},

toggleRead(item: FeedItem): void {
if (item.unread) {
this.$store.dispatch(ACTIONS.MARK_READ, { item })
} else {
this.$store.dispatch(ACTIONS.MARK_UNREAD, { item })
}
},

openUrl(item: FeedItem): void {
// Open the item url in a new tab
if (item.url) {
window.open(item.url, '_blank')
}
},

closeShareMenu() {
this.showShareMenu = false
},
Expand Down
39 changes: 20 additions & 19 deletions src/components/feed-display/FeedItemDisplayList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@
</button>
</div>
<div class="feed-item-display-container">
<VirtualScroll :reached-end="reachedEnd"
<VirtualScroll ref="virtualScroll"
:reached-end="reachedEnd"
:fetch-key="fetchKey"
@load-more="fetchMore()">
<template v-if="items && items.length > 0">
<template v-for="item in filterSortedItems()">
<FeedItemRow :key="item.id" :ref="'feedItemRow' + item.id" :item="item" />
<FeedItemRow :key="item.id"
:ref="'feedItemRow' + item.id"
:item="item"
:class="{ 'active': selectedItem && selectedItem.id === item.id }" />
</template>
</template>
</VirtualScroll>
Expand Down Expand Up @@ -74,7 +78,7 @@

export type Config = {
unreadFilter: boolean;
starFlter: boolean;
starFilter: boolean;
}

export default Vue.extend({
Expand Down Expand Up @@ -121,6 +125,7 @@
}
},
cache: [] as FeedItem[] | undefined,
selectedItem: undefined as FeedItem | undefined,
}
},
computed: {
Expand All @@ -130,10 +135,15 @@
cfg() {
return _.defaults({ ...this.config }, DEFAULT_DISPLAY_LIST_CONFIG)
},
selectedItem() {
getSelectedItem() {
return this.$store.getters.selected
},
},
watch: {
getSelectedItem(newVal) {
this.selectedItem = newVal
},
},
mounted() {
this.mounted = true
},
Expand Down Expand Up @@ -185,7 +195,7 @@
return response.sort(this.sort)
},
// Trigger the click event programmatically to benefit from the item handling inside the FeedItemRow component
clickItem(item: FeedItem) {
clickItem(item: FeedItem, alignToTop = false) {

Check warning on line 198 in src/components/feed-display/FeedItemDisplayList.vue

View workflow job for this annotation

GitHub Actions / eslint node

'alignToTop' is assigned a value but never used

Check warning on line 198 in src/components/feed-display/FeedItemDisplayList.vue

View workflow job for this annotation

GitHub Actions / eslint

'alignToTop' is assigned a value but never used
const refName = 'feedItemRow' + item.id
const ref = this.$refs[refName]
// Make linter happy
Expand All @@ -194,43 +204,34 @@

if (element) {
element.click()

// TODO: This doesn't seem to do a lot in the VirtualScroll component
element.scrollIntoView(true)
// this.$nextTick(() => element.scrollIntoView())
const virtualScroll = this.$refs.virtualScroll

Check warning on line 207 in src/components/feed-display/FeedItemDisplayList.vue

View workflow job for this annotation

GitHub Actions / eslint node

'virtualScroll' is assigned a value but never used

Check warning on line 207 in src/components/feed-display/FeedItemDisplayList.vue

View workflow job for this annotation

GitHub Actions / eslint

'virtualScroll' is assigned a value but never used
// TODO: This is still jerky and even can derail the current item
// virtualScroll.showElement(element, alignToTop)
}
},
currentIndex(items: FeedItem[]): number {
return this.selectedItem ? items.findIndex((item: FeedItem) => item.id === this.selectedItem.id) || 0 : -1
},
// TODO: Make jumpToPreviousItem() highlight the current item
jumpToPreviousItem() {
console.log('Previous item')
const items = this.filterSortedItems()
let currentIndex = this.currentIndex(items)
console.log('currentIndex', currentIndex)
// Prepare to jump to the first item, if none was selected
if (currentIndex === -1) {
currentIndex = 1
}
// Jump to the previous item
if (currentIndex > 0) {
const previousItem = items[currentIndex - 1]
console.log('previousItem', previousItem)
this.clickItem(previousItem)
this.clickItem(previousItem, true)
}
},
// TODO: Make jumpToNextItem() highlight the current item
jumpToNextItem() {
console.log('Next item')
const items = this.filterSortedItems()
const currentIndex = this.currentIndex(items)
console.log('currentIndex', currentIndex)
// Jump to the first item, if none was selected, otherwise jump to the next item
if (currentIndex === -1 || (currentIndex < items.length - 1)) {
const nextItem = items[currentIndex + 1]
console.log('nextItem', nextItem)
this.clickItem(nextItem)
this.clickItem(nextItem, false)
}
},
},
Expand Down
4 changes: 4 additions & 0 deletions src/components/feed-display/FeedItemRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -276,4 +276,8 @@ export default Vue.extend({
.feed-item-row .button-container .eye-check-icon {
color: var(--color-placeholder-dark);
}

.active, .active:hover {
background-color: var(--color-background-darker);
}
</style>
16 changes: 15 additions & 1 deletion src/components/feed-display/VirtualScroll.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,18 @@
scrollHeight: 500,
initialLoadingSkeleton: false,
initialLoadingTimeout: null,
elementToShow: null,
}
},
computed: {
fetching() {
return this.$store.state.items.fetchingItems[this.key]
},
},
created() {
this.elementToShowAlignToTop = false
},
watch: {

Check warning on line 45 in src/components/feed-display/VirtualScroll.vue

View workflow job for this annotation

GitHub Actions / eslint node

The "watch" property should be above the "created" property on line 42

Check warning on line 45 in src/components/feed-display/VirtualScroll.vue

View workflow job for this annotation

GitHub Actions / eslint

The "watch" property should be above the "created" property on line 42
newBookmark() {
this.$el.scrollTop = 0
},
Expand All @@ -58,6 +62,10 @@
this.scrollTop = this.$el.scrollTop
this.scrollHeight = this.$el.scrollHeight
},
showElement(element, alignToTop) {
this.elementToShow = element
this.elementToShowAlignToTop = alignToTop
},
},
render(h) {
let children = []
Expand Down Expand Up @@ -106,7 +114,13 @@

const scrollTop = this.scrollTop
this.$nextTick(() => {
this.$el.scrollTop = scrollTop
if (this.elementToShow) {
// this.elementToShow.scrollIntoView(this.elementToShowAlignToTop)
this.elementToShow.scrollIntoView({ behavior: 'smooth', block: 'center' })
this.elementToShow = null
} else {
this.$el.scrollTop = scrollTop
}
})

return h('div', {
Expand Down
1 change: 1 addition & 0 deletions src/types/FeedItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export type FeedItem = {
feedId: number;
guidHash: string;
pubDate: number;
url: string;
};
Loading