You've already forked AstralRinth
* feat: start on tax compliance * feat: avarala1099 composable * fix: shouldShow should be managed on the page itself * refactor: move show logic to revenue page * feat: security practices rather than info * feat: withdraw page lock * fix: empty modal bug & lint issues * feat: hide behind feature flag * Use standard admonition components, make casing consistent * modal title * lint * feat: withdrawal check * feat: tax cap on withdrawals warning * feat: start on revenue page overhaul * feat: segment generation for bar * feat: tooltips and links * fix: tooltip border * feat: finish initial layout, start on withdraw modal * feat: start on withdrawal limit stage * feat: shade support for primary colors * feat: start on withdraw details stage * fix: convert swatches to hex * feat: payout method/region dropdown temporarily using multiselect * feat: fix modal open issues and use teleport dropdowns * feat: hide transactions section if there are no transactions * refactor: NavStack surfaces * feat: new dropdown component * feat: remove teleport dropdown modal in favour of new combobox component * fix: lint * refactor: dashboard sidebar layout * feat: cleanup * fix: niche bugs * fix: ComboBox styling * feat: first part of qa * feat: animate flash rather than tooltip * fix: lint * feat: qa border gradient * fix: seg hover flashes * feat: i18n * feat: i18n and final QA * fix: lint * feat: QA * fix: lint * fix: merge conflicts * fix: intl * fix: blue hover * fix: transfers page * feat: surface variables & gradients * feat: text vars * fix: lint * fix: intl * feat: stages * fix: lint * feat: region selection * feat: method selection btns * fix: flex col on transactions * feat: hook up method selection to ctx * feat: muralpay kyc stage info * wip: muralpay integration * Basic Mural Pay API bindings * Fix clippy * use dotenvy in muralpay example * Refactor payout creation code * wip: muralpay payout requests * Mural Pay payouts work * Fix clippy * feat: progress * fix: broken tax form stage logic * polish: tax form stage and method selection stage layout * add mural pay fees API * Work on payout fee API * Fees API for more payment methods * Fix CI * polish: muralpay qa * refactor: clean up combobox component * polish: change from critical -> warning admonition in MuralpayDetailsStage * Temporarily disable Venmo and PayPal methods from frontend * polish: clean up transaction component & page * polish: navbar qa, text color-contrast in chips type buttonstyled, mb on rev/index.vue page * fix: incorrectly using available balance as tax form withdraw limit after tax forms submitted * wip: counterparties * Start on counterparties and payment methods API * polish: combobox component * polish: fix broken scroll logic using a composable & web:fix * fix: lint * polish: various QA fixes * feat: hook up with backend (wip) * feat: draft muralpay rails dynamic logic * polish: modify rails to support backend changes * Mural Pay multiple methods when fetching * Don't send supported_countries to frontend * Mural Pay multiple methods when fetching * Don't send supported_countries to frontend * feat: fees & methods endpoint hookup * chore: remove duplicates fix * polish: qa changes + figma match * Add countries to muralpay fiat methods * Compile fix * Add exchange rate info to fees endpoint * Add fees to premium Tremendous options * polish: i18n and better document type dropdown -> id input labels * feat: tremendous * fix: lint & i18n * feat: reintroduce tin mismatch logic to index.vue * polish: qa * fix: i18n * feat: remove teleport dropdown menu - combobox should be used * fix: lint * fix: jsdoc * feat: checkbox for reward program terms * Add delivery email field to Tremendous payouts * Add Tremendous product category to payout methods * Add bank details API to muralpay * Fix CI * Fix CI * polish: qa changes * feat: i18n pass * feat: deduplicate methods endpoint & fix i18n issues * chore: deduplicate i18n strings into common-messages.ts * fix: lint * fix: i18n * feat: estimates * polish: more QA * Remove prepaid visa, compute fees properly for Tremendous methods * Add more details to Tremendous errors * feat: withdraw endpoint impl & internals refactor * Add more details to Tremendous errors * feat: completion stage * Add fees to Mural * feat: transactions page match figma * fix: i18n * polish: QA changes * polish: qa * Payout history route and bank details * polish: autofill and requirements checks * fix: i18n + lint * fix: fiat rail fees * polish: move scroll fade stuff into NewModal rather than just CreatorWithdrawModal * feat: simplify action btn logic & tax form error * fix: tax -> Tax form * Re-add legacy PayPal/Venmo options for US * feat: mobile responsiveness fixes for modal * fix: responsiveness issues * feat: navstack responsiveness * fix: responsiveness * move the mural bank details route * fix: generated state cleanup & bank details input * fix: lint & i18n * Add utoipa support to payout endpoints * address some PR comments * polish: qa * add CORS to new utoipa routes * feat: legacy paypal/venmo stage * polish: reset amount on back qa * revert: navstack mr changes * polish: loading indicator on method selection stage * fix: paypal modal doesnt reopen after auth * fix: lint & i18n * fix: paypal flow * polish: qa changes * fix: gitignore * polish: qa fixes * fix: payouts_available in payouts.rs * fix: bug when limit is zero * polish: qa changes * fix: qa stuff & muralpay sub-division fix * Immediately approve mural payouts * Add currency support to Tremendous payouts * Currency forex * add forex to tremendous fee request * polish: qa & currency support for paypal tremendous * polish: fx qa * feat: demo mode flag * fix: i18n & padding issues * polish: qa changes * fix: ml * Add Mural balance to bank balance info * polish: show warning for paypal international USD withdrawals + more currencies * Add more Tremendous currencies support * fix: colors on balance bars * fix: empty states * fix: pl-8 mobile issue * fix: hide see all * Transaction payouts available use the correct date * Address my own review comment * Address PR comments * Change Mural withdrawal limit to 3k * fix: empty state + paypal warning * maybe fix tremendous gift cards * Change how Mural minimum withdrawals are calculated * Tweak min/max withdrawal values * fix: segment brightness * fix: min & max for muralpay & legacy paypal * Fix some icon issues * more issues * fix user menu * fix: remove + network --------- Signed-off-by: Calum H. <contact@cal.engineer> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com> Co-authored-by: aecsocket <aecsocket@tutanota.com> Co-authored-by: Alejandro González <me@alegon.dev>
621 lines
15 KiB
Vue
621 lines
15 KiB
Vue
<template>
|
|
<div
|
|
class="notification"
|
|
:class="{
|
|
'has-body': hasBody,
|
|
compact: compact,
|
|
read: notification.read,
|
|
}"
|
|
>
|
|
<nuxt-link
|
|
v-if="!type"
|
|
:to="notification.link"
|
|
class="notification__icon backed-svg"
|
|
:class="{ raised: raised }"
|
|
>
|
|
<BellIcon />
|
|
</nuxt-link>
|
|
<DoubleIcon v-else class="notification__icon">
|
|
<template #primary>
|
|
<nuxt-link v-if="project" :to="getProjectLink(project)" tabindex="-1">
|
|
<Avatar size="xs" :src="project.icon_url" :raised="raised" no-shadow />
|
|
</nuxt-link>
|
|
<nuxt-link
|
|
v-else-if="organization"
|
|
:to="`/organization/${organization.slug}`"
|
|
tabindex="-1"
|
|
>
|
|
<Avatar size="xs" :src="organization.icon_url" :raised="raised" no-shadow />
|
|
</nuxt-link>
|
|
<nuxt-link v-else-if="user" :to="getUserLink(user)" tabindex="-1">
|
|
<Avatar size="xs" :src="user.avatar_url" :raised="raised" no-shadow />
|
|
</nuxt-link>
|
|
<Avatar v-else size="xs" :raised="raised" no-shadow />
|
|
</template>
|
|
<template #secondary>
|
|
<ScaleIcon
|
|
v-if="type === 'moderator_message' || type === 'status_change'"
|
|
class="moderation-color"
|
|
/>
|
|
<UserPlusIcon v-else-if="type === 'team_invite' && project" class="creator-color" />
|
|
<UserPlusIcon
|
|
v-else-if="type === 'organization_invite' && organization"
|
|
class="creator-color"
|
|
/>
|
|
<VersionIcon v-else-if="type === 'project_update' && project && version" />
|
|
<BellIcon v-else />
|
|
</template>
|
|
</DoubleIcon>
|
|
<div class="notification__title">
|
|
<template v-if="type === 'project_update' && project && version">
|
|
A project you follow,
|
|
<nuxt-link :to="getProjectLink(project)" class="title-link">{{ project.title }}</nuxt-link>
|
|
, has been updated:
|
|
</template>
|
|
<template v-else-if="type === 'team_invite' && project">
|
|
<nuxt-link
|
|
:to="`/user/${invitedBy.username}`"
|
|
class="iconified-link title-link inline-flex"
|
|
>
|
|
<Avatar
|
|
:src="invitedBy.avatar_url"
|
|
circle
|
|
size="xxs"
|
|
no-shadow
|
|
:raised="raised"
|
|
class="inline-flex"
|
|
/>
|
|
<span class="space"> </span>
|
|
<span>{{ invitedBy.username }}</span>
|
|
</nuxt-link>
|
|
<span>
|
|
has invited you to join
|
|
<nuxt-link :to="getProjectLink(project)" class="title-link">
|
|
{{ project.title }} </nuxt-link
|
|
>.
|
|
</span>
|
|
</template>
|
|
<template v-else-if="type === 'organization_invite' && organization">
|
|
<nuxt-link
|
|
:to="`/user/${invitedBy.username}`"
|
|
class="iconified-link title-link inline-flex"
|
|
>
|
|
<Avatar
|
|
:src="invitedBy.avatar_url"
|
|
circle
|
|
size="xxs"
|
|
no-shadow
|
|
:raised="raised"
|
|
class="inline-flex"
|
|
/>
|
|
<span class="space"> </span>
|
|
<span>{{ invitedBy.username }}</span>
|
|
</nuxt-link>
|
|
<span>
|
|
has invited you to join
|
|
<nuxt-link :to="`/organization/${organization.slug}`" class="title-link">
|
|
{{ organization.name }} </nuxt-link
|
|
>.
|
|
</span>
|
|
</template>
|
|
<template v-else-if="type === 'status_change' && project">
|
|
<nuxt-link :to="getProjectLink(project)" class="title-link">
|
|
{{ project.title }}
|
|
</nuxt-link>
|
|
<template v-if="tags.rejectedStatuses.includes(notification.body.new_status)">
|
|
has been
|
|
<ProjectStatusBadge :status="notification.body.new_status" />
|
|
</template>
|
|
<template v-else>
|
|
updated from
|
|
<ProjectStatusBadge :status="notification.body.old_status" />
|
|
to
|
|
<ProjectStatusBadge :status="notification.body.new_status" />
|
|
</template>
|
|
by the moderators.
|
|
</template>
|
|
<template v-else-if="type === 'moderator_message' && thread && project && !report">
|
|
Your project,
|
|
<nuxt-link :to="getProjectLink(project)" class="title-link">{{ project.title }}</nuxt-link>
|
|
, has received
|
|
<template v-if="notification.grouped_notifs"> messages</template>
|
|
<template v-else>a message</template>
|
|
from the moderators.
|
|
</template>
|
|
<template v-else-if="type === 'moderator_message' && thread && report">
|
|
A moderator replied to your report of
|
|
<template v-if="version">
|
|
version
|
|
<nuxt-link :to="getVersionLink(project, version)" class="title-link">
|
|
{{ version.name }}
|
|
</nuxt-link>
|
|
of project
|
|
</template>
|
|
<nuxt-link v-if="project" :to="getProjectLink(project)" class="title-link">
|
|
{{ project.title }}
|
|
</nuxt-link>
|
|
<nuxt-link v-else-if="user" :to="getUserLink(user)" class="title-link">
|
|
{{ user.username }}
|
|
</nuxt-link>
|
|
.
|
|
</template>
|
|
<nuxt-link v-else :to="notification.link" class="title-link">
|
|
<span v-html="renderString(notification.title)" />
|
|
</nuxt-link>
|
|
<!-- <span v-else class="known-errors">Error reading notification.</span>-->
|
|
</div>
|
|
<div v-if="hasBody" class="notification__body">
|
|
<ThreadSummary
|
|
v-if="type === 'moderator_message' && thread"
|
|
:thread="thread"
|
|
:link="threadLink"
|
|
:raised="raised"
|
|
:messages="getMessages()"
|
|
class="thread-summary"
|
|
:auth="auth"
|
|
/>
|
|
<div v-else-if="type === 'project_update'" class="version-list">
|
|
<div
|
|
v-for="notif in (notification.grouped_notifs
|
|
? [notification, ...notification.grouped_notifs]
|
|
: [notification]
|
|
).filter((x) => x.extra_data.version)"
|
|
:key="notif.id"
|
|
class="version-link"
|
|
>
|
|
<VersionIcon />
|
|
<nuxt-link
|
|
:to="getVersionLink(notif.extra_data.project, notif.extra_data.version)"
|
|
class="text-link"
|
|
>
|
|
{{ notif.extra_data.version.name }}
|
|
</nuxt-link>
|
|
<span class="version-info">
|
|
for
|
|
<Categories
|
|
:categories="getLoaderCategories(notif.extra_data.version)"
|
|
:type="notif.extra_data.project.project_type"
|
|
class="categories"
|
|
/>
|
|
{{ $formatVersion(notif.extra_data.version.game_versions) }}
|
|
<span
|
|
v-tooltip="
|
|
$dayjs(notif.extra_data.version.date_published).format('MMMM D, YYYY [at] h:mm A')
|
|
"
|
|
class="date"
|
|
>
|
|
{{ formatRelativeTime(notif.extra_data.version.date_published) }}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<template v-else>
|
|
{{ notification.text }}
|
|
</template>
|
|
</div>
|
|
<span class="notification__date">
|
|
<span v-if="notification.read" class="read-badge inline-flex">
|
|
<CheckCircleIcon /> Read
|
|
</span>
|
|
<span
|
|
v-tooltip="$dayjs(notification.created).format('MMMM D, YYYY [at] h:mm A')"
|
|
class="inline-flex"
|
|
>
|
|
<CalendarIcon class="mr-1" /> Received
|
|
{{ formatRelativeTime(notification.created) }}
|
|
</span>
|
|
</span>
|
|
<div v-if="compact" class="notification__actions">
|
|
<template v-if="type === 'team_invite' || type === 'organization_invite'">
|
|
<button
|
|
v-tooltip="`Accept`"
|
|
class="iconified-button square-button brand-button button-transparent"
|
|
@click="
|
|
() => {
|
|
acceptTeamInvite(notification.body.team_id)
|
|
read()
|
|
}
|
|
"
|
|
>
|
|
<CheckIcon />
|
|
</button>
|
|
<button
|
|
v-tooltip="`Decline`"
|
|
class="iconified-button square-button danger-button button-transparent"
|
|
@click="
|
|
() => {
|
|
removeSelfFromTeam(notification.body.team_id)
|
|
read()
|
|
}
|
|
"
|
|
>
|
|
<XIcon />
|
|
</button>
|
|
</template>
|
|
<button
|
|
v-else-if="!notification.read"
|
|
v-tooltip="`Mark as read`"
|
|
class="iconified-button square-button button-transparent"
|
|
@click="read()"
|
|
>
|
|
<XIcon />
|
|
</button>
|
|
</div>
|
|
<div v-else class="notification__actions">
|
|
<div v-if="type !== null" class="input-group">
|
|
<template
|
|
v-if="(type === 'team_invite' || type === 'organization_invite') && !notification.read"
|
|
>
|
|
<button
|
|
class="iconified-button brand-button"
|
|
@click="
|
|
() => {
|
|
acceptTeamInvite(notification.body.team_id)
|
|
read()
|
|
}
|
|
"
|
|
>
|
|
<CheckIcon />
|
|
Accept
|
|
</button>
|
|
<button
|
|
class="iconified-button danger-button"
|
|
@click="
|
|
() => {
|
|
removeSelfFromTeam(notification.body.team_id)
|
|
read()
|
|
}
|
|
"
|
|
>
|
|
<XIcon />
|
|
Decline
|
|
</button>
|
|
</template>
|
|
<button
|
|
v-else-if="!notification.read"
|
|
class="iconified-button"
|
|
:class="{ 'raised-button': raised }"
|
|
@click="read()"
|
|
>
|
|
<CheckIcon />
|
|
Mark as read
|
|
</button>
|
|
<CopyCode v-if="flags.developerMode" :text="notification.id" />
|
|
</div>
|
|
<div v-else class="input-group">
|
|
<nuxt-link
|
|
v-if="notification.link && notification.link !== '#'"
|
|
class="iconified-button"
|
|
:class="{ 'raised-button': raised }"
|
|
:to="notification.link"
|
|
target="_blank"
|
|
>
|
|
<ExternalIcon />
|
|
Open link
|
|
</nuxt-link>
|
|
<button
|
|
v-for="(action, actionIndex) in notification.actions"
|
|
:key="actionIndex"
|
|
class="iconified-button"
|
|
:class="{ 'raised-button': raised }"
|
|
@click="performAction(notification, actionIndex)"
|
|
>
|
|
<CheckIcon v-if="action.title === 'Accept'" />
|
|
<XIcon v-else-if="action.title === 'Deny'" />
|
|
{{ action.title }}
|
|
</button>
|
|
<button
|
|
v-if="notification.actions.length === 0 && !notification.read"
|
|
class="iconified-button"
|
|
:class="{ 'raised-button': raised }"
|
|
@click="performAction(notification, null)"
|
|
>
|
|
<CheckIcon />
|
|
Mark as read
|
|
</button>
|
|
<CopyCode v-if="flags.developerMode" :text="notification.id" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {
|
|
BellIcon,
|
|
CalendarIcon,
|
|
CheckCircleIcon,
|
|
CheckIcon,
|
|
ExternalIcon,
|
|
ScaleIcon,
|
|
UserPlusIcon,
|
|
VersionIcon,
|
|
XIcon,
|
|
} from '@modrinth/assets'
|
|
import {
|
|
Avatar,
|
|
Categories,
|
|
CopyCode,
|
|
DoubleIcon,
|
|
injectNotificationManager,
|
|
ProjectStatusBadge,
|
|
useRelativeTime,
|
|
} from '@modrinth/ui'
|
|
import { getUserLink, renderString } from '@modrinth/utils'
|
|
|
|
import { markAsRead } from '~/helpers/platform-notifications'
|
|
import { getProjectLink, getVersionLink } from '~/helpers/projects'
|
|
import { acceptTeamInvite, removeSelfFromTeam } from '~/helpers/teams'
|
|
|
|
import ThreadSummary from './thread/ThreadSummary.vue'
|
|
|
|
const { addNotification } = injectNotificationManager()
|
|
const emit = defineEmits(['update:notifications'])
|
|
const formatRelativeTime = useRelativeTime()
|
|
|
|
const props = defineProps({
|
|
notification: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
notifications: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
raised: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
compact: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
auth: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
})
|
|
|
|
const flags = useFeatureFlags()
|
|
const tags = useGeneratedState()
|
|
|
|
const type = computed(() =>
|
|
!props.notification.body || props.notification.body.type === 'legacy_markdown'
|
|
? null
|
|
: props.notification.body.type,
|
|
)
|
|
const thread = computed(() => props.notification.extra_data.thread)
|
|
const report = computed(() => props.notification.extra_data.report)
|
|
const project = computed(() => props.notification.extra_data.project)
|
|
const version = computed(() => props.notification.extra_data.version)
|
|
const user = computed(() => props.notification.extra_data.user)
|
|
const organization = computed(() => props.notification.extra_data.organization)
|
|
const invitedBy = computed(() => props.notification.extra_data.invited_by)
|
|
|
|
const threadLink = computed(() => {
|
|
if (report.value) {
|
|
return `/dashboard/report/${report.value.id}`
|
|
} else if (project.value) {
|
|
return `${getProjectLink(project.value)}/moderation#messages`
|
|
}
|
|
return '#'
|
|
})
|
|
|
|
const hasBody = computed(() => !type.value || thread.value || type.value === 'project_update')
|
|
|
|
async function read() {
|
|
try {
|
|
const ids = [
|
|
props.notification.id,
|
|
...(props.notification.grouped_notifs
|
|
? props.notification.grouped_notifs.map((notif) => notif.id)
|
|
: []),
|
|
]
|
|
const updateNotifs = await markAsRead(ids)
|
|
const newNotifs = updateNotifs(props.notifications)
|
|
emit('update:notifications', newNotifs)
|
|
} catch (err) {
|
|
addNotification({
|
|
title: 'Error marking notification as read',
|
|
text: err.data ? err.data.description : err,
|
|
type: 'error',
|
|
})
|
|
}
|
|
}
|
|
|
|
async function performAction(notification, actionIndex) {
|
|
startLoading()
|
|
try {
|
|
await read()
|
|
|
|
if (actionIndex !== null) {
|
|
await useBaseFetch(`${notification.actions[actionIndex].action_route[1]}`, {
|
|
method: notification.actions[actionIndex].action_route[0].toUpperCase(),
|
|
})
|
|
}
|
|
} catch (err) {
|
|
addNotification({
|
|
title: 'An error occurred',
|
|
text: err.data ? err.data.description : err,
|
|
type: 'error',
|
|
})
|
|
}
|
|
stopLoading()
|
|
}
|
|
|
|
function getMessages() {
|
|
const messages = []
|
|
if (props.notification.body.message_id) {
|
|
messages.push(props.notification.body.message_id)
|
|
}
|
|
if (props.notification.grouped_notifs) {
|
|
for (const notif of props.notification.grouped_notifs) {
|
|
if (notif.body.message_id) {
|
|
messages.push(notif.body.message_id)
|
|
}
|
|
}
|
|
}
|
|
return messages
|
|
}
|
|
|
|
function getLoaderCategories(ver) {
|
|
return tags.value.loaders.filter((loader) => {
|
|
return ver?.loaders?.includes(loader.name)
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.notification {
|
|
display: grid;
|
|
grid-template:
|
|
'icon title'
|
|
'actions actions'
|
|
'date date';
|
|
grid-template-columns: min-content 1fr;
|
|
grid-template-rows: min-content min-content min-content;
|
|
gap: var(--spacing-card-sm);
|
|
|
|
&.compact {
|
|
grid-template:
|
|
'icon title actions'
|
|
'date date date';
|
|
grid-template-columns: min-content 1fr auto;
|
|
grid-template-rows: auto min-content;
|
|
}
|
|
|
|
&.has-body {
|
|
grid-template:
|
|
'icon title'
|
|
'body body'
|
|
'actions actions'
|
|
'date date';
|
|
grid-template-columns: min-content 1fr;
|
|
grid-template-rows: min-content auto auto min-content;
|
|
|
|
&.compact {
|
|
grid-template:
|
|
'icon title actions'
|
|
'body body body'
|
|
'date date date';
|
|
grid-template-columns: min-content 1fr auto;
|
|
grid-template-rows: min-content auto min-content;
|
|
}
|
|
}
|
|
|
|
.label__title,
|
|
.label__description,
|
|
h1,
|
|
h2,
|
|
h3,
|
|
h4,
|
|
:deep(p) {
|
|
margin: 0 !important;
|
|
}
|
|
|
|
.notification__icon {
|
|
grid-area: icon;
|
|
}
|
|
|
|
.notification__title {
|
|
grid-area: title;
|
|
color: var(--color-heading);
|
|
margin-block: auto;
|
|
display: inline-block;
|
|
vertical-align: middle;
|
|
line-height: 1.25rem;
|
|
|
|
.iconified-link {
|
|
display: inline;
|
|
|
|
img {
|
|
vertical-align: middle;
|
|
position: relative;
|
|
top: -2px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.notification__body {
|
|
grid-area: body;
|
|
|
|
.version-list {
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style-type: none;
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex-wrap: wrap;
|
|
gap: var(--spacing-card-sm);
|
|
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
|
|
|
.version-link {
|
|
display: flex;
|
|
flex-direction: row;
|
|
gap: var(--spacing-card-xs);
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
|
|
.version-info {
|
|
display: contents;
|
|
|
|
:deep(span) {
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.date {
|
|
color: var(--color-text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.notification__date {
|
|
grid-area: date;
|
|
color: var(--color-text-secondary);
|
|
|
|
svg {
|
|
vertical-align: top;
|
|
}
|
|
|
|
.read-badge {
|
|
font-weight: bold;
|
|
color: var(--color-text);
|
|
margin-right: var(--spacing-card-xs);
|
|
}
|
|
}
|
|
|
|
.notification__actions {
|
|
grid-area: actions;
|
|
display: flex;
|
|
flex-direction: row;
|
|
gap: var(--spacing-card-sm);
|
|
}
|
|
|
|
.notification__actions .iconified-button.square-button svg {
|
|
margin-right: 0;
|
|
}
|
|
|
|
.unknown-type {
|
|
color: var(--color-red);
|
|
}
|
|
|
|
.title-link {
|
|
&:not(:hover) {
|
|
text-decoration: none;
|
|
}
|
|
|
|
font-weight: bold;
|
|
}
|
|
|
|
.moderation-color {
|
|
color: var(--color-orange);
|
|
}
|
|
|
|
.creator-color {
|
|
color: var(--color-blue);
|
|
}
|
|
}
|
|
</style>
|