Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
</div>

<div class="text-2xs text-gray-600 mt-4 mb-6">
Receive automatically in your inbox a collection of up
to 10 most relevant results from Eagle Eye.
Receive the 10 most relevant results from Eagle Eye
automatically in your inbox.
</div>

<el-button
Expand Down
30 changes: 27 additions & 3 deletions frontend/src/premium/eagle-eye/components/list/eagle-eye-list.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
class="grid grid-cols-3 sm:grid-cols-2 gap-5 pr-8 pb-10 pl-3"
class="grid grid-cols-3 md:grid-cols-2 gap-5 pr-8 pb-10 pl-3"
>
<div
v-for="(_, i) in Array(3)"
Expand All @@ -19,6 +19,14 @@
</div>
</div>

<!-- Bottom of feed message -->
<app-empty-state
v-if="showBottomFeedMessage"
icon="ri-search-eye-line"
description="We couldn't find any more results based on your feed setting."
class="pb-12"
/>

<!-- Load more button -->
<div
v-if="isLoadMoreVisible"
Expand Down Expand Up @@ -52,15 +60,27 @@ const props = defineProps({
})

const store = useStore()
const activeView = computed(
() => store.getters['eagleEye/activeView']
)
const loading = computed(
() => store.state.eagleEye.list.loading
() =>
store.state.eagleEye.views[activeView.value.id].list
.loading
)
const count = computed(
() =>
store.state.eagleEye.views[activeView.value.id].count
)
const count = computed(() => store.state.eagleEye.count)
const pagination = computed(
() => store.getters['eagleEye/pagination']
)

