mirror of
https://github.com/wassname/talk.git
synced 2026-08-13 12:40:11 +08:00
[CORL 547] org-wide announcements (#2813)
* CRUD announcements * only show announcement if not disabled * make announcements dismissable * add announcement mutations * update announcement form logic * style announcements on stream * update snap * localize strings * close form if announcement is removed * move announcement config below sitewide commenting config * move date calculation inside useMemo * move announcementconfig code to announcementconfigcontainer * use coralContext for localStorage * fix type of announcement createdAt * move announcement form to modal * remove payload pruning from configure route * simplify announcement display logic * make validation message full width Co-authored-by: Kim Gardner <kgardnr@gmail.com>
This commit is contained in:
co-authored by
Kim Gardner
parent
a7b2af85fc
commit
a1a8652f7e
@@ -9,6 +9,11 @@ const CLASSES = {
|
||||
*/
|
||||
guidelines: "coral coral-guidelines",
|
||||
|
||||
/**
|
||||
* guidlines represents the box containing the guidlines.
|
||||
*/
|
||||
announcement: "coral coral-announcement",
|
||||
|
||||
/**
|
||||
* closedSitewide represents the box containing the message when comments
|
||||
* are closed sitewide.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.root {
|
||||
background-color: #2b7eb5;
|
||||
color: white;
|
||||
padding: var(--v2-spacing-3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.text {
|
||||
@mixin bodyCopy;
|
||||
color: white;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import cn from "classnames";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import CLASSES from "coral-stream/classes";
|
||||
import { Button, Flex, Icon } from "coral-ui/components";
|
||||
|
||||
import styles from "./Announcement.css";
|
||||
|
||||
interface Props {
|
||||
children: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const Announcement: FunctionComponent<Props> = props => {
|
||||
return (
|
||||
<div className={cn(styles.root, CLASSES.announcement)}>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<Flex itemGutter>
|
||||
<Icon size="lg">notifications</Icon>
|
||||
<span className={styles.text}>{props.children}</span>
|
||||
</Flex>
|
||||
<Button color="light" onClick={props.onClose}>
|
||||
<Icon>close</Icon>
|
||||
</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Announcement;
|
||||
@@ -0,0 +1,67 @@
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { ANNOUNEMENT_DISMISSED_KEY } from "coral-framework/constants";
|
||||
import { useCoralContext } from "coral-framework/lib/bootstrap";
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { AnnouncementContainer_settings as SettingsData } from "coral-stream/__generated__/AnnouncementContainer_settings.graphql";
|
||||
|
||||
import Announcement from "./Announcement";
|
||||
|
||||
interface Props {
|
||||
settings: SettingsData;
|
||||
}
|
||||
|
||||
export const AnnouncementContainer: FunctionComponent<Props> = ({
|
||||
settings,
|
||||
}) => {
|
||||
const { localStorage } = useCoralContext();
|
||||
const [dismissed, setDismissed] = useState(false);
|
||||
useEffect(() => {
|
||||
async function getDismissedStatus() {
|
||||
if (settings.announcement) {
|
||||
const key = await localStorage.getItem(ANNOUNEMENT_DISMISSED_KEY);
|
||||
if (key && key === settings.announcement.id) {
|
||||
setDismissed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
getDismissedStatus();
|
||||
}, [settings]);
|
||||
const dismissAnnouncement = useCallback(async () => {
|
||||
if (settings.announcement) {
|
||||
await localStorage.setItem(
|
||||
ANNOUNEMENT_DISMISSED_KEY,
|
||||
settings.announcement.id
|
||||
);
|
||||
setDismissed(true);
|
||||
}
|
||||
}, [settings]);
|
||||
if (!settings.announcement || dismissed) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Announcement onClose={dismissAnnouncement}>
|
||||
{settings.announcement.content}
|
||||
</Announcement>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment AnnouncementContainer_settings on Settings {
|
||||
announcement {
|
||||
id
|
||||
content
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(AnnouncementContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -0,0 +1,4 @@
|
||||
export {
|
||||
default,
|
||||
default as AnnouncementContainer,
|
||||
} from "./AnnouncementContainer";
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
} from "coral-stream/__generated__/StreamContainerLocal.graphql";
|
||||
|
||||
import AllCommentsTab from "./AllCommentsTab";
|
||||
import AnnouncementContainer from "./Announcement";
|
||||
import BannedInfo from "./BannedInfo";
|
||||
import { CommunityGuidelinesContainer } from "./CommunityGuidelines";
|
||||
import StreamDeletionRequestCalloutContainer from "./DeleteAccount/StreamDeletionRequestCalloutContainer";
|
||||
@@ -152,6 +153,7 @@ export const StreamContainer: FunctionComponent<Props> = props => {
|
||||
size="double"
|
||||
>
|
||||
<UserBoxContainer viewer={props.viewer} settings={props.settings} />
|
||||
<AnnouncementContainer settings={props.settings} />
|
||||
{props.viewer && (
|
||||
<StreamDeletionRequestCalloutContainer viewer={props.viewer} />
|
||||
)}
|
||||
@@ -296,6 +298,7 @@ const enhanced = withFragmentContainer<Props>({
|
||||
...UserBoxContainer_settings
|
||||
...CommunityGuidelinesContainer_settings
|
||||
...SuspendedInfoContainer_settings
|
||||
...AnnouncementContainer_settings
|
||||
}
|
||||
`,
|
||||
})(StreamContainer);
|
||||
|
||||
Reference in New Issue
Block a user