feat: separate read and done statuses

Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
Heikki Hellgren
2024-01-26 09:00:29 +02:00
parent c3a22686dd
commit 8724ed1fdf
11 changed files with 214 additions and 114 deletions
@@ -37,6 +37,10 @@ export interface NotificationsApi {
getStatus(): Promise<NotificationStatus>;
markDone(ids: string[]): Promise<NotificationIds>;
markUndone(ids: string[]): Promise<NotificationIds>;
markRead(ids: string[]): Promise<NotificationIds>;
markUnread(ids: string[]): Promise<NotificationIds>;
@@ -52,6 +52,22 @@ export class NotificationsClient implements NotificationsApi {
return await this.request<NotificationStatus>('status');
}
async markDone(ids: string[]): Promise<NotificationIds> {
return await this.request<NotificationIds>('done', {
method: 'POST',
body: JSON.stringify({ ids: ids }),
headers: { 'Content-Type': 'application/json' },
});
}
async markUndone(ids: string[]): Promise<NotificationIds> {
return await this.request<NotificationIds>('undone', {
method: 'POST',
body: JSON.stringify({ ids: ids }),
headers: { 'Content-Type': 'application/json' },
});
}
async markRead(ids: string[]): Promise<NotificationIds> {
return await this.request<NotificationIds>('read', {
method: 'POST',
@@ -37,7 +37,7 @@ const useStyles = makeStyles(_theme => ({
}));
export const NotificationsPage = () => {
const [type, setType] = useState<NotificationType>('unread');
const [type, setType] = useState<NotificationType>('undone');
const [refresh, setRefresh] = React.useState(false);
const { error, value, retry } = useNotificationsApi(
@@ -77,16 +77,16 @@ export const NotificationsPage = () => {
<Button
className={styles.filterButton}
startIcon={<Inbox />}
variant={type === 'unread' ? 'contained' : 'text'}
onClick={() => setType('unread')}
variant={type === 'undone' ? 'contained' : 'text'}
onClick={() => setType('undone')}
>
Inbox
</Button>
<Button
className={styles.filterButton}
startIcon={<Check />}
variant={type === 'read' ? 'contained' : 'text'}
onClick={() => setType('read')}
variant={type === 'done' ? 'contained' : 'text'}
onClick={() => setType('done')}
>
Done
</Button>
@@ -20,6 +20,7 @@ import {
IconButton,
makeStyles,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
@@ -49,9 +50,13 @@ const useStyles = makeStyles(theme => ({
header: {
borderBottom: `1px solid ${theme.palette.divider}`,
},
notificationRow: {
cursor: 'pointer',
'&.hideOnHover': {
'&.unread': {
border: '1px solid rgba(255, 255, 255, .3)',
},
'& .hideOnHover': {
display: 'initial',
},
'& .showOnHover': {
@@ -138,12 +143,12 @@ export const NotificationsTable = (props: {
type !== 'saved' &&
'Select all'}
{selected.length > 0 && `${selected.length} selected`}
{type === 'read' && selected.length > 0 && (
{type === 'done' && selected.length > 0 && (
<Button
startIcon={<Inbox fontSize="small" />}
onClick={() => {
notificationsApi
.markUnread(selected)
.markUndone(selected)
.then(() => props.onUpdate());
setSelected([]);
}}
@@ -152,12 +157,12 @@ export const NotificationsTable = (props: {
</Button>
)}
{type === 'unread' && selected.length > 0 && (
{type === 'undone' && selected.length > 0 && (
<Button
startIcon={<Check fontSize="small" />}
onClick={() => {
notificationsApi
.markRead(selected)
.markDone(selected)
.then(() => props.onUpdate());
setSelected([]);
}}
@@ -168,107 +173,121 @@ export const NotificationsTable = (props: {
</TableCell>
</TableRow>
</TableHead>
{props.notifications?.map(notification => {
return (
<TableRow
key={notification.id}
className={styles.notificationRow}
hover
>
<TableCell
width="60px"
style={{ verticalAlign: 'center', paddingRight: '0px' }}
<TableBody>
{props.notifications?.map(notification => {
return (
<TableRow
key={notification.id}
className={`${styles.notificationRow} ${
!notification.read ? 'unread' : ''
}`}
hover
>
<Checkbox
className={styles.checkBox}
size="small"
checked={isChecked(notification.id)}
onClick={() => onCheckBoxClick(notification.id)}
/>
</TableCell>
<TableCell
onClick={() => navigate(notification.link)}
style={{ paddingLeft: 0 }}
>
<Typography variant="subtitle2">{notification.title}</Typography>
<Typography variant="body2">
{notification.description}
</Typography>
</TableCell>
<TableCell style={{ textAlign: 'right' }}>
<Box className="hideOnHover">
<RelativeTime value={notification.created} />
</Box>
<Box className="showOnHover">
<Tooltip title={notification.link}>
<IconButton
className={styles.actionButton}
onClick={() => navigate(notification.link)}
>
<ArrowForwardIcon />
</IconButton>
</Tooltip>
<Tooltip
title={notification.read ? 'Move to inbox' : 'Mark as done'}
>
<IconButton
className={styles.actionButton}
onClick={() => {
if (notification.read) {
notificationsApi
.markUnread([notification.id])
.then(() => {
props.onUpdate();
});
} else {
<TableCell
width="60px"
style={{ verticalAlign: 'center', paddingRight: '0px' }}
>
<Checkbox
className={styles.checkBox}
size="small"
checked={isChecked(notification.id)}
onClick={() => onCheckBoxClick(notification.id)}
/>
</TableCell>
<TableCell
onClick={() =>
notificationsApi
.markRead([notification.id])
.then(() => navigate(notification.link))
}
style={{ paddingLeft: 0 }}
>
<Typography variant="subtitle2">
{notification.title}
</Typography>
<Typography variant="body2">
{notification.description}
</Typography>
</TableCell>
<TableCell style={{ textAlign: 'right' }}>
<Box className="hideOnHover">
<RelativeTime value={notification.created} />
</Box>
<Box className="showOnHover">
<Tooltip title={notification.link}>
<IconButton
className={styles.actionButton}
onClick={() =>
notificationsApi
.markRead([notification.id])
.then(() => {
props.onUpdate();
});
.then(() => navigate(notification.link))
}
}}
>
<ArrowForwardIcon />
</IconButton>
</Tooltip>
<Tooltip
title={notification.read ? 'Move to inbox' : 'Mark as done'}
>
{notification.read ? (
<Inbox fontSize="small" />
) : (
<Check fontSize="small" />
)}
</IconButton>
</Tooltip>
<Tooltip
title={notification.saved ? 'Remove from saved' : 'Save'}
>
<IconButton
className={styles.actionButton}
onClick={() => {
if (notification.saved) {
notificationsApi
.markUnsaved([notification.id])
.then(() => {
props.onUpdate();
});
} else {
notificationsApi
.markSaved([notification.id])
.then(() => {
props.onUpdate();
});
}
}}
<IconButton
className={styles.actionButton}
onClick={() => {
if (notification.read) {
notificationsApi
.markUndone([notification.id])
.then(() => {
props.onUpdate();
});
} else {
notificationsApi
.markDone([notification.id])
.then(() => {
props.onUpdate();
});
}
}}
>
{notification.read ? (
<Inbox fontSize="small" />
) : (
<Check fontSize="small" />
)}
</IconButton>
</Tooltip>
<Tooltip
title={notification.saved ? 'Remove from saved' : 'Save'}
>
{notification.saved ? (
<CloseIcon fontSize="small" />
) : (
<Bookmark fontSize="small" />
)}
</IconButton>
</Tooltip>
</Box>
</TableCell>
</TableRow>
);
})}
<IconButton
className={styles.actionButton}
onClick={() => {
if (notification.saved) {
notificationsApi
.markUnsaved([notification.id])
.then(() => {
props.onUpdate();
});
} else {
notificationsApi
.markSaved([notification.id])
.then(() => {
props.onUpdate();
});
}
}}
>
{notification.saved ? (
<CloseIcon fontSize="small" />
) : (
<Bookmark fontSize="small" />
)}
</IconButton>
</Tooltip>
</Box>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
);
};