Skip to content
Open
Show file tree
Hide file tree
Changes from all 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)"
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