diff options
Diffstat (limited to 'FrontEnd/src')
4 files changed, 105 insertions, 39 deletions
diff --git a/FrontEnd/src/app/views/timeline-common/Timeline.tsx b/FrontEnd/src/app/views/timeline-common/Timeline.tsx index 5c8f9195..ab658b89 100644 --- a/FrontEnd/src/app/views/timeline-common/Timeline.tsx +++ b/FrontEnd/src/app/views/timeline-common/Timeline.tsx @@ -5,6 +5,15 @@ import { TimelinePostInfo } from "@/services/timeline"; import TimelineItem from "./TimelineItem"; import TimelineTop from "./TimelineTop"; +import TimelineDateItem from "./TimelineDateItem"; + +function dateEqual(left: Date, right: Date): boolean { + return ( + left.getDate() == right.getDate() && + left.getMonth() == right.getMonth() && + left.getFullYear() == right.getFullYear() + ); +} export interface TimelinePostInfoEx extends TimelinePostInfo { onDelete?: () => void; @@ -24,6 +33,31 @@ const Timeline: React.FC<TimelineProps> = (props) => { const [showMoreIndex, setShowMoreIndex] = React.useState<number>(-1); + const groupedPosts = React.useMemo< + { date: Date; posts: (TimelinePostInfoEx & { index: number })[] }[] + >(() => { + const result: { + date: Date; + posts: (TimelinePostInfoEx & { index: number })[]; + }[] = []; + let index = 0; + for (const post of posts) { + const { time } = post; + if (result.length === 0) { + result.push({ date: time, posts: [{ ...post, index }] }); + } else { + const lastGroup = result[result.length - 1]; + if (dateEqual(lastGroup.date, time)) { + lastGroup.posts.push({ ...post, index }); + } else { + result.push({ date: time, posts: [{ ...post, index }] }); + } + } + index++; + } + return result; + }, [posts]); + return ( <div ref={props.containerRef} @@ -31,29 +65,33 @@ const Timeline: React.FC<TimelineProps> = (props) => { className={clsx("timeline", props.className)} > <TimelineTop height="56px" /> - {(() => { - const length = posts.length; - return posts.map((post, index) => { - return ( - <TimelineItem - post={post} - key={post.id} - current={length - 1 === index} - more={ - post.onDelete != null - ? { - isOpen: showMoreIndex === index, - toggle: () => - setShowMoreIndex((old) => (old === index ? -1 : index)), - onDelete: post.onDelete, - } - : undefined - } - onClick={() => setShowMoreIndex(-1)} - /> - ); - }); - })()} + {groupedPosts.map((group) => { + return ( + <> + <TimelineDateItem date={group.date} /> + {group.posts.map((post) => ( + <TimelineItem + post={post} + key={post.id} + current={posts.length - 1 === post.index} + more={ + post.onDelete != null + ? { + isOpen: showMoreIndex === post.index, + toggle: () => + setShowMoreIndex((old) => + old === post.index ? -1 : post.index + ), + onDelete: post.onDelete, + } + : undefined + } + onClick={() => setShowMoreIndex(-1)} + /> + ))} + </> + ); + })} </div> ); }; diff --git a/FrontEnd/src/app/views/timeline-common/TimelineDateItem.tsx b/FrontEnd/src/app/views/timeline-common/TimelineDateItem.tsx new file mode 100644 index 00000000..bcc1530f --- /dev/null +++ b/FrontEnd/src/app/views/timeline-common/TimelineDateItem.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import TimelineLine from "./TimelineLine"; + +export interface TimelineDateItemProps { + date: Date; +} + +const TimelineDateItem: React.FC<TimelineDateItemProps> = ({ date }) => { + return ( + <div className="timeline-date-item"> + <TimelineLine center={null} /> + <div className="timeline-date-item-badge"> + {date.toLocaleDateString()} + </div> + </div> + ); +}; + +export default TimelineDateItem; diff --git a/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx b/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx index 74431402..c096f890 100644 --- a/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx +++ b/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx @@ -1,7 +1,6 @@ import React from "react"; import clsx from "clsx"; import { Link } from "react-router-dom"; -import { useTranslation } from "react-i18next"; import { useAvatar } from "@/services/user"; import { TimelinePostInfo } from "@/services/timeline"; @@ -24,8 +23,6 @@ export interface TimelineItemProps { } const TimelineItem: React.FC<TimelineItemProps> = (props) => { - const { i18n } = useTranslation(); - const current = props.current === true; const { more } = props; @@ -42,6 +39,15 @@ const TimelineItem: React.FC<TimelineItemProps> = (props) => { > <TimelineLine center="node" current={current} /> <div className="timeline-item-card"> + {more != null ? ( + <i + className="bi-chevron-down text-info icon-button float-right" + onClick={(e) => { + more.toggle(); + e.stopPropagation(); + }} + /> + ) : null} <div className="timeline-item-header"> <span className="mr-2"> <span> @@ -52,19 +58,10 @@ const TimelineItem: React.FC<TimelineItemProps> = (props) => { {props.post.author.nickname} </small> <small className="text-secondary white-space-no-wrap"> - {props.post.time.toLocaleString(i18n.languages)} + {props.post.time.toLocaleTimeString()} </small> </span> </span> - {more != null ? ( - <i - className="bi-chevron-down text-info icon-button" - onClick={(e) => { - more.toggle(); - e.stopPropagation(); - }} - /> - ) : null} </div> <div className="timeline-content"> {(() => { diff --git a/FrontEnd/src/app/views/timeline-common/timeline-common.sass b/FrontEnd/src/app/views/timeline-common/timeline-common.sass index 5af3a9ef..ebaf96b5 100644 --- a/FrontEnd/src/app/views/timeline-common/timeline-common.sass +++ b/FrontEnd/src/app/views/timeline-common/timeline-common.sass @@ -49,7 +49,7 @@ $timeline-line-color-current: #36c2e6 background: $timeline-line-color &.start - height: 1.4em + height: 1.8em flex: 0 0 auto &.end @@ -102,7 +102,7 @@ $timeline-line-color-current: #36c2e6 .timeline-item-card @extend .cru-card position: relative - padding: 0.5em 2em 0.5em 4em + padding: 0.3em 0.5em 1em 4em transition: background 0.5s, padding-left 0.5s @include media-breakpoint-down(sm) @@ -114,7 +114,7 @@ $timeline-line-color-current: #36c2e6 .timeline-item-header display: flex align-items: center - @extend .mb-2 + @extend .my-2 .timeline-avatar border-radius: 50% @@ -133,6 +133,18 @@ $timeline-line-color-current: #36c2e6 max-width: 60% max-height: 200px +.timeline-date-item + position: relative + padding: 0.3em 0 0.3em 4em + +.timeline-date-item-badge + display: inline-block + padding: 0.1em 0.4em + border-radius: 0.4em + background: #7c7c7c + color: white + font-size: 0.8em + .timeline-post-edit-image max-width: 100px max-height: 100px |