Skip to content

Commit 18132a8

Browse files
change notification system; update group feedback ui
1 parent 7898122 commit 18132a8

20 files changed

+428
-433
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@
7373
"react-flatpickr": "^3.10.7",
7474
"react-full-screen": "^1.0.2",
7575
"react-helmet": "^6.1.0",
76+
"react-hot-toast": "^2.0.0",
7677
"react-instantsearch-dom": "^6.11.1",
7778
"react-markdown": "^5.0.3",
78-
"react-notification-system": "^0.4.0",
7979
"react-select": "^4.3.1",
8080
"react-share": "^4.4.0",
8181
"react-simplemde-editor": "^4.1.3",

root-wrapper.tsx

+30-29
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,30 @@
1-
import * as React from 'react';
2-
import { DarkModeProvider } from './src/context/DarkModeProvider';
3-
import { EditorContext } from './src/context/EditorContext';
4-
import { FirebaseProvider } from './src/context/FirebaseContext';
5-
import GlobalErrorBoundary from './src/context/GlobalErrorBoundary';
6-
import { NotificationSystemProvider } from './src/context/NotificationSystemContext';
7-
import { SignInProvider } from './src/context/SignInContext';
8-
import { UserDataProvider } from './src/context/UserDataContext/UserDataContext';
9-
import { UserGroupsProvider } from './src/hooks/groups/useUserGroups';
10-
11-
export const wrapRootElement = ({ element }) => (
12-
<NotificationSystemProvider>
13-
<GlobalErrorBoundary>
14-
<FirebaseProvider>
15-
<UserDataProvider>
16-
<DarkModeProvider>
17-
<SignInProvider>
18-
<UserGroupsProvider>
19-
<EditorContext.Provider value={{ inEditor: false }}>
20-
{element}
21-
</EditorContext.Provider>
22-
</UserGroupsProvider>
23-
</SignInProvider>
24-
</DarkModeProvider>
25-
</UserDataProvider>
26-
</FirebaseProvider>
27-
</GlobalErrorBoundary>
28-
</NotificationSystemProvider>
29-
);
1+
import React from 'react';
2+
import { Toaster } from "react-hot-toast";
3+
import { DarkModeProvider } from './src/context/DarkModeProvider';
4+
import { EditorContext } from './src/context/EditorContext';
5+
import { FirebaseProvider } from './src/context/FirebaseContext';
6+
import GlobalErrorBoundary from './src/context/GlobalErrorBoundary';
7+
import { SignInProvider } from './src/context/SignInContext';
8+
import { UserDataProvider } from './src/context/UserDataContext/UserDataContext';
9+
import { UserGroupsProvider } from './src/hooks/groups/useUserGroups';
10+
11+
export const wrapRootElement = ({ element }): JSX.Element => (
12+
<>
13+
<GlobalErrorBoundary>
14+
<FirebaseProvider>
15+
<UserDataProvider>
16+
<DarkModeProvider>
17+
<SignInProvider>
18+
<UserGroupsProvider>
19+
<EditorContext.Provider value={{ inEditor: false }}>
20+
{element}
21+
</EditorContext.Provider>
22+
</UserGroupsProvider>
23+
</SignInProvider>
24+
</DarkModeProvider>
25+
</UserDataProvider>
26+
</FirebaseProvider>
27+
</GlobalErrorBoundary>
28+
<Toaster position="top-right" />
29+
</>
30+
);

src/components/Groups/EditGroupPage/EditGroupPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { Link, navigate } from 'gatsby';
2-
import * as React from 'react';
2+
import React from 'react';
33
import { useReducer } from 'react';
4-
import { useNotificationSystem } from '../../../context/NotificationSystemContext';
54
import { useActiveGroup } from '../../../hooks/groups/useActiveGroup';
65
import { useGroupActions } from '../../../hooks/groups/useGroupActions';
76
import { GroupData } from '../../../models/groups/groups';
87
import Layout from '../../layout';
98
import SEO from '../../seo';
109
import TopNavigationBar from '../../TopNavigationBar/TopNavigationBar';
1110
import Breadcrumbs from '../Breadcrumbs';
11+
import toast from "react-hot-toast";
1212

