diff --git a/CHANGELOG.md b/CHANGELOG.md
index 251c668351..b1f547e7b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/Makefile b/Makefile
index ece7d07f11..fd3b254d81 100644
--- a/Makefile
+++ b/Makefile
@@ -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:
diff --git a/src/components/feed-display/FeedItemDisplay.vue b/src/components/feed-display/FeedItemDisplay.vue
index 0831214212..bc5fd4ad25 100644
--- a/src/components/feed-display/FeedItemDisplay.vue
+++ b/src/components/feed-display/FeedItemDisplay.vue
@@ -12,7 +12,12 @@
+
+
+
+
+
@@ -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,
@@ -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
},
diff --git a/src/components/feed-display/FeedItemDisplayList.vue b/src/components/feed-display/FeedItemDisplayList.vue
index d62a4ee7e1..b66a1bcd7b 100644
--- a/src/components/feed-display/FeedItemDisplayList.vue
+++ b/src/components/feed-display/FeedItemDisplayList.vue
@@ -36,12 +36,16 @@
-
-
+
@@ -74,7 +78,7 @@ const DEFAULT_DISPLAY_LIST_CONFIG = {
export type Config = {
unreadFilter: boolean;
- starFlter: boolean;
+ starFilter: boolean;
}
export default Vue.extend({
@@ -121,6 +125,7 @@ export default Vue.extend({
}
},
cache: [] as FeedItem[] | undefined,
+ selectedItem: undefined as FeedItem | undefined,
}
},
computed: {
@@ -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
},
@@ -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) {
const refName = 'feedItemRow' + item.id
const ref = this.$refs[refName]
// Make linter happy
@@ -194,21 +204,17 @@ 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
+ // 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
@@ -216,21 +222,16 @@ export default Vue.extend({
// 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)
}
},
},
diff --git a/src/components/feed-display/FeedItemRow.vue b/src/components/feed-display/FeedItemRow.vue
index baa9e78edd..b7e9b86929 100644
--- a/src/components/feed-display/FeedItemRow.vue
+++ b/src/components/feed-display/FeedItemRow.vue
@@ -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);
+ }
diff --git a/src/components/feed-display/VirtualScroll.vue b/src/components/feed-display/VirtualScroll.vue
index c6bb471bd9..160be18eeb 100644
--- a/src/components/feed-display/VirtualScroll.vue
+++ b/src/components/feed-display/VirtualScroll.vue
@@ -31,6 +31,7 @@ export default Vue.extend({
scrollHeight: 500,
initialLoadingSkeleton: false,
initialLoadingTimeout: null,
+ elementToShow: null,
}
},
computed: {
@@ -38,6 +39,9 @@ export default Vue.extend({
return this.$store.state.items.fetchingItems[this.key]
},
},
+ created() {
+ this.elementToShowAlignToTop = false
+ },
watch: {
newBookmark() {
this.$el.scrollTop = 0
@@ -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 = []
@@ -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', {
diff --git a/src/types/FeedItem.ts b/src/types/FeedItem.ts
index 879a663f4d..979f4bfcb8 100644
--- a/src/types/FeedItem.ts
+++ b/src/types/FeedItem.ts
@@ -6,4 +6,5 @@ export type FeedItem = {
feedId: number;
guidHash: string;
pubDate: number;
+ url: string;
};