Skip to content

Commit

Permalink
implement bulk read for messages
Browse files Browse the repository at this point in the history
  • Loading branch information
TuuKeZu committed Jan 7, 2025
1 parent 8df3575 commit 0a3e918
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 42 deletions.
12 changes: 6 additions & 6 deletions src/compontents/Frontpage/Frontpage.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,11 @@ export default function Frontpage() {
const auth = useSelector(useAuth);

const now = new Date();

const weekCount = auth.isStudent ? 3 : 5;

const days = [
new Date(now.getFullYear(), now.getMonth(), now.getDate()),
new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7),
new Date(now.getFullYear(), now.getMonth(), now.getDate() + 14)
]
const days = Array(weekCount).fill(null).map((_, i) => new Date(now.getFullYear(), now.getMonth(), now.getDate() + (i * 7)))


const initialize = () => {
dispatch(getMessages({auth: auth.token, path: 'inbox'}))
Expand All @@ -75,6 +74,7 @@ export default function Frontpage() {

const loadSchedule = () => {
setCategory('schedule');

days.forEach(date => {
dispatch(getWeek({auth: auth.token, date: date}))
})
Expand All @@ -98,7 +98,7 @@ export default function Frontpage() {
<>
{open ? <ScheduleWindow current={current} loadCalendar={loadCalendar} onClose={() => setOpen(false)} /> : null}
<BlurLayer className={styles['content']} isLoading={open}>
<div className={styles['top-container']}>
<div className={`${styles['top-container']} ${styles['full']}`}>
<div className={styles['left']}>
<div className={styles['side-bar']}>
<div className={styles['side-bar-content']}>
Expand Down
6 changes: 6 additions & 0 deletions src/compontents/Frontpage/Frontpage.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
right: calc(250px + 300px);
}

.top-container.full .left {
height: 200%;

align-self: flex-start;
}

.top-container .middle {
position: absolute;
width: 250px;
Expand Down
171 changes: 151 additions & 20 deletions src/compontents/Messages/Messages.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef, useMemo } from 'react';
import { useAuth } from '../../features/authentication/authSlice';
import { useSelector, useDispatch } from 'react-redux';
import { useGrades, getGradebook } from '../../features/grades/gradeSlice';
Expand All @@ -13,9 +13,12 @@ export default function Messages() {

const [category, setCategory] = useState('inbox');
const [current, setCurrent] = useState(null);
const [bulkMode, setBulkMode] = useState(false);
const dispatch = useDispatch();
const auth = useSelector(useAuth);

const container = useRef(null);

const initialize = () => {
loadMessages('inbox');

Expand All @@ -27,33 +30,55 @@ export default function Messages() {
setCategory(category);
}

const loadMessage = (id) => {
dispatch(getMessage({auth: auth.token, path: category, id: id}))
const loadMessage = (id, autoUnread) => {
dispatch(getMessage({auth: auth.token, id: id, autoUnread }))
setCurrent(id);
}

useEffect(() => { initialize() }, []);

return (
<div className={styles['content']}>
<div className={bulkMode ? styles['content-bulk'] : styles['content']}>
<div className={styles['categories']}>
<div onClick={() => loadMessages('inbox')} className={category == 'inbox' ? styles['category-selected'] : null}>
<h1>Saapuneet</h1>
</div>
<div onClick={() => loadMessages('outbox')} className={category == 'outbox' ? styles['category-selected'] : null}>
<h1>Lähetetyt</h1>
</div>
<div onClick={() => loadMessages('appointments')} className={category == 'appointments' ? styles['category-selected'] : null}>
<h1>Tapahtumakutsut</h1>
</div>
{
bulkMode ?
<>
<h1 className={styles['title']}>Kaikki viestit yhdessä näkymässä</h1>
<button onClick={() => { setBulkMode(false) }} className={styles['action']}>Takaisin</button>
</>
:
<>
<div onClick={() => loadMessages('inbox')} className={category == 'inbox' ? styles['category-selected'] : null}>
<h1>Saapuneet</h1>
</div>
<div onClick={() => loadMessages('outbox')} className={category == 'outbox' ? styles['category-selected'] : null}>
<h1>Lähetetyt</h1>
</div>
<div onClick={() => loadMessages('appointments')} className={category == 'appointments' ? styles['category-selected'] : null}>
<h1>Tapahtumakutsut</h1>
</div>
<button onClick={() => { setBulkMode(true) }} className={styles['action']}>Avaa kaikki lukemattomat</button>
</>
}

</div>
<div className={styles['messages']}>
<div className={styles['list']}>
<MessageList category={category} onLoad={loadMessage}/>
{
bulkMode ?
null
:
<div className={styles['messages']}>
<div className={styles['list']}>
<MessageList category={category} onLoad={loadMessage}/>
</div>
</div>
</div>
<div className={styles['message-content']}>
<MessageContentObject current={current} />
}
<div ref={container} className={styles['message-content']}>
{
bulkMode ?
<MessageObjectList container={container} setBulk={setBulkMode} setCurrent={setCurrent} />
:
<MessageContentObject current={current} />
}
</div>
</div>
)
Expand All @@ -79,7 +104,7 @@ const MessageList = ({category, onLoad}) => {

const MessageObject = ({message, onLoad}) => {
return (
<div onClick={() => onLoad(message.id)} className={message.new ? `${styles['message-object']} ${styles['new']}` : `${styles['message-object']}`}>
<div onClick={() => onLoad(message.id, false)} className={message.new ? `${styles['message-object']} ${styles['new']}` : `${styles['message-object']}`}>
<h1>{message.subject}</h1>
<h2>{message.timeStamp}</h2>
{message.senders ? message.senders.map((s, i) => <h2 key={i}>{s.name}</h2>) : null}
Expand Down Expand Up @@ -146,3 +171,109 @@ const WilmaLink = ({message}) => {
</>
)
}

const MessageObjectList = ({ container, setBulk, setCurrent }) => {
const messages = useSelector(useMessages);
const map = messages.messages;

const list = Object.keys(map).filter(k => map[k].new);

if (list.length <= 0) {
return <PlaceHolder className={styles['message-placeholder']} />
}

return (
<>
{list.reverse().map((id, i) => {
return <MessageContentObjectFull key={i} id={id} container={container} setBulk={setBulk} setCurrent={setCurrent} />
})}
</>
)
}

const MessageContentObjectFull = ({ id, container, setBulk, setCurrent }) => {
const ref = useRef(null);
const isVisible = useOnScreen(ref, container);
const dispatch = useDispatch();
const auth = useSelector(useAuth);
const messages = useSelector(useMessages);

const message = messages.messages[id];

const [initialized, initialize] = useState(false);

useEffect(() => {
if (isVisible && !initialized) {
setTimeout(() => {
dispatch(getMessage({auth: auth.token, id: message.id, autoUnread: true }))
initialize(true);
}, 100);
}
}, [isVisible]);

const markRead = () => {
dispatch(getMessage({auth: auth.token, id: message.id, autoUnread: false, forceRefresh: true }))
}

const respond = () => {
setCurrent(message.id);
setBulk(false);
}


return (
<div ref={ref} className={`${styles['message-full']} ${styles['full']}`}>
<h1>{message.subject}</h1>
<div className={styles['info']}>
<ul><a>Lähettäjä(t) </a>{message.senders ? message.senders.map((s, i) => <a key={i}>{s.name}</a>) : null}</ul>
<ul>Vastaanottaja(t) <a></a><a>{message.recipients ? message.recipients : 'Piilotettu'}</a></ul>
<ul>Lähetetty <a></a><a>{message.timeStamp}</a></ul>
</div>
{
message.isLoading ?
<LoadingScreen className={styles['message-full-loading-screen']}/>
:
<>
<div className={styles['actions']}>
<button onClick={markRead} className={styles['action']}>Merkitse luetuksi</button>
<button onClick={respond} className={styles['action']}>Vastaa viestiin</button>
</div>
<div className={styles['main-content']}>
<div dangerouslySetInnerHTML={{__html: message.content}}></div>
</div>
<div className={styles['responses']}>
{
(message.replyList ?? []).map((reply, i) => {
return <MessageReply key={i} reply={reply} />
})
}
</div>

</>
}

</div>
)
}



// https://stackoverflow.com/questions/45514676/how-to-check-if-element-is-visible-in-dom
export function useOnScreen(ref, container) {

const [isIntersecting, setIntersecting] = useState(false)

const observer = useMemo(() => {
return new IntersectionObserver(([entry]) => {
setIntersecting(entry.isIntersecting)
}, { root: container.current })
}, [ref]);


useEffect(() => {
observer.observe(ref.current)
return () => observer.disconnect()
}, [])

return isIntersecting
}
Loading

0 comments on commit 0a3e918

Please sign in to comment.