summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDevlin Junker <devlin.junker@gmail.com>2023-09-01 13:24:16 -0700
committerBenjamin Brahmer <info@b-brahmer.de>2023-09-15 06:57:42 +0200
commit5caefbadc6b9fec1010a0e092dc0f014525d21b9 (patch)
tree5896cc471d0ca2e6061018c4234524e57d7792d3 /src
parente2e8ae517afcb57bd2e0d5e6d4aacbb96603bd96 (diff)
add routes for folder items and all items
Signed-off-by: Devlin Junker <devlin.junker@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/components/Sidebar.vue6
-rw-r--r--src/components/feed-display/FeedItemDisplay.vue18
-rw-r--r--src/components/feed-display/FeedItemDisplayList.vue3
-rw-r--r--src/components/routes/All.vue63
-rw-r--r--src/components/routes/Folder.vue94
-rw-r--r--src/dataservices/item.service.ts52
-rw-r--r--src/routes/index.ts16
-rw-r--r--src/store/item.ts51
8 files changed, 294 insertions, 9 deletions
diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue
index eedead3b0..493eba31e 100644
--- a/src/components/Sidebar.vue
+++ b/src/components/Sidebar.vue
@@ -20,7 +20,7 @@
<NcCounterBubble>{{ items.unreadCount }}</NcCounterBubble>
</template>
</NcAppNavigationItem>
- <NcAppNavigationItem :title="t('news', 'All articles')" icon="icon-rss">
+ <NcAppNavigationItem :title="t('news', 'All articles')" icon="icon-rss" :to="{ name: ROUTES.ALL }">
<template #actions>
<ActionButton icon="icon-checkmark" @click="alert('TODO: Edit')">
t('news','Mark read')
@@ -37,13 +37,13 @@
:key="topLevelItem.name || topLevelItem.title"
:title="topLevelItem.name || topLevelItem.title"
:icon="isFolder(topLevelItem) ? 'icon-folder': ''"
- :to="isFolder(topLevelItem) ? {} : { name: ROUTES.FEED, params: { feedId: topLevelItem.id.toString() } }"
+ :to="isFolder(topLevelItem) ? { name: ROUTES.FOLDER, params: { folderId: topLevelItem.id.toString() }} : { name: ROUTES.FEED, params: { feedId: topLevelItem.id.toString() } }"
:allow-collapse="true">
<template #default>
<NcAppNavigationItem v-for="feed in topLevelItem.feeds"
:key="feed.name"
:title="feed.title"
- :to="{ name: ROUTES.FEED, props: { feedId: feed.id } }">
+ :to="{ name: ROUTES.FEED, params: { feedId: feed.id } }">
<template #icon>
<RssIcon v-if="!feed.faviconLink" />
<span v-if="feed.faviconLink" style="width: 24px; background-size: contain;" :style="{ 'backgroundImage': 'url(' + feed.faviconLink + ')' }" />
diff --git a/src/components/feed-display/FeedItemDisplay.vue b/src/components/feed-display/FeedItemDisplay.vue
index 4eab900ab..8d97e856e 100644
--- a/src/components/feed-display/FeedItemDisplay.vue
+++ b/src/components/feed-display/FeedItemDisplay.vue
@@ -183,7 +183,7 @@ export default Vue.extend({
</script>
-<style scoped>
+<style>
.feed-item-display {
max-height: 100%;
overflow-y: hidden;
@@ -210,6 +210,22 @@ export default Vue.extend({
color: #3a84e4
}
+ .article .body ul {
+ margin: 7px 0;
+ padding-left: 14px;
+ list-style-type: disc;
+ }
+
+ .article .body ul li {
+ cursor: default;
+ line-height: 21px;
+ }
+
+ .article .body p {
+ line-height: 1.5;
+ margin: 7px 0 14px 0;
+ }
+
.article .subtitle {
color: var(--color-text-lighter);
font-size: 15px;
diff --git a/src/components/feed-display/FeedItemDisplayList.vue b/src/components/feed-display/FeedItemDisplayList.vue
index 8fce1fb25..8fc58281e 100644
--- a/src/components/feed-display/FeedItemDisplayList.vue
+++ b/src/components/feed-display/FeedItemDisplayList.vue
@@ -1,5 +1,5 @@
<template>
- <div>
+ <div style="height: 100%; display: flex; flex-direction: column;">
<div style="justify-content: right; display: flex">
<NcActions class="filter-container" :force-menu="true">
<template #icon>
@@ -193,6 +193,7 @@ export default Vue.extend({
.feed-item-container {
max-width: 50%;
overflow-y: hidden;
+ height: calc(100vh - 50px - 50px - 10px)
}
.filter-container {
diff --git a/src/components/routes/All.vue b/src/components/routes/All.vue
new file mode 100644
index 000000000..a1cc52fef
--- /dev/null
+++ b/src/components/routes/All.vue
@@ -0,0 +1,63 @@
+<template>
+ <div class="route-container">
+ <div class="header">
+ {{ t('news', 'All Articles') }}
+ </div>
+
+ <FeedItemDisplayList :items="allItems"
+ :fetch-key="'all'"
+ @load-more="fetchMore()" />
+ </div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue'
+import { mapState } from 'vuex'
+
+import FeedItemDisplayList from '../feed-display/FeedItemDisplayList.vue'
+
+import { FeedItem } from '../../types/FeedItem'
+import { ACTIONS, MUTATIONS } from '../../store'
+
+export default Vue.extend({
+ components: {
+ FeedItemDisplayList,
+ },
+ computed: {
+ ...mapState(['items']),
+
+ allItems(): FeedItem[] {
+ return this.$store.getters.allItems
+ },
+ },
+ created() {
+ this.$store.commit(MUTATIONS.SET_SELECTED_ITEM, { id: undefined })
+ },
+ methods: {
+ async fetchMore() {
+ if (!this.$store.state.items.fetchingItems.all) {
+ this.$store.dispatch(ACTIONS.FETCH_ITEMS)
+ }
+ },
+ },
+})
+</script>
+
+<style scoped>
+ .route-container {
+ height: 100%;
+ }
+
+ .header {
+ padding-left: 50px;
+ position: absolute;
+ top: 1em;
+ font-weight: 700;
+ }
+
+ .counter-bubble {
+ display: inline-block;
+ vertical-align: sub;
+ margin-left: 10px;
+ }
+</style>
diff --git a/src/components/routes/Folder.vue b/src/components/routes/Folder.vue
new file mode 100644
index 000000000..17fd768c3
--- /dev/null
+++ b/src/components/routes/Folder.vue
@@ -0,0 +1,94 @@
+<template>
+ <div class="route-container">
+ <div class="header">
+ {{ folder ? folder.name : '' }}
+ <NcCounterBubble v-if="folder" class="counter-bubble">
+ {{ unreadCount }}
+ </NcCounterBubble>
+ </div>
+
+ <FeedItemDisplayList :items="items" :fetch-key="'folder-'+folderId" @load-more="fetchMore()" />
+ </div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue'
+import { mapState } from 'vuex'
+
+import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js'
+
+import FeedItemDisplayList from '../feed-display/FeedItemDisplayList.vue'
+
+import { FeedItem } from '../../types/FeedItem'
+import { ACTIONS, MUTATIONS } from '../../store'
+import { Feed } from '../../types/Feed'
+import { Folder } from '../../types/Folder'
+
+export default Vue.extend({
+ components: {
+ NcCounterBubble,
+ FeedItemDisplayList,
+ },
+ props: {
+ folderId: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['items', 'feeds', 'folders']),
+ folder(): Folder {
+ return this.$store.getters.folders.find((folder: Folder) => folder.id === this.id)
+ },
+ items(): FeedItem[] {
+ const feeds: Array<number> = this.$store.getters.feeds.filter((feed: Feed) => feed.folderId === this.id).map((feed: Feed) => feed.id)
+
+ return this.$store.state.items.allItems.filter((item: FeedItem) => {
+ return feeds.includes(item.feedId)
+ }) || []
+ },
+ id(): number {
+ return Number(this.folderId)
+ },
+ unreadCount(): number {
+ const totalUnread = this.$store.getters.feeds
+ .filter((feed: Feed) => feed.folderId === this.id)
+ .reduce((acc: number, feed: Feed) => { acc += feed.unreadCount; return acc }, 0)
+
+ return totalUnread
+ },
+ },
+ created() {
+ this.$store.commit(MUTATIONS.SET_SELECTED_ITEM, { id: undefined })
+ this.fetchMore()
+ this.$watch(() => this.$route.params, this.fetchMore)
+ },
+ methods: {
+ async fetchMore() {
+ if (!this.$store.state.items.fetchingItems['folder-' + this.folderId]) {
+ this.$store.dispatch(ACTIONS.FETCH_FOLDER_FEED_ITEMS, { folderId: this.id })
+ }
+ },
+ },
+})
+</script>
+
+<style scoped>
+.route-container {
+ height: 100%;
+}
+
+.header {
+ padding-left: 50px;
+ position: absolute;
+ top: 1em;
+ font-weight: 700;
+}
+
+.counter-bubble {
+ display: inline-block;
+ vertical-align: sub;
+ margin-left: 10px;
+}
+
+</style>
diff --git a/src/dataservices/item.service.ts b/src/dataservices/item.service.ts
index 6d4aabdb4..98b9d9eb3 100644
--- a/src/dataservices/item.service.ts
+++ b/src/dataservices/item.service.ts
@@ -6,16 +6,39 @@ import { API_ROUTES } from '../types/ApiRoutes'
import { FeedItem } from '../types/FeedItem'
export const ITEM_TYPES = {
- ALL: 0,
+ FEED: 0,
+ FOLDER: 1,
STARRED: 2,
+ ALL: 3,
UNREAD: 6,
}
export class ItemService {
+ static debounceFetchAll = _.debounce(ItemService.fetchAll, 400, { leading: true })
static debounceFetchStarred = _.debounce(ItemService.fetchStarred, 400, { leading: true })
static debounceFetchUnread = _.debounce(ItemService.fetchUnread, 400, { leading: true })
static debounceFetchFeedItems = _.debounce(ItemService.fetchFeedItems, 400, { leading: true })
+ static debounceFetchFolderFeedItems = _.debounce(ItemService.fetchFolderItems, 400, { leading: true })
+
+ /**
+ * Makes backend call to retrieve all items
+ *
+ * @param start (id of last starred item loaded)
+ * @return {AxiosResponse} response object containing backend request response
+ */
+ static async fetchAll(start: number): Promise<AxiosResponse> {
+ return await axios.get(API_ROUTES.ITEMS, {
+ params: {
+ limit: 40,
+ oldestFirst: false,
+ search: '',
+ showAll: true,
+ type: ITEM_TYPES.ALL,
+ offset: start,
+ },
+ })
+ }
/**
* Makes backend call to retrieve starred items
@@ -29,7 +52,7 @@ export class ItemService {
limit: 40,
oldestFirst: false,
search: '',
- showAll: false,
+ showAll: true,
type: ITEM_TYPES.STARRED,
offset: start,
},
@@ -68,8 +91,8 @@ export class ItemService {
limit: 40,
oldestFirst: false,
search: '',
- showAll: false,
- type: ITEM_TYPES.ALL,
+ showAll: true,
+ type: ITEM_TYPES.FEED,
offset: start,
id: feedId,
},
@@ -77,6 +100,27 @@ export class ItemService {
}
/**
+ * Makes backend call to retrieve items from a specific folder
+ *
+ * @param folderId id number of folder to retrieve items for
+ * @param start (id of last unread item loaded)
+ * @return {AxiosResponse} response object containing backend request response
+ */
+ static async fetchFolderItems(folderId: number, start: number): Promise<AxiosResponse> {
+ return await axios.get(API_ROUTES.ITEMS, {
+ params: {
+ limit: 40,
+ oldestFirst: false,
+ search: '',
+ showAll: true,
+ type: ITEM_TYPES.FOLDER,
+ offset: start,
+ id: folderId,
+ },
+ })
+ }
+
+ /**
* Makes backend call to mark item as read/unread in DB
*
* @param {FeedItem} item FeedItem (containing id) that wil be marked as read/unread
diff --git a/src/routes/index.ts b/src/routes/index.ts
index fb0f7742a..b4ed2f757 100644
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -4,12 +4,16 @@ import ExplorePanel from '../components/routes/Explore.vue'
import StarredPanel from '../components/routes/Starred.vue'
import UnreadPanel from '../components/routes/Unread.vue'
import FeedPanel from '../components/routes/Feed.vue'
+import FolderPanel from '../components/routes/Folder.vue'
+import AllPanel from '../components/routes/All.vue'
export const ROUTES = {
EXPLORE: 'explore',
STARRED: 'starred',
UNREAD: 'unread',
FEED: 'feed',
+ FOLDER: 'folder',
+ ALL: 'all',
}
const getInitialRoute = function() {
@@ -49,6 +53,18 @@ const routes = [
component: FeedPanel,
props: true,
},
+ {
+ name: ROUTES.FOLDER,
+ path: '/folder/:folderId',
+ component: FolderPanel,
+ props: true,
+ },
+ {
+ name: ROUTES.ALL,
+ path: '/all',
+ component: AllPanel,
+ props: true,
+ },
]
export default new VueRouter({
diff --git a/src/store/item.ts b/src/store/item.ts
index fe8d5b6c8..4f73bff55 100644
--- a/src/store/item.ts
+++ b/src/store/item.ts
@@ -12,6 +12,8 @@ export const FEED_ITEM_ACTION_TYPES = {
STAR_ITEM: 'STAR_ITEM',
UNSTAR_ITEM: 'UNSTAR_ITEM',
FETCH_FEED_ITEMS: 'FETCH_FEED_ITEMS',
+ FETCH_FOLDER_FEED_ITEMS: 'FETCH_FOLDER_FEED_ITEMS',
+ FETCH_ITEMS: 'FETCH_ITEMS',
}
export type ItemState = {
@@ -49,6 +51,9 @@ const getters = {
selected(state: ItemState) {
return state.allItems.find((item: FeedItem) => item.id === state.selectedId)
},
+ allItems(state: ItemState) {
+ return state.allItems
+ },
}
export const actions = {
@@ -77,6 +82,30 @@ export const actions = {
},
/**
+ * Fetch All Items from Backend and call commit to update state
+ *
+ * @param param0 ActionParams
+ * @param param0.commit
+ * @param param1 ActionArgs
+ * @param param1.start
+ */
+ async [FEED_ITEM_ACTION_TYPES.FETCH_ITEMS]({ commit }: ActionParams, { start }: { start: number } = { start: 0 }) {
+ commit(FEED_ITEM_MUTATION_TYPES.SET_FETCHING, { key: 'all', fetching: true })
+
+ const response = await ItemService.debounceFetchAll(start || state.lastItemLoaded.all)
+
+ commit(FEED_ITEM_MUTATION_TYPES.SET_ITEMS, response?.data.items)
+
+ if (response?.data.items.length < 40) {
+ commit(FEED_ITEM_MUTATION_TYPES.SET_ALL_LOADED, { key: 'all', loaded: true })
+ }
+
+ const lastItem = response?.data.items[response?.data.items.length - 1].id
+ commit(FEED_ITEM_MUTATION_TYPES.SET_LAST_ITEM_LOADED, { key: 'all', lastItem })
+ commit(FEED_ITEM_MUTATION_TYPES.SET_FETCHING, { key: 'all', fetching: false })
+ },
+
+ /**
* Fetch Starred Items from Backend and call commit to update state
*
* @param param0 ActionParams
@@ -124,6 +153,28 @@ export const actions = {
},
/**
+ * Fetch Folder Items from Backend and call commit to update state
+ *
+ * @param param0 ActionParams
+ * @param param0.commit
+ * @param param1 ActionArgs
+ * @param param1.start
+ * @param param1.folderId
+ */
+ async [FEED_ITEM_ACTION_TYPES.FETCH_FOLDER_FEED_ITEMS]({ commit }: ActionParams, { folderId, start }: { folderId: number; start: number }) {
+ commit(FEED_ITEM_MUTATION_TYPES.SET_FETCHING, { key: 'folder-' + folderId, fetching: true })
+ const response = await ItemService.debounceFetchFolderFeedItems(folderId, start || state.lastItemLoaded['folder-' + folderId])
+
+ commit(FEED_ITEM_MUTATION_TYPES.SET_ITEMS, response?.data.items)
+ if (response?.data.items.length < 40) {
+ commit(FEED_ITEM_MUTATION_TYPES.SET_ALL_LOADED, { key: 'folder-' + folderId, loaded: true })
+ }
+ const lastItem = response?.data.items[response?.data.items.length - 1].id
+ commit(FEED_ITEM_MUTATION_TYPES.SET_LAST_ITEM_LOADED, { key: 'folder-' + folderId, lastItem })
+ commit(FEED_ITEM_MUTATION_TYPES.SET_FETCHING, { key: 'folder-' + folderId, fetching: false })
+ },
+
+ /**
* Sends message to Backend to mark as read, and then call commit to update state
*
* @param param0 ActionParams