kodadot/nft-gallery

View on GitHub
utils/notification.ts

Summary

Maintainability
A
0 mins
Test Coverage
import {
  type NeoMessageIconVariant,
  type NeoMessageVariant,
  NeoNotificationProgrammatic as Notif,
} from '@kodadot1/brick'
import consola from 'consola'
import { h } from 'vue'
import Notification from '@/components/common/Notification.vue'
import MessageNotify from '@/components/MessageNotify.vue'

export type NotificationAction = { label: string, url: string, icon?: string }

export type NotificationFooter = MaybeRef<Omit<NotificationAction, 'url'> | undefined>

type Params = {
  variant: NeoMessageVariant
  duration?: number
}

export const notificationTypes: Record<string, Params> = {
  success: {
    variant: 'success',
  },
  info: {
    variant: 'info',
  },
  danger: {
    variant: 'danger',
    duration: 15000,
  },
  warn: {
    variant: 'warning',
  },
}

export const showNotification = ({
  title,
  message,
  action,
  variant,
  holdTimer,
  icon,
  footer,
  params = notificationTypes.info,
  duration = 10000,
}: {
  title?: MaybeRef<string>
  message?: MaybeRef<string> | null
  variant?: Ref<NeoMessageVariant>
  params?: Params
  duration?: number
  action?: MaybeRef<NotificationAction | undefined>
  holdTimer?: Ref<boolean>
  icon?: Ref<NeoMessageIconVariant | undefined>
  footer?: NotificationFooter
}) => {
  if (params === notificationTypes.danger) {
    consola.error('[Notification Error]', message)
  }

  duration = params.duration || duration

  const componentParams = {
    component: h(Notification, {
      title: title ? toRef(title) : title,
      message: message ? toRef(message!) : message,
      variant: variant ?? params.variant,
      duration: duration,
      action: action,
      holdTimer: holdTimer,
      icon: icon,
      footer: footer,
    }),
    variant: 'component',
    duration: 50000, // child component will trigger close when the real duration is ended
  }

  const notification = Notif.open(componentParams)

  return notification.close as () => void
}

export const showLargeNotification = ({
  message,
  title,
  duration = 10000,
  shareLink = undefined,
}: {
  message: string
  title?: string
  duration?: number
  shareLink?: string
}): void => {
  Notif.open({
    component: h(MessageNotify, {
      title: title,
      subtitle: message,
      noToast: true,
      shareLink,
    }),
    duration,
    variant: 'component',
    closable: true,
  })
}

export const infoMessage = (
  message,
  {
    url,
    duration,
    title = 'Information',
  }: { url?: string, duration?: number, title?: string } = {},
) => {
  const { $i18n } = useNuxtApp()
  showNotification({
    title,
    message,
    params: notificationTypes.info,
    action: url ? { label: $i18n.t('helper.learnMore'), url: '' } : undefined,
    duration,
  })
}

export const successMessage = (message: string, extraConfig?: { footer?: NotificationFooter }) =>
  showNotification({
    title: 'Success',
    message: message,
    params: notificationTypes.success,
    ...extraConfig,
  })

export const getReportIssueAction = (message: string) => {
  const { $i18n } = useNuxtApp()
  return {
    label: $i18n.t('helper.reportIssue'),
    url: getGithubReportUrl(message),
  }
}

export const warningMessage = (message, { reportable = true } = {}) =>
  showNotification({
    title: 'Warning',
    message,
    params: notificationTypes.warn,
    action: reportable ? getReportIssueAction(message) : undefined,
  })
export const dangerMessage = (
  message,
  {
    title = 'Critical Error',
    reportable = true,
  }: { title?: string, reportable?: boolean } = {},
) =>
  showNotification({
    title,
    message,
    params: notificationTypes.danger,
    action: reportable ? getReportIssueAction(message) : undefined,
  })

const ifIsRef = <T>(value: MaybeRef<T | undefined>, otherwise: T): T =>
  Boolean(value) && isRef(value) && unref(value)
    ? (value.value as T)
    : otherwise

const NotificationStateToVariantMap: Record<
  LoadingNotificationState,
  NeoMessageVariant
> = {
  succeeded: 'success',
  loading: 'neutral',
  failed: 'danger',
}

export type LoadingNotificationState = 'loading' | 'succeeded' | 'failed'

export type LoadingMessageParams = {
  title: MaybeRef<string>
  message?: MaybeRef<string | undefined>
  state: Ref<LoadingNotificationState>
  action?: Ref<NotificationAction | undefined>
  showIndexerDelayMessage?: boolean
}

export const loadingMessage = ({
  title,
  message,
  state,
  action,
  showIndexerDelayMessage: showIndexerDelayMesasge = false,
}: LoadingMessageParams) => {
  const { $i18n } = useNuxtApp()
  const stateMessage = ref(unref(message) ?? `${$i18n.t('mint.progress')}...`)

  watch(
    [state],
    ([state]) => {
      if (state === 'succeeded') {
        stateMessage.value = ifIsRef(
          message,
          $i18n.t('transactionLoader.completed'),
        )
      }
      else if (state === 'failed') {
        stateMessage.value = ifIsRef(message, '')
      }
    },
    {
      once: true,
    },
  )

  const isLoadingState = computed(() => state.value === 'loading')

  return showNotification({
    title,
    message: stateMessage,
    variant: computed(() => NotificationStateToVariantMap[state.value]),
    action: action,
    holdTimer: isLoadingState,
    icon: computed(() =>
      isLoadingState.value ? { icon: 'spinner-third', spin: true } : undefined,
    ),
    footer: computed(() =>
      state.value === 'succeeded' && showIndexerDelayMesasge ? { icon: 'circle-info', label: $i18n.t('general.updateOnWebsiteSoon') } : undefined,
    ),
  })
}