Skip to content

Commit

Permalink
Implement highlighting of active feed item and more keyboard shortcuts (
Browse files Browse the repository at this point in the history
#2677)

* add: highlighting of active item
* add: keyboard shortcuts "u", "s", "i", "l" and "o"
* dev: force running commands on stale session
* add: prepare scrolling to the current item

Signed-off-by: Patrizio Bekerle <[email protected]>
  • Loading branch information
pbek authored Jun 8, 2024
1 parent 9ff40ab commit 8145582
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 21 deletions.
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 @@ const DEFAULT_DISPLAY_LIST_CONFIG = {
export type Config = {
unreadFilter: boolean;
starFlter: boolean;
starFilter: boolean;
}
export default Vue.extend({
Expand Down Expand Up @@ -121,6 +125,7 @@ export default Vue.extend({
}
},
cache: [] as FeedItem[] | undefined,
selectedItem: undefined as FeedItem | undefined,
}
},
computed: {
Expand All @@ -130,10 +135,15 @@ export default Vue.extend({
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 @@ export default Vue.extend({
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
const refName = 'feedItemRow' + item.id
const ref = this.$refs[refName]
// Make linter happy
Expand All @@ -194,43 +204,34 @@ export default Vue.extend({
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
// 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,13 +31,17 @@ export default Vue.extend({
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
newBookmark() {
this.$el.scrollTop = 0
Expand All @@ -58,6 +62,10 @@ export default Vue.extend({
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 @@ export default Vue.extend({
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;
};

0 comments on commit 8145582

Please sign in to comment.