fix: move feed state to a store and create feed loading UI (#189)

* style: Improved font size of the answers in the FAQ section of the landing page

* feat: move all email to defer queue (#186)

* fix: use relative part for defer func #185

* chore: move sendEmail into defer

* fix: remove anon function to defer #185

* feat: implement batch email sending with defer.run #185

* chore: debug sendEmails #185

* chore: add robots.txt

* chore: use promise.all in sendEmails

* chore: debug defer sendEmails #185

* chore: try defer sendEmail without loop #105

* chore: comment code

* chore: change loop style #185

* chore: use promise.all for vercel edge #185

* Moved the feed state to a store and added a loader

* feat: add await to sendEmailNotification #185

* chore: use sendEmails #185

* fix: debug sendEmails #185

* fix: email not updating in LMS

* fix: remove early return in sendEmails #185

* fixed the edit bug and moved the loader state to the file

* added a loading state for the post button

* change isDisabled to isLoading

---------

Co-authored-by: Rotimi Best <[email protected]>
This commit is contained in:
Em_Dev
2024-03-01 23:40:20 +01:00
committed by GitHub
co-authored by Rotimi Best
parent bae6163f18
commit 805575356d
4 changed files with 104 additions and 87 deletions
@@ -20,68 +20,65 @@
export let editFeed: Feed;
let newPost = '';
let isLoading = false;
let createdFeed;
const onPost = async () => {
if (!newPost) return;
// Edit state
if (edit) {
onEdit(editFeed.id, newPost);
snackbar.success('Feed edited successfully');
edit = false;
newPost = '';
$isNewFeedModal.open = false;
return;
}
// Create state
isLoading = true;
try {
const {
response: { data }
} = await createNewFeed({
content: newPost,
author_id: author.id,
course_id: courseId,
reaction: {
smile: [],
thumbsup: [],
thumbsdown: [],
clap: []
}
});
if (edit) {
onEdit(editFeed.id, newPost);
snackbar.success('Feed edited successfully');
if (!data) return;
edit = false;
newPost = '';
$isNewFeedModal.open = false;
} else {
const {
response: { data }
} = await createNewFeed({
content: newPost,
author_id: author.id,
course_id: courseId,
reaction: {
smile: [],
thumbsup: [],
thumbsdown: [],
clap: []
}
});
createdFeed = data[0];
if (!data) return;
createdFeed = data[0];
if (!createdFeed) return;
onSave({
id: createdFeed.id,
content: newPost,
author: {
profile: { ...author }
},
created_at: createdFeed.created_at,
comment: [],
reaction: createdFeed.reaction,
isPinned: false
});
snackbar.success('New feed added successfully');
triggerSendEmail(NOTIFICATION_NAME.NEWSFEED, {
authorId: createdFeed.author_id,
feedId: createdFeed.id
});
resetEditor();
}
} catch (error) {
return snackbar.error('An error occurred while creating feed');
snackbar.error('An error occurred while ' + (edit ? 'editing feed' : 'creating feed'));
} finally {
isLoading = false;
}
if (!createdFeed) return;
onSave({
id: createdFeed.id,
content: newPost,
author: {
profile: { ...author }
},
created_at: createdFeed.created_at,
comment: [],
reaction: createdFeed.reaction,
isPinned: false
});
snackbar.success('New feed added successfully');
triggerSendEmail(NOTIFICATION_NAME.NEWSFEED, {
authorId: createdFeed.author_id,
feedId: createdFeed.id
});
resetEditor();
};
const resetEditor = () => {
@@ -110,12 +107,8 @@
/>
<div class="flex items-center justify-end py-2">
<div class="flex gap-2">
<PrimaryButton
label="Cancel"
variant={VARIANTS.OUTLINED}
onClick={() => ($isNewFeedModal.open = false)}
/>
<PrimaryButton label="Post" onClick={onPost} />
<PrimaryButton label="Cancel" variant={VARIANTS.OUTLINED} onClick={resetEditor} />
<PrimaryButton {isLoading} label="Post" onClick={onPost} />
</div>
</div>
</section>
@@ -0,0 +1,11 @@
<script>
import { SkeletonPlaceholder, SkeletonText } from 'carbon-components-svelte';
</script>
<div class="flex flex-col gap-5 border border-gray-300 p-4 rounded-md mb-7 max-w-3xl">
<div class="flex items-center gap-2">
<SkeletonPlaceholder style="height: 2rem; width: 2rem;" class="rounded-full overflow-hidden" />
<SkeletonText width="30%" />
</div>
<SkeletonText paragraph lines={3} />
</div>
@@ -1,5 +1,8 @@
import type { Feed } from '$lib/utils/types/feed';
import { writable } from 'svelte/store';
export const isNewFeedModal = writable({
open: false
});
export const newsFeed = writable<Feed[]>([]);
@@ -20,6 +20,7 @@
toggleFeedIsPinned,
fetchNewsFeedReaction
} from '$lib/utils/services/newsfeed';
import { newsFeed } from '$lib/components/Course/components/NewsFeed/store';
import Box from '$lib/components/Box/index.svelte';
import { supabase } from '$lib/utils/functions/supabase';
import { snackbar } from '$lib/components/Snackbar/store';
@@ -28,11 +29,12 @@
NOTIFICATION_NAME,
triggerSendEmail
} from '$lib/utils/services/notification/notification';
import NewsFeedLoader from '$lib/components/Course/components/NewsFeed/NewsFeedLoader.svelte';
export let data;
let createdComment;
let edit = false;
let isLoading = false;
let editFeed: Feed;
let author = {
id: '',
@@ -40,14 +42,14 @@
fullname: '',
avatar_url: ''
};
let newsFeed: Feed[] = [];
let query = new URLSearchParams($page.url.search);
let feedId = query.get('feedId') || '';
const onEdit = (id: string, content: string) => {
handleEditFeed(id, content);
newsFeed = newsFeed.map((feed) => {
$newsFeed = $newsFeed.map((feed) => {
if (feed.id === id) {
return { ...feed, content: content };
}
@@ -58,7 +60,7 @@
const deleteComment = (id: string) => {
deleteNewsFeedComment(id);
newsFeed = newsFeed.flatMap((feed) => ({
$newsFeed = $newsFeed.flatMap((feed) => ({
...feed,
comment: feed.comment.filter((comment) => comment.id !== id)
}));
@@ -71,7 +73,7 @@
if (!data) return;
const reactedFeed = data || newsFeed.find((feed) => feed.id === feedId);
const reactedFeed = data || $newsFeed.find((feed) => feed.id === feedId);
let reactedAuthorIds: string[] = reactedFeed.reaction[reactionType];
@@ -99,7 +101,7 @@
return snackbar.error('An error occurred while reacting to news feed');
}
newsFeed = newsFeed.map((feed) => {
$newsFeed = $newsFeed.map((feed) => {
if (feed.id === feedId) {
feed.reaction = reactedFeed.reaction;
}
@@ -129,7 +131,7 @@
comment
});
newsFeed = newsFeed.map((feed) => {
$newsFeed = $newsFeed.map((feed) => {
if (feed.id === feedId) {
const newComment = {
id: createdComment.id,
@@ -160,7 +162,7 @@
return snackbar.error('Failed to toggle pinned feed');
}
newsFeed = newsFeed.map((feed) => {
$newsFeed = $newsFeed.map((feed) => {
if (feed.id === feedId) {
snackbar.success(`${newIsPinned ? 'Pinned' : 'Unpinned'} Successfully`);
@@ -175,28 +177,31 @@
const deleteFeed = (id: string) => {
deleteNewsFeed(id);
const deletedFeed = newsFeed.filter((feed) => feed.id !== id);
const deletedFeed = $newsFeed.filter((feed) => feed.id !== id);
snackbar.success('Feed deleted successfully');
newsFeed = deletedFeed;
$newsFeed = deletedFeed;
};
const initNewsFeed = async (courseId: string) => {
if (!courseId) return;
const { data, error } = await fetchNewsFeeds(courseId);
if (error) {
snackbar.error('Failed to fetch news feeds');
return;
try {
isLoading = true;
const { data, error } = await fetchNewsFeeds(courseId);
if (error) {
snackbar.error('Failed to fetch news feeds');
}
if (data) {
$newsFeed = data.map((feedItem) => ({
...feedItem,
isPinned: feedItem.is_pinned
}));
}
} catch (error) {
snackbar.error('An error occured while fetching feed');
} finally {
isLoading = false;
}
if (!data) return;
newsFeed = data.map((feedItem) => ({
...feedItem,
isPinned: feedItem.is_pinned
}));
};
function setAuthor(groups, profileId) {
@@ -214,7 +219,7 @@
$: setAuthor($group, $profile.id);
$: newsFeed = newsFeed.sort((a, b) => Number(b.isPinned) - Number(a.isPinned));
$: $newsFeed = $newsFeed.sort((a, b) => Number(b.isPinned) - Number(a.isPinned));
</script>
<CourseContainer bind:courseId={data.courseId}>
@@ -234,13 +239,18 @@
bind:edit
bind:editFeed
onSave={(newFeed) => {
newsFeed = [newFeed, ...newsFeed];
$newsFeed = [newFeed, ...$newsFeed];
}}
{onEdit}
/>
</RoleBasedSecurity>
{#if !newsFeed.length}
{#if isLoading}
<div>
<NewsFeedLoader />
<NewsFeedLoader />
<NewsFeedLoader />
</div>
{:else if !$newsFeed.length}
<Box>
<div class="flex justify-between flex-col items-center w-[90%] md:w-96">
<img src="/images/empty-lesson-icon.svg" alt="Lesson" class="my-2.5 mx-auto" />
@@ -251,7 +261,7 @@
</div>
</Box>
{:else}
{#each newsFeed as feed}
{#each $newsFeed as feed}
{#if feed.isPinned}
<div class="flex items-center gap-2 mb-3">
<PinFilled size={16} />