feat(app): minor improvements
Build and Push Docker image / build-and-push (push) Successful in 5m0s
Details
Build and Push Docker image / build-and-push (push) Successful in 5m0s
Details
added sender and sendTime to announcements added sendTime as duration to message improved duration function (no more 59Min instead of 1Std)
This commit is contained in:
parent
ed2056fded
commit
841be395f9
|
@ -57,11 +57,15 @@ function getLocalizedNowWord(locale: string) {
|
||||||
return translations[locale as keyof typeof translations] || 'now';
|
return translations[locale as keyof typeof translations] || 'now';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const humanDeltaFromNow = (start: string | Date | Dayjs, end: string | Date | Dayjs): {
|
export const humanDeltaFromNow = (start: string | Date | Dayjs, end?: string | Date | Dayjs): {
|
||||||
message: string,
|
message: string,
|
||||||
delta: "PAST" | "FUTURE" | "NOW"
|
delta: "PAST" | "FUTURE" | "NOW"
|
||||||
} => {
|
} => {
|
||||||
|
|
||||||
|
if (!end) {
|
||||||
|
end = start
|
||||||
|
}
|
||||||
|
|
||||||
// check if end is in the past
|
// check if end is in the past
|
||||||
if (dayjs(end).isBefore(dayjs())) {
|
if (dayjs(end).isBefore(dayjs())) {
|
||||||
return {
|
return {
|
||||||
|
@ -112,29 +116,27 @@ export const pprintDateRange = (d1: string | Date | Dayjs, d2: string | Date | D
|
||||||
* @param date2
|
* @param date2
|
||||||
*/
|
*/
|
||||||
export function formatDuration(date1: string | Date | Dayjs, date2: string | Date | Dayjs) {
|
export function formatDuration(date1: string | Date | Dayjs, date2: string | Date | Dayjs) {
|
||||||
const start = dayjs(date1)
|
|
||||||
const end = dayjs(date2)
|
|
||||||
const diff = end.diff(start)
|
|
||||||
const duration = dayjs.duration(diff)
|
|
||||||
const weeks = Math.floor(duration.asDays() / 7);
|
|
||||||
const days = duration.days() % 7;
|
|
||||||
const hours = duration.hours();
|
|
||||||
const minutes = duration.minutes();
|
|
||||||
|
|
||||||
const parts = [];
|
// ignore seconds and milliseconds
|
||||||
|
date1 = dayjs(date1).startOf('minute');
|
||||||
|
date2 = dayjs(date2).startOf('minute');
|
||||||
|
|
||||||
if (weeks > 0) {
|
// get the difference in milliseconds
|
||||||
parts.push(`${weeks} Wo`);
|
const diff = dayjs(date2).diff(dayjs(date1));
|
||||||
}
|
const durationObj = dayjs.duration(diff);
|
||||||
if (days > 0 || weeks > 0) {
|
|
||||||
parts.push(`${days} Tag${days > 1 ? 'e' : ''}`);
|
|
||||||
}
|
|
||||||
if (hours > 0) {
|
|
||||||
parts.push(`${hours} Std`);
|
|
||||||
}
|
|
||||||
if (minutes > 0) {
|
|
||||||
parts.push(`${minutes} Min`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts.join(', ');
|
// get the duration in weeks, days, hours and minutes
|
||||||
|
const weeks = Math.floor(durationObj.asWeeks());
|
||||||
|
const days = durationObj.days();
|
||||||
|
const hours = durationObj.hours();
|
||||||
|
const minutes = durationObj.minutes();
|
||||||
|
|
||||||
|
// create a string array with the duration parts
|
||||||
|
const result: string[] = [];
|
||||||
|
if (weeks > 0) result.push(`${weeks} Wo`);
|
||||||
|
if (days > 0) result.push(`${days} Tag${days > 1 ? 'e' : ''}`);
|
||||||
|
if (hours > 0) result.push(`${hours} Std`);
|
||||||
|
if (minutes > 0) result.push(`${minutes} Min`);
|
||||||
|
|
||||||
|
return result.join(', ');
|
||||||
}
|
}
|
|
@ -3,17 +3,14 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
|
|
||||||
background-color: var(--mantine-color-body);
|
background-color: var(--mantine-color-body);
|
||||||
|
|
||||||
padding: var(--padding);
|
padding: var(--padding);
|
||||||
|
|
||||||
border: var(--border);
|
border: var(--border);
|
||||||
border-color: var(--mantine-primary-color-5);
|
border-color: var(--mantine-primary-color-5);
|
||||||
border-radius: var(--mantine-radius-lg);
|
border-radius: var(--border-radius);
|
||||||
|
|
||||||
margin: var(--gap);
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
@ -24,4 +21,11 @@
|
||||||
font-size: var(--mantine-font-size-lg);
|
font-size: var(--mantine-font-size-lg);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--mantine-primary-color-5);
|
color: var(--mantine-primary-color-5);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subjectStack {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
|
@ -1,26 +1,37 @@
|
||||||
import InnerHtml from "@/components/InnerHtml";
|
import InnerHtml from "@/components/InnerHtml";
|
||||||
import classes from './Announcement.module.css'
|
import classes from './Announcement.module.css'
|
||||||
import {Group, ThemeIcon} from "@mantine/core";
|
import {Group, Text, ThemeIcon, Tooltip} from "@mantine/core";
|
||||||
import {IconSpeakerphone} from "@tabler/icons-react";
|
import {IconSpeakerphone} from "@tabler/icons-react";
|
||||||
|
import {MessagesModel} from "@/models/MessageTypes.ts";
|
||||||
|
import {getUserName} from "@/components/users/modals/util.tsx";
|
||||||
|
import {humanDeltaFromNow, pprintDate} from "@/lib/datetime.ts";
|
||||||
|
|
||||||
export default function Announcement({subject, content}: {
|
export default function Announcement({announcement}: {
|
||||||
subject: string | null,
|
announcement: MessagesModel
|
||||||
content: string,
|
|
||||||
}) {
|
}) {
|
||||||
|
const senderName = getUserName(announcement.expand.sender)
|
||||||
return <div className={classes.announcement}>
|
return <div className={classes.announcement}>
|
||||||
<Group justify={"space-between"} wrap={"nowrap"} align={"top"} mb={"md"}>
|
<Group justify={"space-between"} wrap={"nowrap"} align={"top"} mb={"md"}>
|
||||||
{subject && <div className={classes.subject}>
|
<div className={classes.subjectStack}>
|
||||||
{subject}
|
{announcement.subject && <div className={`${classes.subject} wrapWords`}>
|
||||||
</div>}
|
{announcement.subject}
|
||||||
|
</div>}
|
||||||
|
|
||||||
<ThemeIcon
|
<Text size={"xs"} c={"dimmed"} className={"wrapWords"}>
|
||||||
className={classes.icon}
|
{senderName} • {pprintDate(announcement.created)} • {humanDeltaFromNow(announcement.created).message}
|
||||||
variant={"transparent"} size={"sm"}
|
</Text>
|
||||||
>
|
</div>
|
||||||
<IconSpeakerphone/>
|
|
||||||
</ThemeIcon>
|
<Tooltip label={`Ankündigung von ${getUserName(announcement.expand.sender)}`} withArrow>
|
||||||
|
<ThemeIcon
|
||||||
|
className={classes.icon}
|
||||||
|
variant={"transparent"} size={"sm"}
|
||||||
|
>
|
||||||
|
<IconSpeakerphone/>
|
||||||
|
</ThemeIcon>
|
||||||
|
</Tooltip>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<InnerHtml html={content}/>
|
<InnerHtml html={announcement.content}/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
|
@ -2,5 +2,6 @@
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column;
|
||||||
|
gap: var(--gap);
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ import {useInfiniteQuery} from "@tanstack/react-query";
|
||||||
import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx";
|
import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx";
|
||||||
import {Button, Center, Loader, Text} from "@mantine/core";
|
import {Button, Center, Loader, Text} from "@mantine/core";
|
||||||
import classes from './Announcements.module.css'
|
import classes from './Announcements.module.css'
|
||||||
import {IconMessageCircleUp} from "@tabler/icons-react";
|
import {IconMessageCircleDown} from "@tabler/icons-react";
|
||||||
import Announcement from "@/pages/chat/components/Announcement.tsx";
|
import Announcement from "@/pages/chat/components/Announcement.tsx";
|
||||||
|
|
||||||
export default function Announcements() {
|
export default function Announcements() {
|
||||||
|
@ -11,9 +11,10 @@ export default function Announcements() {
|
||||||
const query = useInfiniteQuery({
|
const query = useInfiniteQuery({
|
||||||
queryKey: ["announcements"],
|
queryKey: ["announcements"],
|
||||||
queryFn: async ({pageParam}) => (
|
queryFn: async ({pageParam}) => (
|
||||||
await pb.collection("messages").getList(pageParam, 100, {
|
await pb.collection("messages").getList(pageParam, 50, {
|
||||||
filter: `isAnnouncement=true&&sender!='${user?.id}'`,
|
filter: `isAnnouncement=true&&sender!='${user?.id}'`,
|
||||||
sort: "-created"
|
sort: "-created",
|
||||||
|
expand: "sender"
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
getNextPageParam: (lastPage) =>
|
getNextPageParam: (lastPage) =>
|
||||||
|
@ -39,8 +40,7 @@ export default function Announcements() {
|
||||||
{announcements.map((announcement) => (
|
{announcements.map((announcement) => (
|
||||||
<Announcement
|
<Announcement
|
||||||
key={announcement.id}
|
key={announcement.id}
|
||||||
subject={announcement.subject}
|
announcement={announcement}
|
||||||
content={announcement.content}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
@ -50,21 +50,18 @@ export default function Announcements() {
|
||||||
variant={"transparent"} color={"blue"}
|
variant={"transparent"} color={"blue"}
|
||||||
radius={"xl"}
|
radius={"xl"}
|
||||||
onClick={() => query.fetchNextPage()}
|
onClick={() => query.fetchNextPage()}
|
||||||
leftSection={<IconMessageCircleUp/>}
|
leftSection={<IconMessageCircleDown/>}
|
||||||
loading={query.isFetchingNextPage}
|
loading={query.isFetchingNextPage}
|
||||||
>
|
>
|
||||||
Mehr laden
|
Mehr laden
|
||||||
</Button>
|
</Button>
|
||||||
</Center>
|
</Center>
|
||||||
) : <div className={classes.text}>
|
) : announcements.length === 0 ? <div className={classes.text}>
|
||||||
<Text ta={"center"} size={"xs"} c={"dimmed"}>
|
<Text ta={"center"} size={"xs"} c={"dimmed"}>
|
||||||
{
|
"Noch keine Ankündigungen"
|
||||||
announcements.length > 0 ?
|
|
||||||
"Keine weiteren Ankündigungen"
|
|
||||||
: "Noch keine Ankündigungen"
|
|
||||||
}
|
|
||||||
</Text>
|
</Text>
|
||||||
</div>}
|
</div> : null
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
|
@ -17,8 +17,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
|
|
||||||
background-color: var(--mantine-color-body);
|
background-color: var(--mantine-color-body);
|
||||||
|
|
||||||
padding: var(--padding);
|
padding: var(--padding);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {useInfiniteQuery, useMutation} from "@tanstack/react-query";
|
||||||
import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx";
|
import {PocketBaseErrorAlert, usePB} from "@/lib/pocketbase.tsx";
|
||||||
import InnerHtml from "@/components/InnerHtml";
|
import InnerHtml from "@/components/InnerHtml";
|
||||||
import {getUserName} from "@/components/users/modals/util.tsx";
|
import {getUserName} from "@/components/users/modals/util.tsx";
|
||||||
import {pprintDateTime} from "@/lib/datetime.ts";
|
import {humanDeltaFromNow, pprintDateTime} from "@/lib/datetime.ts";
|
||||||
import {EventListModel} from "@/models/EventTypes.ts";
|
import {EventListModel} from "@/models/EventTypes.ts";
|
||||||
|
|
||||||
export default function Messages({eventList}: {
|
export default function Messages({eventList}: {
|
||||||
|
@ -87,7 +87,7 @@ export default function Messages({eventList}: {
|
||||||
<InnerHtml html={message.content}/>
|
<InnerHtml html={message.content}/>
|
||||||
|
|
||||||
<div className={classes.messageSender}>
|
<div className={classes.messageSender}>
|
||||||
{pprintDateTime(message.created)}
|
{humanDeltaFromNow(message.created).message} • {pprintDateTime(message.created)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.announcementsContainer {
|
.announcementsContainer {
|
||||||
max-height: 60vh;
|
max-height: 40vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
& > * {
|
& > * {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
|
|
Loading…
Reference in New Issue