-
- Platforms
+
+
+
+ Published date
+
+
+ {{ publishedDate }}
+
-
-
-
![]()
-
{{
- platformOptions[platform].label
- }}
+
+
+
+
+ Platforms
+
+
+
+
![]()
+
{{
+ platformOptions[platform].label
+ }}
+
-
-
-
Feed settings
-
-
-
-
+
+
Feed settings
+
+
+
+
+
@@ -83,37 +113,40 @@ const { currentUser } = mapGetters('auth')
const settingsDrawerOpen = ref(false)
-const keywords = computed(() => {
- const { eagleEyeSettings } = currentUser.value
-
- if (!eagleEyeSettings?.feed) {
- return []
- }
-
- return eagleEyeSettings.feed.keywords
+const eagleEyeFeedSettings = computed(() => {
+ return currentUser.value.eagleEyeSettings?.feed
})
-const exactKeywords = computed(() => {
- const { eagleEyeSettings } = currentUser.value
+const keywords = computed(
+ () => eagleEyeFeedSettings.value.keywords
+)
- if (!eagleEyeSettings?.feed) {
- return []
- }
+const exactKeywords = computed(
+ () => eagleEyeFeedSettings.value.exactKeywords
+)
- return eagleEyeSettings.feed.exactKeywords
-})
-const platforms = computed(() => {
- const { eagleEyeSettings } = currentUser.value
+const excludedKeywords = computed(
+ () => eagleEyeFeedSettings.value.excludedKeywords
+)
- if (!eagleEyeSettings?.feed) {
- return []
- }
+const platforms = computed(
+ () => eagleEyeFeedSettings.value.platforms
+)
- return eagleEyeSettings.feed.platforms
-})
+const publishedDate = computed(
+ () => eagleEyeFeedSettings.value.publishedDate
+)
diff --git a/frontend/src/premium/eagle-eye/eagle-eye-service.js b/frontend/src/premium/eagle-eye/eagle-eye-service.js
index bbc15f1f7a..bbc7620013 100644
--- a/frontend/src/premium/eagle-eye/eagle-eye-service.js
+++ b/frontend/src/premium/eagle-eye/eagle-eye-service.js
@@ -53,12 +53,12 @@ export class EagleEyeService {
return response.data
}
- static async addAction({ postId, actionData }) {
+ static async addAction({ postId, action }) {
const tenantId = AuthCurrentTenant.get()
const response = await authAxios.post(
`/tenant/${tenantId}/eagleEyeContent/${postId}/action`,
- actionData
+ action
)
return response.data
diff --git a/frontend/src/premium/eagle-eye/pages/eagle-eye-page.vue b/frontend/src/premium/eagle-eye/pages/eagle-eye-page.vue
index 218099dc21..26243af5ae 100644
--- a/frontend/src/premium/eagle-eye/pages/eagle-eye-page.vue
+++ b/frontend/src/premium/eagle-eye/pages/eagle-eye-page.vue
@@ -2,27 +2,30 @@
@@ -49,6 +52,15 @@ const store = useStore()
const { activeView, activeViewList } =
mapGetters('eagleEye')
+const cssVars = computed(() => {
+ const isMenuCollapsed =
+ store.getters['layout/menuCollapsed']
+ const menuWidth = isMenuCollapsed ? '64px' : '260px'
+
+ return {
+ '--eagle-eye-padding': menuWidth
+ }
+})
const list = computed(() => activeViewList.value.posts)
const isLoading = computed(() => {
if (activeView.value.id === 'feed') {
@@ -86,3 +98,19 @@ onMounted(async () => {
}
})
+
+
diff --git a/frontend/src/premium/eagle-eye/store/actions.js b/frontend/src/premium/eagle-eye/store/actions.js
index 4aabde6c87..761500db41 100644
--- a/frontend/src/premium/eagle-eye/store/actions.js
+++ b/frontend/src/premium/eagle-eye/store/actions.js
@@ -8,6 +8,7 @@ import {
shouldFetchNewResults,
isStorageUpdating
} from '@/premium/eagle-eye/eagle-eye-storage'
+import Message from '@/shared/message/message'
export default {
...sharedActions('eagleEye'),
@@ -129,105 +130,232 @@ export default {
}
},
+ // Add temporary actions to post so that UI updates immeadiately
+ async doAddTemporaryPostAction(
+ { commit, getters },
+ { index, storeActionType, action }
+ ) {
+ const activeView = getters.activeView.id
+
+ // Add new action
+ if (storeActionType === 'add') {
+ commit('CREATE_TEMPORARY_ACTION', {
+ action,
+ activeView,
+ index
+ })
+ // Remove action
+ } else {
+ commit('REMOVE_TEMPORARY_ACTION', {
+ action,
+ activeView,
+ index
+ })
+ }
+ },
+
+ // Add or remove actions from the database depending on the action type
+ async doUpdatePostAction(
+ { state, dispatch, getters },
+ { post, index, storeActionType, actionType }
+ ) {
+ const activeView = getters.activeView.id
+ const action = state.views[activeView].list.posts[
+ index
+ ].actions.find((a) => a.type === actionType) || {
+ type: actionType,
+ timestamp: moment()
+ }
+ // Add new action
+ if (storeActionType === 'add') {
+ await dispatch('doAddAction', {
+ post,
+ action,
+ index
+ })
+ // Remove action
+ } else {
+ await dispatch('doRemoveAction', {
+ postId: post.id,
+ action,
+ index
+ })
+ }
+ },
+
async doAddAction(
{ state, commit, getters, rootGetters },
{ post, action, index }
) {
const activeView = getters.activeView.id
+ const oppositeActionTypes = {
+ ['thumbs-up']: 'thumbs-down',
+ ['thumbs-down']: 'thumbs-up'
+ }
+ const oppositeAction = state.views[
+ activeView
+ ].list.posts[index].actions.find(
+ (a) => a.type === oppositeActionTypes[action.type]
+ )
- try {
- commit('UPDATE_ACTION_LOADING', {
- index,
- activeView
+ // If action is thumbs, delete opposite thumbs from post
+ if (
+ oppositeActionTypes[action.type] &&
+ oppositeAction
+ ) {
+ commit('REMOVE_TEMPORARY_ACTION', {
+ action: oppositeAction,
+ activeView,
+ index
})
- // Create content in database
- const postResponse =
- await EagleEyeService.createContent({
- post
- })
-
- commit('CREATE_CONTENT_SUCCESS', {
- post: postResponse,
- index,
- activeView
+ await EagleEyeService.deleteAction({
+ postId: post.id,
+ actionId: oppositeAction.id
})
+ }
- // Add action to db content
- const actionData = {
- type: action,
- timestamp: moment().utc().format('YYYY-MM-DD')
+ const postDb = await EagleEyeService.createContent({
+ post: {
+ actions: [],
+ platform: post.platform,
+ post: post.post,
+ postedAt: post.postedAt,
+ url: post.url
}
+ })
- const actionResponse =
- await EagleEyeService.addAction({
- postId: postResponse.id,
- actionData
- })
+ const actionDb = await EagleEyeService.addAction({
+ postId: postDb.id,
+ action
+ })
- commit('CREATE_ACTION_SUCCESS', {
- action: actionResponse,
- index,
- activeView
- })
+ commit('CREATE_ACTION_SUCCESS', {
+ post: postDb,
+ action: actionDb,
+ index,
+ activeView
+ })
- // Update local storage with updated action
- const currentUser = rootGetters['auth/currentUser']
- const currentTenant =
- rootGetters['auth/currentTenant']
+ // Update posts with new actions in local storage
+ const currentUser = rootGetters['auth/currentUser']
+ const currentTenant = rootGetters['auth/currentTenant']
- setResultsInStorage({
- posts: state.list.posts,
- tenantId: currentTenant.id,
- userId: currentUser.id
- })
- } catch (error) {
- commit('UPDATE_ACTION_ERROR', {
- index,
- activeView
- })
- }
+ setResultsInStorage({
+ posts: state.views['feed'].list.posts,
+ storageDate: moment(),
+ tenantId: currentTenant.id,
+ userId: currentUser.id
+ })
},
async doRemoveAction(
{ state, commit, getters, rootGetters },
- { postId, actionId, actionType, index }
+ { postId, action, index }
) {
const activeView = getters.activeView.id
+ const actionId = action.id
+ const deleteImmeadiately =
+ activeView === 'bookmarked' &&
+ action.type === 'bookmark'
- try {
- commit('UPDATE_ACTION_LOADING', {
+ if (deleteImmeadiately) {
+ commit('REMOVE_ACTION_SUCCESS', {
+ postId,
+ action,
index,
activeView
})
+ }
- await EagleEyeService.deleteAction({
- postId,
- actionId
- })
+ await EagleEyeService.deleteAction({
+ postId,
+ actionId
+ })
- commit('DELETE_ACTION_SUCCESS', {
- actionId,
- actionType,
+ if (!deleteImmeadiately) {
+ commit('REMOVE_ACTION_SUCCESS', {
+ postId,
+ action,
index,
activeView
})
+ }
- // Update local storage with updated action
- const currentUser = rootGetters['auth/currentUser']
- const currentTenant =
- rootGetters['auth/currentTenant']
+ // Update posts with new actions in local storage
+ const currentUser = rootGetters['auth/currentUser']
+ const currentTenant = rootGetters['auth/currentTenant']
- setResultsInStorage({
- posts: state.list.posts,
- tenantId: currentTenant.id,
- userId: currentUser.id
- })
- } catch (error) {
- commit('UPDATE_ACTION_ERROR', { index, activeView })
+ setResultsInStorage({
+ posts: state.views['feed'].list.posts,
+ storageDate: moment(),
+ tenantId: currentTenant.id,
+ userId: currentUser.id
+ })
+ },
+
+ doAddActionQueue({ commit, state, dispatch }, job) {
+ commit('ADD_PENDING_ACTION', job)
+
+ // If there are no actions active, start the next one in the queue
+ if (Object.keys(state.activeAction).length == 0) {
+ dispatch('doStartActionQueue')
}
},
+ async doStartActionQueue({
+ commit,
+ dispatch,
+ state,
+ getters,
+ rootGetters
+ }) {
+ if (state.pendingActions.length > 0) {
+ commit('SET_ACTIVE_ACTION', state.pendingActions[0])
+
+ const pendingAction = { ...state.pendingActions[0] }
+
+ commit('POP_CURRENT_ACTION')
+
+ try {
+ await pendingAction.handler()
+ await dispatch('doStartActionQueue')
+ } catch (error) {
+ // In case of an error, create post again and update it in UI
+ EagleEyeService.createContent({
+ post: pendingAction.post
+ }).then((response) => {
+ const activeView = getters.activeView.id
+ const currentUser =
+ rootGetters['auth/currentUser']
+ const currentTenant =
+ rootGetters['auth/currentTenant']
+
+ commit('UPDATE_POST', {
+ activeView,
+ index: pendingAction.index,
+ post: response
+ })
+
+ // Update posts with new actions in local storage
+ setResultsInStorage({
+ posts: state.views['feed'].list.posts,
+ storageDate: moment(),
+ tenantId: currentTenant.id,
+ userId: currentUser.id
+ })
+ })
+
+ Message.error(
+ 'Something went wrong. Please try again'
+ )
+ commit('SET_ACTIVE_ACTION', {})
+ }
+ }
+
+ commit('SET_ACTIVE_ACTION', {})
+ },
+
async doUpdateSettings({ commit, dispatch }, data) {
commit('UPDATE_EAGLE_EYE_SETTINGS_STARTED')
return EagleEyeService.updateSettings(data)
diff --git a/frontend/src/premium/eagle-eye/store/getters.js b/frontend/src/premium/eagle-eye/store/getters.js
index 2ebedde6fe..b52596f2df 100644
--- a/frontend/src/premium/eagle-eye/store/getters.js
+++ b/frontend/src/premium/eagle-eye/store/getters.js
@@ -1,4 +1,5 @@
import sharedGetters from '@/shared/store/getters'
+import { INITIAL_PAGE_SIZE } from './constants'
export default {
...sharedGetters(),
@@ -7,5 +8,35 @@ export default {
const activeView = sharedGetters().activeView(state)
return state.views[activeView.id].list
+ },
+
+ pagination: (state, getters) => {
+ return {
+ ...getters.activeView.pagination,
+ total: getters.activeView.count,
+ showSizeChanger: true
+ }
+ },
+
+ limit: (state, getters) => {
+ const { pagination } = getters.activeView
+
+ if (!pagination?.pageSize) {
+ return INITIAL_PAGE_SIZE
+ }
+
+ return pagination.pageSize
+ },
+
+ offset: (state, getters) => {
+ const { pagination } = getters.activeView
+
+ if (!pagination?.pageSize) {
+ return 0
+ }
+
+ const { currentPage = 1 } = pagination
+
+ return (currentPage - 1) * pagination.pageSize
}
}
diff --git a/frontend/src/premium/eagle-eye/store/mutations.js b/frontend/src/premium/eagle-eye/store/mutations.js
index 2a2552f21f..a1d399bb2a 100644
--- a/frontend/src/premium/eagle-eye/store/mutations.js
+++ b/frontend/src/premium/eagle-eye/store/mutations.js
@@ -24,11 +24,11 @@ export default {
) {
state.views[activeView].list.loading = false
if (appendToList) {
- state.views[activeView].list.posts.concat(list)
+ state.views[activeView].list.posts.push(...list)
} else {
state.views[activeView].list.posts = list
}
- state.count = count
+ state.views[activeView].count = count
},
FETCH_ERROR(state, { activeView }) {
@@ -37,64 +37,128 @@ export default {
state.views[activeView].count = 0
},
- UPDATE_ACTION_LOADING(state, { index, activeView }) {
- state.views[activeView].list.posts[index].loading = true
- },
-
- CREATE_CONTENT_SUCCESS(
+ CREATE_ACTION_SUCCESS(
state,
- { post, index, activeView }
+ { post, action, index, activeView }
) {
- state.views[activeView].list.posts[index] = {
- ...state.views[activeView].list.posts[index],
- ...post
+ // Update feed post if bookmark action is updated
+ if (activeView === 'bookmarked') {
+ const feedPost = state.views['feed'].list.posts.find(
+ (p) => p.url === post.url
+ )
+
+ if (feedPost) {
+ const indexAction = feedPost.actions.findIndex(
+ (a) => a.type === action.type
+ )
+
+ if (indexAction === -1) {
+ feedPost.actions.push(action)
+ } else {
+ feedPost.actions[indexAction] = {
+ ...action,
+ id: action.id
+ }
+ }
+ }
}
- },
- CREATE_ACTION_SUCCESS(
- state,
- { action, index, activeView }
- ) {
- state.views[activeView].list.posts[
- index
- ].loading = false
- state.views[activeView].list.posts[index].actions.push(
- action
+ const { actions } =
+ state.views[activeView].list.posts[index]
+ const indexAction = actions.findIndex(
+ (a) => a.type === action.type
)
+
+ // Update store post with new one, except for actions
+ state.views[activeView].list.posts[index] = {
+ ...post,
+ actions
+ }
+
+ if (indexAction === -1) {
+ actions.push(action)
+ } else {
+ actions[indexAction] = {
+ ...action,
+ id: action.id
+ }
+ }
},
- DELETE_ACTION_SUCCESS(
+ REMOVE_ACTION_SUCCESS(
state,
- { actionId, actionType, index, activeView }
+ { postId, action, index, activeView }
) {
- state.views[activeView].list.posts[
- index
- ].loading = false
+ // Update feed post if bookmark action is updated
+ if (activeView === 'bookmarked') {
+ const feedPost = state.views['feed'].list.posts.find(
+ (p) => p.id === postId
+ )
+
+ if (feedPost) {
+ const deleteIndex = feedPost.actions.findIndex(
+ (a) => a.type === action.type
+ )
+
+ if (deleteIndex !== -1) {
+ feedPost.actions.splice(deleteIndex, 1)
+ console.log(feedPost.actions)
+ }
+ }
+ }
// Remove post from bookmarks view
if (
- actionType === 'bookmark' &&
+ action.type === 'bookmark' &&
activeView === 'bookmarked'
) {
state.views[activeView].list.posts.splice(index, 1)
- // Remove action from post
} else {
- const deleteIndex = state.views[
- activeView
- ].list.posts[index].actions.findIndex(
- (a) => a.id === actionId
+ // Remove action from post
+ const { actions } =
+ state.views[activeView].list.posts[index]
+ const deleteIndex = actions.findIndex(
+ (a) => a.type === action.type
)
- state.views[activeView].list.posts[
- index
- ].actions.splice(deleteIndex, 1)
+ if (deleteIndex !== -1) {
+ actions.splice(deleteIndex, 1)
+ }
}
},
- UPDATE_ACTION_ERROR(state, { index, activeView }) {
- state.views[activeView].list.posts[
- index
- ].loading = false
+ CREATE_TEMPORARY_ACTION(
+ state,
+ { action, activeView, index }
+ ) {
+ const { actions } =
+ state.views[activeView].list.posts[index]
+ const indexAction = actions.findIndex(
+ (a) => a.type === action.type
+ )
+
+ if (indexAction === -1) {
+ actions.push(action)
+ } else {
+ actions[indexAction] = action
+ }
+ },
+
+ REMOVE_TEMPORARY_ACTION(
+ state,
+ { action, activeView, index }
+ ) {
+ const { actions } =
+ state.views[activeView].list.posts[index]
+ const deleteIndex = actions.findIndex(
+ (a) => a.type === action.type
+ )
+
+ actions[deleteIndex].toRemove = true
+ },
+
+ UPDATE_POST(state, { activeView, index, post }) {
+ state.views[activeView].list.posts[index] = post
},
SORTER_CHANGED(state, payload) {
@@ -112,5 +176,17 @@ export default {
UPDATE_EAGLE_EYE_SETTINGS_ERROR(state) {
state.loadingUpdateSettings = false
+ },
+
+ ADD_PENDING_ACTION(state, job) {
+ state.pendingActions.push(job)
+ },
+
+ SET_ACTIVE_ACTION(state, job) {
+ state.activeAction = job
+ },
+
+ POP_CURRENT_ACTION(state) {
+ state.pendingActions.shift()
}
}
diff --git a/frontend/src/premium/eagle-eye/store/state.js b/frontend/src/premium/eagle-eye/store/state.js
index 8a37deec75..8e92f374b2 100644
--- a/frontend/src/premium/eagle-eye/store/state.js
+++ b/frontend/src/premium/eagle-eye/store/state.js
@@ -30,6 +30,8 @@ export default () => {
active: false
}
},
+ pendingActions: [],
+ activeAction: {},
loadingUpdateSettings: false
}
}