const isLoadMoreVisible = computed(() => {
if (activeView.value.id === 'feed') {
return false
}

return (
pagination.value.currentPage *
pagination.value.pageSize <
Expand All @@ -80,4 +100,8 @@ const onLoadMore = () => {
pagination.value.currentPage + 1
)
}

const showBottomFeedMessage = computed(() => {
return activeView.value.id === 'feed'
})
</script>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
class="grid grid-cols-3 sm:grid-cols-2 gap-5 pr-8 pb-10 pl-3"
class="grid grid-cols-3 md:grid-cols-2 gap-5 pr-8 pb-10 pl-3"
>
<div
v-for="(_, index) in Array(3)"
Expand Down
191 changes: 100 additions & 91 deletions frontend/src/premium/eagle-eye/components/list/eagle-eye-result-card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,33 @@

<!-- Title & description -->
<div class="mt-4 pb-9 border-b border-gray-100">
<h6 v-if="result.post.title" class="black mb-3">
{{ result.post.title }}
</h6>
<div
class="eagle-eye-result-content text-gray-600 text-xs line-clamp-4"
v-if="result.platform === 'twitter'"
class="text-sm text-gray-900 break-words"
>
{{ result.post.description }}
</div>
<div v-else>
<a
v-if="subreddit"
class="text-xs mb-1 font-medium leading-6"
target="_blank"
:href="`https://www.reddit.com/${subreddit}`"
>
{{ subreddit }}
</a>
<h6
v-if="result.post.title"
class="black mb-3 break-words"
>
{{ result.post.title }}
</h6>
<div
class="eagle-eye-result-content text-gray-600 text-xs line-clamp-4 break-words"
>
{{ result.post.description }}
</div>
</div>
</div>

<!-- Actions footer -->
Expand All @@ -51,21 +70,15 @@
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-1">
<el-tooltip placement="top" content="Relevant">
<span
:class="{
'!cursor-auto': isLoading
}"
@click.stop
>
<span @click.stop>
<div
class="h-8 w-8 flex items-center justify-center rounded-full hover:bg-gray-200 group"
:class="{
'bg-green-100 hover:bg-green-100':
isRelevant,
'pointer-events-none opacity-50': isLoading
isRelevant
}"
@click.stop="
onThumbsClick({
onActionClick({
actionType: 'thumbs-up',
shouldAdd: !isRelevant
})
Expand All @@ -85,21 +98,14 @@
</el-tooltip>

<el-tooltip placement="top" content="Not relevant">
<span
:class="{
'!cursor-auto': isLoading
}"
@click.stop
>
<span @click.stop>
<div
class="h-8 w-8 flex items-center justify-center rounded-full hover:bg-gray-200 group"
:class="{
'bg-red-100 hover:bg-red-100':
isNotRelevant,
'pointer-events-none opacity-50': isLoading
'bg-red-100 hover:bg-red-100': isNotRelevant
}"
@click.stop="
onThumbsClick({
onActionClick({
actionType: 'thumbs-down',
shouldAdd: !isNotRelevant
})
Expand All @@ -124,16 +130,15 @@
>
<span
:class="{
'!cursor-auto': isBookmarkDisabled
'!cursor-auto': isBookmarkedByTeam
}"
@click.stop
>
<div
class="h-8 w-8 flex items-center justify-center rounded-full hover:bg-gray-200 group"
:class="{
'bg-blue-100 hover:bg-blue-100': isBookmarked,
'pointer-events-none bg-transparent':
isBookmarkDisabled
'pointer-events-none': isBookmarkedByTeam
}"
@click.stop="
onActionClick({
Expand All @@ -146,11 +151,11 @@
class="text-lg text-gray-400 group-hover:text-gray-900"
:class="{
'ri-bookmark-line text-gray-400':
!isBookmarked && !isBookmarkDisabled,
!isBookmarked && !isBookmarkedByTeam,
'ri-bookmark-fill text-blue-600 group-hover:text-blue-600':
isBookmarked && !isBookmarkDisabled,
isBookmarked && !isBookmarkedByTeam,
'ri-bookmark-fill text-blue-300':
isBookmarkDisabled
isBookmarkedByTeam
}"
/>
</div>
Expand All @@ -166,10 +171,9 @@ import { computed, defineProps } from 'vue'
import platformOptions from '@/premium/eagle-eye/constants/eagle-eye-platforms.json'
import { EagleEyeService } from '../../eagle-eye-service'
import { withHttp } from '@/utils/string'
import {
mapActions,
mapGetters
} from '@/shared/vuex/vuex.helpers'
import { mapGetters } from '@/shared/vuex/vuex.helpers'
import { useStore } from 'vuex'
import moment from 'moment'

const props = defineProps({
result: {
Expand All @@ -182,33 +186,38 @@ const props = defineProps({
}
})

const store = useStore()
const { currentUser } = mapGetters('auth')
const { doAddAction, doRemoveAction } =
mapActions('eagleEye')

const isLoading = computed(() => props.result.loading)
const isBookmarked = computed(() =>
props.result.actions.some((a) => a.type === 'bookmark')
props.result.actions.some(
(a) => a.type === 'bookmark' && !a.toRemove
)
)
const isRelevant = computed(() =>
props.result.actions.some((a) => a.type === 'thumbs-up')
props.result.actions.some(
(a) => a.type === 'thumbs-up' && !a.toRemove
)
)
const isNotRelevant = computed(() =>
props.result.actions.some((a) => a.type === 'thumbs-down')
)
const isBookmarkedByUser = computed(
() =>
props.result.actions.find((a) => a.type === 'bookmark')
?.actionById === currentUser.value.id
props.result.actions.some(
(a) => a.type === 'thumbs-down' && !a.toRemove
)
)

const isBookmarkDisabled = computed(() => {
const isBookmarkedByUser = computed(() => {
const bookmarkAction = props.result.actions.find(
(a) => a.type === 'bookmark'
)
return (
isLoading.value ||
(isBookmarked.value && !isBookmarkedByUser.value)
!bookmarkAction?.actionById ||
bookmarkAction?.actionById === currentUser.value.id
)
})

const isBookmarkedByTeam = computed(() => {
return isBookmarked.value && !isBookmarkedByUser.value
})

const bookmarkTooltip = computed(() => {
if (isBookmarked.value && !isBookmarkedByUser.value) {
return 'Bookmarked by team member'
Expand All @@ -217,6 +226,22 @@ const bookmarkTooltip = computed(() => {
return isBookmarked.value ? 'Unbookmark' : 'Bookmark'
})

const subreddit = computed(() => {
if (props.result.platform !== 'reddit') {
return null
}

const pattern =
/.*reddit\.com(?<subreddit>\/r\/.[^\\/]*).*/gm
const matches = pattern.exec(props.result.url)

if (!matches.groups.subreddit) {
return null
}

return matches.groups.subreddit.slice(1)
})

// Open post in origin url
const onCardClick = async (e) => {
if (!props.result.url || e.target.localName === 'a') {
Expand All @@ -231,51 +256,35 @@ const onCardClick = async (e) => {
})
}

// If opposite thumbs up is set, remove before creating the new action
const onThumbsClick = async ({ actionType, shouldAdd }) => {
if (isLoading.value) {
return
}

if (actionType === 'thumbs-up' && isNotRelevant.value) {
await onActionClick({
actionType: 'thumbs-down',
shouldAdd: false
})
}

if (actionType === 'thumbs-down' && isRelevant.value) {
await onActionClick({
actionType: 'thumbs-up',
shouldAdd: false
})
}

await onActionClick({ actionType, shouldAdd })
}

const onActionClick = async ({ actionType, shouldAdd }) => {
if (isLoading.value) {
return
}
const storeActionType = shouldAdd ? 'add' : 'delete'
const action = shouldAdd
? {
type: actionType,
timestamp: moment()
}
: props.result.actions.find(
(a) => a.type === actionType
)

if (shouldAdd) {
await doAddAction({
post: props.result,
action: actionType,
index: props.index
})
} else {
const actionId = props.result.actions.find(
(a) => a.type === actionType
).id
await doRemoveAction({
postId: props.result.id,
actionId,
actionType,
index: props.index
})
}
store.dispatch('eagleEye/doAddTemporaryPostAction', {
index: props.index,
storeActionType,
action
})

store.dispatch('eagleEye/doAddActionQueue', {
index: props.index,
id: props.result.id,
post: props.result,
handler: async () =>
await store.dispatch('eagleEye/doUpdatePostAction', {
post: props.result,
index: props.index,
storeActionType,
actionType
})
})
}
</script>

Expand Down
Loading