Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion components/Notifications/MembershipRequest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
:icon="notification.details.kind === 'invitation' ? RiMailSendLine : RiUserAddLine"
:title="notification.details.kind === 'invitation' ? $t('Invitation à rejoindre une organisation') : $t('Demande d\'adhésion')"
:notification="notification"
:requires-action="true"
:title-link="notification.details.kind === 'invitation' ? '/admin/me/profile' : `/admin/organizations/${notification.details.request_organization.id}/members`"
:title-link-title="$t('Voir la demande')"
>
Expand Down
10 changes: 5 additions & 5 deletions components/Notifications/NotificationLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@
<div
v-else-if="!notification.handled_at"
class="size-2 rounded-full mt-0.5"
:class="requiresAction ? 'bg-danger' : 'bg-new-primary'"
:class="requireAction(notification) ? 'bg-danger' : 'bg-new-primary'"
/>
</div>
<!-- Auto overlay: only when no titleLink and unread -->
<!-- overlay: only when no titleLink and unread -->
<button
v-if="!titleLink && !notification.handled_at && !requiresAction"
v-if="!titleLink && !notification.handled_at && !requireAction(notification)"
Comment thread
nicolaskempf57 marked this conversation as resolved.
Outdated
class="after:absolute after:inset-0 bg-none"
:title="$t('Marquer la notification comme lue')"
@click="handleMarkAsRead"
Expand All @@ -52,21 +52,21 @@ import { AnimatedLoader, useFormatDate } from '@datagouv/components-next'
import type { Component } from 'vue'
import CdataLink from '../CdataLink.vue'
import type { UserNotification } from '~/types/notifications'
import { requireAction } from '~/utils/notifications'

const props = defineProps<{
icon: Component
title: string
notification: UserNotification
titleLink?: string
titleLinkTitle?: string
requiresAction?: boolean
}>()

const { formatDate } = useFormatDate()
const { loading, markAsRead } = useMarkAsRead()