1313
export default function EditGroupPage(props) {
1414
const { groupId } = props as {
@@ -25,7 +25,6 @@ export default function EditGroupPage(props) {
2525
originalGroup
2626
);
2727
const { deleteGroup, updateGroup } = useGroupActions();
28-
const notifications = useNotificationSystem();
2928

3029
React.useEffect(() => {
3130
if (!group && originalGroup) editGroup(originalGroup);
@@ -128,7 +127,7 @@ export default function EditGroupPage(props) {
128127
) {
129128
deleteGroup(groupId)
130129
.then(() => navigate(`/groups/`, { replace: true }))
131-
.catch(e => notifications.showErrorNotification(e));
130+
.catch(e => toast.error(e.message));
132131
}
133132
}}
134133
className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-dark-surface focus:ring-red-500"

src/components/Groups/EditPostPage/EditPostPage.tsx

+2-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import { Link, navigate } from 'gatsby';
44
import * as React from 'react';
55
import { useReducer } from 'react';
66
import Flatpickr from 'react-flatpickr';
7-
import { useNotificationSystem } from '../../../context/NotificationSystemContext';
87
import { useActiveGroup } from '../../../hooks/groups/useActiveGroup';
98
import { usePost } from '../../../hooks/groups/usePost';
109
import { usePostActions } from '../../../hooks/groups/usePostActions';
11-
import { useFirebaseApp } from '../../../hooks/useFirebase';
10+
import toast from "react-hot-toast";
1211
import { PostData } from '../../../models/groups/posts';
1312
import Layout from '../../layout';
1413
import SEO from '../../seo';
@@ -32,8 +31,6 @@ export default function EditPostPage(props) {
3231
null
3332
);
3433
const { updatePost, deletePost } = usePostActions(groupId);
35-
const firebaseApp = useFirebaseApp();
36-
const notifications = useNotificationSystem();
3734

3835
React.useEffect(() => {
3936
// we need to check for timestamp -- ServerValue is null initially
@@ -207,7 +204,7 @@ export default function EditPostPage(props) {
207204
.then(() =>
208205
navigate(`/groups/${groupId}`, { replace: true })
209206
)
210-
.catch(e => notifications.showErrorNotification(e));
207+
.catch(e => toast.error(e.message));
211208
}
212209
}}
213210
className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-dark-surface focus:ring-red-500"

src/components/Groups/EditProblemPage/EditProblemPage.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { Link, navigate } from 'gatsby';
1212
import * as React from 'react';
1313
import { useReducer } from 'react';
1414
import Flatpickr from 'react-flatpickr';
15-
import { useNotificationSystem } from '../../../context/NotificationSystemContext';
1615
import { useActiveGroup } from '../../../hooks/groups/useActiveGroup';
1716
import { usePost } from '../../../hooks/groups/usePost';
1817
import { usePostActions } from '../../../hooks/groups/usePostActions';
@@ -32,6 +31,7 @@ import TopNavigationBar from '../../TopNavigationBar/TopNavigationBar';
3231
import Breadcrumbs from '../Breadcrumbs';
3332
import MarkdownEditor from '../MarkdownEditor';
3433
import EditProblemHintSection from './EditProblemHintSection';
34+
import toast from "react-hot-toast";
3535

3636
export default function EditProblemPage(props) {
3737
const { groupId, postId, problemId } = props as {
@@ -53,7 +53,6 @@ export default function EditProblemPage(props) {
5353
originalProblem
5454
);
5555
const { saveProblem, deleteProblem } = usePostActions(groupId);
56-
const notifications = useNotificationSystem();
5756
const [isSearchOpen, setIsSearchOpen] = React.useState(false);
5857

5958
React.useEffect(() => {
@@ -87,7 +86,7 @@ export default function EditProblemPage(props) {
8786
replace: true,
8887
});
8988
})
90-
.catch(e => notifications.showErrorNotification(e));
89+
.catch(e => toast.error(e.message));
9190
}
9291
};
9392
const handleSaveProblem = () => {

src/components/Groups/Feedback.tsx

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import classNames from 'classnames';
2+
import React, { useState } from 'react';
3+
import toast from 'react-hot-toast';
4+
5+
export default function Feedback(): JSX.Element {
6+
const baseClasses =
7+
'rounded-full border h-8 w-8 text-xl transform transition focus:outline-none';
8+
const unselectedClasses = 'hover:scale-110 border-gray-200';
9+
const selectedClasses = 'scale-110 border-teal-600';
10+
11+
const [selected, setSelected] = useState<
12+
'terrible' | 'bad' | 'good' | 'great' | null
13+
>(null);
14+
15+
const submitFeedback = feedback => {
16+
if (selected === feedback) {
17+
setSelected(null);
18+
// todo reset feedback
19+
} else {
20+
toast.promise(
21+
(() =>
22+
new Promise(resolve => {
23+
// todo save feedback
24+
setTimeout(resolve, 100);
25+
}))(),
26+
{
27+
loading: 'Submitting...',
28+
success: 'Thanks for the feedback!',
29+
error: 'Error submitting feedback.',
30+
}
31+
);
32+
setSelected(feedback);
33+
}
34+
};
35+
36+
return (
37+
<>
38+
<div className="text-center">
39+
<div className="font-medium">How was the video?</div>
40+
<span className="flex items-center space-x-2 justify-center">
41+
<button
42+
type="button"
43+
title={'Rate video as Terrible'}
44+
className={classNames(
45+
baseClasses,
46+
selected === 'terrible' ? selectedClasses : unselectedClasses
47+
)}
48+
onClick={() => submitFeedback('terrible')}
49+
>
50+
😨
51+
</button>
52+
<button
53+
type="button"
54+
title={'Rate video as Bad'}
55+
className={classNames(
56+
baseClasses,
57+
selected === 'bad' ? selectedClasses : unselectedClasses
58+
)}
59+
onClick={() => submitFeedback('bad')}
60+
>
61+
🤨
62+
</button>
63+
<button
64+
type="button"
65+
title={'Rate video as Good'}
66+
className={classNames(
67+
baseClasses,
68+
selected === 'good' ? selectedClasses : unselectedClasses
69+
)}
70+
onClick={() => submitFeedback('good')}
71+
>
72+
😀
73+
</button>
74+
<button
75+
title={'Rate video as Great'}
76+
type="button"
77+
className={classNames(
78+
baseClasses,
79+
selected === 'great' ? selectedClasses : unselectedClasses
80+
)}
81+
onClick={() => submitFeedback('great')}
82+
>
83+
😍
84+
</button>
85+
</span>
86+
</div>
87+
<div className="h-4" />
88+
{selected !== null && (
89+
<>
90+
<textarea
91+
required
92+
className="text-sm w-full mt-4 px-2 py-2 placeholder-gray-500 focus:ring-teal-500 focus:border-teal-500 border-gray-300 rounded-md mr-2"
93+
placeholder="Give Additional Video Feedback"
94+
/>
95+
96+
<button
97+
type="submit"
98+
className="items-center mt-2 sm:mt-0 px-3 py-2 rounded-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-teal-500 focus:border-teal-500"
99+
>
100+
Submit Additional Feedback
101+
</button>
102+
</>
103+
)}
104+
</>
105+
);
106+
}

src/components/Groups/GroupPage/FeedItem.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CheckIcon, ClipboardListIcon } from '@heroicons/react/outline';
33
import { BookmarkIcon } from '@heroicons/react/solid';
44
import { Link } from 'gatsby';
55
import React, { useState } from 'react';
6-
import { useNotificationSystem } from '../../../context/NotificationSystemContext';
6+
import toast from "react-hot-toast";
77
import { useActiveGroup } from '../../../hooks/groups/useActiveGroup';
88
import { usePostActions } from '../../../hooks/groups/usePostActions';
99
import { GroupData } from '../../../models/groups/groups';
@@ -80,7 +80,6 @@ export default function FeedItem({
8080
const { updatePost, deletePost } = usePostActions(group.id);
8181

8282
const [showDropdown, setShowDropdown] = useState(false);
83-
const notifications = useNotificationSystem();
8483
const ref = React.useRef<HTMLDivElement>();
8584

8685
React.useEffect(() => {
@@ -218,7 +217,7 @@ export default function FeedItem({
218217
confirm('Are you sure you want to delete this post?')
219218
) {
220219
deletePost(post.id).catch(e =>
221-
notifications.showErrorNotification(e)
220+
toast.error(e.message)
222221
);
223222
}
224223
}}

src/components/Groups/MembersPage/MemberDetail.tsx

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { navigate } from 'gatsby-link';
22
import * as React from 'react';
33
import { useContext } from 'react';
4-
import { useNotificationSystem } from '../../../context/NotificationSystemContext';
4+
import toast from 'react-hot-toast';
55
import UserDataContext from '../../../context/UserDataContext/UserDataContext';
66
import getPermissionLevel from '../../../functions/src/groups/utils/getPermissionLevel';
77
import { useActiveGroup } from '../../../hooks/groups/useActiveGroup';
@@ -11,7 +11,6 @@ import { MemberInfo } from '../../../hooks/groups/useMemberInfoForGroup';
1111
export default function MemberDetail({ member }: { member: MemberInfo }) {
1212
const activeGroup = useActiveGroup();
1313
const { removeMemberFromGroup, updateMemberPermissions } = useGroupActions();
14-
const notifications = useNotificationSystem();
1514
const {
1615
firebaseUser: { uid: userId },
1716
} = useContext(UserDataContext);
@@ -96,13 +95,11 @@ export default function MemberDetail({ member }: { member: MemberInfo }) {
9695
) {
9796
removeMemberFromGroup(activeGroup.activeGroupId, member.uid)
9897
.then(() =>
99-
notifications.addNotification({
100-
level: 'success',
101-
message:
102-
'This member has been successfully removed from the group.',
103-
})
98+
toast.success(
99+
'This member has been successfully removed from the group.'
100+
)
104101
)
105-
.catch(notifications.showErrorNotification);
102+
.catch(e => toast.error(e));
106103
}
107104
}}
108105
>

src/components/Groups/PostLeaderboardPage/PostLeaderboardPage.tsx

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { doc, getDoc, getFirestore } from 'firebase/firestore';
22
import React from 'react';
3-
import { useNotificationSystem } from '../../../context/NotificationSystemContext';
3+
import toast from 'react-hot-toast';
44
import { useActiveGroup } from '../../../hooks/groups/useActiveGroup';
55
import { useActivePostProblems } from '../../../hooks/groups/useActivePostProblems';
66
import useLeaderboardData from '../../../hooks/groups/useLeaderboardData';
@@ -24,7 +24,6 @@ export default function PostLeaderboardPage(props) {
2424
const post = usePost(postId);
2525
const { problems } = useActivePostProblems();
2626
const firebaseApp = useFirebaseApp();
27-
const notifications = useNotificationSystem();
2827
const leaderboard = useLeaderboardData({
2928
groupId: activeGroup.activeGroupId,
3029
postId: postId,
@@ -54,10 +53,7 @@ export default function PostLeaderboardPage(props) {
5453
openProblemSubmissionPopup(submission);
5554
})
5655
.catch(e => {
57-
notifications.addNotification({
58-
level: 'error',
59-
message: "Couldn't get submission: " + e.message,
60-
});
56+
toast.error("Couldn't get submission: " + e.message);
6157
});
6258
};
6359

src/components/Groups/ProblemPage/ProblemPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import dayjs from 'dayjs';
22
import { Link, navigate } from 'gatsby';
3-
import * as React from 'react';
4-
import { useNotificationSystem } from '../../../context/NotificationSystemContext';
3+
import React from 'react';
4+
import toast from "react-hot-toast";
55
import { useActiveGroup } from '../../../hooks/groups/useActiveGroup';
66
import { usePost } from '../../../hooks/groups/usePost';
77
import { usePostActions } from '../../../hooks/groups/usePostActions';
@@ -26,7 +26,6 @@ export default function ProblemPage(props) {
2626
const post = usePost(postId);
2727
const problem = useProblem(problemId);
2828
const { deleteProblem } = usePostActions(activeGroup.groupData?.id);
29-
const notifications = useNotificationSystem();
3029

3130
if (!problem || post.type !== 'assignment' || activeGroup.isLoading) {
3231
return null;
@@ -78,7 +77,7 @@ export default function ProblemPage(props) {
7877
}
7978
);
8079
})
81-
.catch(e => notifications.showErrorNotification(e));
80+
.catch(e => toast.error(e.message));
8281
}
8382
}}
8483
className="btn"

0 commit comments

Comments
 (0)