const handleMarkAsRead = () => {
if (!props.requiresAction) {
if (!props.notification.handled_at && !requireAction(props.notification)) {
markAsRead(props.notification)
}
}
Expand Down
3 changes: 1 addition & 2 deletions components/Notifications/NotificationsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,11 @@
</template>

<script setup lang="ts">
import type { DeepReadonly } from 'vue'
import type { DiscussionSubjectTypes } from '~/types/discussions'
import type { DiscussionNotification, MembershipAcceptedNotification, MembershipRefusedNotification, MembershipRequestNotification, NewBadgeNotification, DataserviceCreatedNotification, ReuseCreatedNotification, TransferRequestNotification, UserNotification, ValidateHarvesterNotification } from '~/types/notifications'

const props = defineProps<{
notifications: DeepReadonly<Array<UserNotification>>
notifications: Array<UserNotification>
}>()

const subjects = ref<Record<string, DiscussionSubjectTypes | null>>({})
Expand Down
1 change: 0 additions & 1 deletion components/Notifications/TransferRequest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
:icon="RiSendPlaneLine"
:title="$t('Demande de transfert')"
:notification="notification"
:requires-action="true"
:title-link="link"
:title-link-title="$t('Voir la demande')"
>
Expand Down
1 change: 0 additions & 1 deletion components/Notifications/ValidateHarvester.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
:icon="harvesterIcon"
:title="statusLabel"
:notification="notification"
:requires-action="notification.details.status === 'pending'"
:title-link="`/admin/harvesters/${notification.details.source.id}`"
:title-link-title="$t('Voir le moissonneur')"
>
Expand Down
52 changes: 33 additions & 19 deletions components/SiteHeader/SiteHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -320,23 +320,35 @@
</p>
</div>
</template>
<button
v-if="nextPage"
type="button"
class="w-full bg-datagouv hover:bg-datagouv-dark text-white p-2 flex items-center justify-center"
:disabled="isLoading"
@click="loadMoreNotifications"
<div
v-if="nextPage || notificationsToRead.length > 0"
class="px-2 py-2 space-y-2 border-t border-gray-default"
>
<AnimatedLoader
v-if="isLoading"
class="size-5"
/>
<RiAddLine
v-else
class="size-5"
/>
{{ t('Charger plus de notifications') }}
</button>
<BrandedButton
v-if="nextPage"
type="button"
color="primary"
size="xs"
:icon="RiAddLine"
:loading="isLoading"
class="w-full rounded-full"
@click="loadMoreNotifications"
>
{{ t('Charger plus de notifications') }}
</BrandedButton>
<BrandedButton
v-if="notificationsToRead.length > 0"
type="button"
color="secondary"
size="xs"
:icon="RiCheckLine"
:loading="loading"
class="w-full rounded-full"
@click="() => markWithoutActionAsRead(notificationsCombinedList)"
>
{{ t('Marquer comme lues') }}
</BrandedButton>
</div>
</template>
</Toggletip>
</li>
Expand Down Expand Up @@ -508,12 +520,13 @@
<script setup lang="ts">
import { NuxtImg as _NuxtImg } from '#components'
import type { Component } from 'vue'
import { AnimatedLoader, BrandedButton, Toggletip, useGetUserAvatar, toast } from '@datagouv/components-next'
import { RiAccountCircleLine, RiAddLine, RiDatabase2Line, RiInbox2Line, RiLockLine, RiMenuLine, RiSearchLine, RiTerminalLine, RiLineChartLine, RiServerLine, RiArticleLine, RiSettings3Line, RiLogoutBoxRLine, RiBuilding2Line, RiCloseLine } from '@remixicon/vue'
import { BrandedButton, Toggletip, useGetUserAvatar, toast } from '@datagouv/components-next'
import { RiAccountCircleLine, RiAddLine, RiCheckLine, RiDatabase2Line, RiInbox2Line, RiLockLine, RiMenuLine, RiSearchLine, RiTerminalLine, RiLineChartLine, RiServerLine, RiArticleLine, RiSettings3Line, RiLogoutBoxRLine, RiBuilding2Line, RiCloseLine } from '@remixicon/vue'
import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems, Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
import CdataLink from '../CdataLink.vue'
import LogoAsText from '../LogoAsText.vue'
import LogoImage from '../LogoImage.vue'
import { useMarkAsRead } from '~/composables/useMarkAsRead'
import { useNotifications } from '~/composables/useNotifications.client'
import { useLogout, useMaybeMe } from '~/utils/auth'

Expand All @@ -533,7 +546,8 @@ const currentRoute = useRoute()
const router = useRouter()
const route = useRoute()
const { isLoading } = useLoadingIndicator()
const { refreshNotifications, loadMoreNotifications, pendingNotifications, nextPage, notificationsCombinedList } = useNotifications()
const { refreshNotifications, loadMoreNotifications, pendingNotifications, nextPage, notificationsCombinedList, notificationsToRead } = useNotifications()
const { markWithoutActionAsRead, loading } = useMarkAsRead()

const menu = [
{ label: t('Données'), link: '/datasets' },
Expand Down
25 changes: 25 additions & 0 deletions composables/useMarkAsRead.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { UserNotification } from '~/types/notifications'
import { requireAction } from '~/utils/notifications'

export function useMarkAsRead() {
const loading = ref(false)
Expand All @@ -20,8 +21,32 @@ export function useMarkAsRead() {
}
}

const markWithoutActionAsRead = async (notifications: Array<UserNotification>) => {
const withoutAction = notifications.filter((n) => {
return !n.handled_at && !requireAction(n)
})

if (withoutAction.length === 0) {
return
}

try {
loading.value = true
await Promise.all(
withoutAction.map(notification =>
$api(`/api/1/notifications/${notification.id}/read/`, { method: 'POST' }),
),
)
await refreshNotifications()
}
finally {
loading.value = false
}
}

return {
markAsRead,
markWithoutActionAsRead,
loading,
}
}
9 changes: 6 additions & 3 deletions composables/useNotifications.client.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { UserNotification } from '~/types/notifications'
import type { PaginatedArray } from '~/types/types'
import { requireAction } from '~/utils/notifications'

const page = ref(1)
const PAGE_SIZE = 10
const nextPage = ref<string | null>(null)
const pendingNotifications = ref<PaginatedArray<UserNotification> | null>(null)
const notificationsCombinedList = ref<Array<UserNotification>>([])
const notificationsToRead = computed(() => notificationsCombinedList.value.filter(n => !n.handled_at && !requireAction(n)))

export function useNotifications() {
const { start, finish } = useLoadingIndicator()
Expand Down Expand Up @@ -58,9 +60,10 @@ export function useNotifications() {
return loadNotifications()
}
return {
pendingNotifications: readonly(pendingNotifications),
nextPage: readonly(nextPage),
notificationsCombinedList: readonly(notificationsCombinedList),
pendingNotifications: pendingNotifications,
nextPage: nextPage,
notificationsCombinedList: notificationsCombinedList,
notificationsToRead,
loadNotifications,
loadMoreNotifications,
refreshNotifications,
Expand Down
9 changes: 9 additions & 0 deletions utils/notifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { UserNotification } from '~/types/notifications'

export function requireAction(notification: UserNotification) {
const cls = notification.details.class
if (notification.handled_at) return false
return cls === 'MembershipRequestNotificationDetails'
|| cls === 'TransferRequestNotificationDetails'
|| (cls === 'ValidateHarvesterNotificationDetails' && notification.details.status === 'pending')
}
Loading