Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Order comments refresh n state read #1048

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
23 changes: 21 additions & 2 deletions lib/kalda/forums.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1198,10 +1198,29 @@ defmodule Kalda.Forums do
end
end

def get_notification!(id) do
def get_notification!(id, opts \\ []) do
preload = opts[:preload] || []

from(n in Notification,
where: n.id == ^id
where: n.id == ^id,
preload: ^preload
)
|> Repo.get!(id)
end

@doc """
Updates a notification, raises error if notification not found.

## Examples

iex> update_notification!(notification, %{field: new_value})
%Notification{}


"""
def update_notification!(%Notification{} = notification, attrs) do
notification
|> Notification.changeset(attrs)
|> Repo.update!()
end
end
24 changes: 24 additions & 0 deletions lib/kalda_web/controllers/api/v1/post_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ defmodule KaldaWeb.Api.V1.PostController do

alias Kalda.Forums

@doc """
This 'show' shows the single post to the user when they click on a notification
"""

def show(conn, %{"id" => id}) do
post = Forums.get_post_order_preloads!(id)

Expand All @@ -11,4 +15,24 @@ defmodule KaldaWeb.Api.V1.PostController do
|> render("show.json", post: post)
|> KaldaWeb.Api.V1.handle_error(conn)
end

@doc """
The post that this notification id references is the parent post for a **comment-notification**!
"""
def show_read(conn, %{"id" => notification_id}) do
notification = Forums.get_notification!(notification_id, preload: [:comment])

post = Forums.get_post_order_preloads!(notification.comment.post_id)

# return post
# update notification
# front end refreshes notification

Forums.update_notification!(notification, %{read: true})

conn
|> put_status(200)
|> render("show.json", post: post)
|> KaldaWeb.Api.V1.handle_error(conn)
end
end
4 changes: 4 additions & 0 deletions lib/kalda_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ defmodule KaldaWeb.Router do
:json_require_subscribed_user
]

# get "/posts/:post_id/comments/:comment_id", PostController, :show_comment_notification
get "/posts/:notifications/:id", PostController, :show_read
get "/posts/:id", PostController, :show
post "/posts/:id/comments", CommentController, :create
post "/comments/:id/replies", ReplyController, :create
Expand Down Expand Up @@ -162,6 +164,8 @@ defmodule KaldaWeb.Router do
:json_require_subscribed_user
]

# get "/posts/:post_id/comments/:comment_id", PostController, :show_comment_notification
get "/posts/:notifications/:id", PostController, :show_read
get "/posts/:id", PostController, :show
post "/posts/:id/comments", CommentController, :create
post "/comments/:id/replies", ReplyController, :create
Expand Down
1 change: 1 addition & 0 deletions lib/kalda_web/views/api/v1/notification_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule KaldaWeb.Api.V1.NotificationView do

def render_comment_notification(notification) do
%{
notification_id: notification.id,
parent_post_id: notification.comment.post_id,
comment_id: notification.comment_id,
comment_content: notification.comment.content,
Expand Down
17 changes: 13 additions & 4 deletions spa/src/Notifications.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<script lang="ts">
// import type { CommentNotification } from "./state";
import type { AppState, CommentNotification } from "./state";
import { link } from "svelte-routing";
import { truncate } from "./functions";
// import type { ApiClient } from "./backend";

export let state: AppState;
// export let notifications: CommentNotification[];
// export let api: ApiClient;
// let state = api.getInitialAppState();
let notifications: Array<CommentNotification> = state.commentNotifications;
</script>

Expand All @@ -14,11 +18,16 @@
<p>
<span class="author">{notification.replyAuthor.username}</span>
<span class="italic"> replied to your comment: </span>
"{notification.commentContent}"
"{truncate(notification.commentContent)}"
</p>
<p>
<span class="italic">with: </span>
"{notification.replyContent}."
"{truncate(notification.replyContent)}."
<span class="underline">
<a use:link href="/posts/{notification.parentPostId}">
<a
use:link
href="/posts/notifications/{notification.notificationId}"
>
See the whole thread
</a>
</span>
Expand Down
56 changes: 43 additions & 13 deletions spa/src/Router.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,52 @@
import Notifications from "./Notifications.svelte";
import { Router, Route } from "svelte-routing";
import type { Stripe } from "./stripe";
import type { AppState, Post } from "./state";
import type { AppState, CommentNotification, Post } from "./state";
import type { ApiClient } from "./backend";
import {
scheduleDailyReflectionNotifications,
scheduleTherapyNotifications,
} from "./local-notification";
import { reorderComments } from "./functions";

export let state: AppState;
export let api: ApiClient;
export let stripe: Promise<Stripe>;

let updatedState = api.getInitialAppState();

scheduleDailyReflectionNotifications();
scheduleTherapyNotifications(state.therapies);

async function getPostById(
async function getPostByNotificationId(
state: AppState,
paramId: string
paramNotificationId: string
): Promise<Post | undefined> {
let id: number = parseInt(paramId);
// Look for the post in the daily reflections
let foundPost = state.reflections.find((post) => post.id == id);
if (foundPost) return foundPost;
let notificationId: number = parseInt(paramNotificationId);

// We don't have this post yet so get it from the API
let response = await api.getPostState(id);
if (response.type === "Success") return response.resource;
let notification = state.commentNotifications.find(
(notification: CommentNotification) =>
notification.notificationId == notificationId
);
let commentId = notification?.commentId;
let postId = notification?.parentPostId;

// Look for the post in the daily reflections
let foundPost = state.reflections.find((post) => post.id == postId);

if (foundPost && commentId) {
// REORDER THE COMMENTS
return reorderComments(foundPost, commentId);
}
// We don't have this post yet so get it from the API, with reordered comments
if (postId && commentId) {
let response = await api.getPostState(postId, commentId);
if (response.type === "Success") {
let responsePost = response.resource;
// REORDER THE COMMENTS
return reorderComments(responsePost, commentId);
}
}
}
</script>

Expand Down Expand Up @@ -109,14 +129,24 @@
{/if}
</Route>

<!-- Update state for notifications page to account for marking as 'read: true' -->
<Route path="notifications">
<Navbar title="Notifications" {state} />
<Notifications {state} />
{#await updatedState}
<Loading />
{:then response}
{#if response.type === "Success"}
<Notifications state={response.resource} />
{:else}
<Notifications {state} />
{/if}
{/await}
</Route>

<Route path="posts/:id" let:params>
<!-- TODO: getting post by notification to also return updated notification state? -->
<Route path="posts/notifications/:notificationId" let:params>
<Navbar title="Notification" {state} />
{#await getPostById(state, params.id)}
{#await getPostByNotificationId(state, params.notificationId)}
<Loading />
{:then post}
{#if post}
Expand Down
6 changes: 3 additions & 3 deletions spa/src/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export interface ApiClient {

getStripePaymentIntent(): Promise<Response<StripePaymentIntent>>;

getPostState(postId: number): Promise<Response<Post>>;
getPostState(postId: number, commentId: number): Promise<Response<Post>>;
}

// An implementation of ApiClient that actually connects to the backend.
Expand Down Expand Up @@ -195,7 +195,7 @@ export class AuthenticatedApiClient implements ApiClient {
.request(stripePaymentIntent);
}

async getPostState(postId: number): Promise<Response<Post>> {
async getPostState(postId: number, commentId: number): Promise<Response<Post>> {
let url = this.route(`/v1/token/posts/${postId}`)
return await this.httpClient.get(url).expect(200).request(post);
}
Expand Down Expand Up @@ -244,7 +244,7 @@ export class MockApiClient implements ApiClient {
getStripePaymentIntent(): Promise<Response<StripePaymentIntent>> {
throw new Error("Method not implemented.");
}
getPostState(postId: number): Promise<Response<Post>> {
getPostState(postId: number, commentId: number): Promise<Response<Post>> {
throw new Error("Method not implemented.");
}
}
Expand Down
1 change: 1 addition & 0 deletions spa/src/backend/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export function therapy(json: unknown): Therapy {

export function commentNotification(json: unknown): CommentNotification {
return {
notificationId: field("notification_id", number)(json),
parentPostId: field("parent_post_id", number)(json),
commentContent: field("comment_content", string)(json),
commentId: field("comment_id", number)(json),
Expand Down
35 changes: 35 additions & 0 deletions spa/src/functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { Post, Comment } from "./state"

export function reorderComments(post: Post, commentId: number): Post {
// REORDER THE COMMENTS
let postFirstComment = post.comments.find(
(comment) => comment.id == commentId
);

if (postFirstComment) {
let otherComments = post.comments.filter(
(comment: Comment) => comment.id != commentId
);

let resultComments = [postFirstComment, ...otherComments];

let orderedPost: Post = {
id: post.id,
content: post.content,
author: post.author,
comments: resultComments,
};
return orderedPost;
} else {
return post
}
}


export function truncate(content: string): string {
if (content.length > 30) {
return content.substring(0, 30) + "...";
} else {
return content;
}
}
2 changes: 2 additions & 0 deletions spa/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export type Notifications = {
};

export type CommentNotification = {
notificationId: number,
parentPostId: number,
commentContent: string;
commentId: number;
Expand All @@ -95,6 +96,7 @@ export type CommentNotification = {
};

export type PostNotification = {
notificationId: number,
parentPostId: number;
postContent: string;
insertedAt: Date;
Expand Down
13 changes: 13 additions & 0 deletions test/kalda/forums_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,7 @@ defmodule Kalda.ForumsTest do
alias Kalda.Forums.Notification

@valid_rn_notification %{read: false, expired: false}
@update_n_attrs %{read: true}

# TODO: Creating a reply or reply_fixture should always create a notification???

Expand Down Expand Up @@ -1150,5 +1151,17 @@ defmodule Kalda.ForumsTest do
assert {:error, %Ecto.Changeset{}} =
Forums.create_reply_with_notification(user, comment, @valid_reply_attrs)
end

test "update_notification1/2 with valid data updates the notification" do
user = AccountsFixtures.user()
post = ForumsFixtures.post(user)
comment = ForumsFixtures.comment(post, user)
{_reply, notification} = ForumsFixtures.reply_with_notification(comment, user)

assert %Notification{} =
notification = Forums.update_notification!(notification, @update_n_attrs)

assert notification.read == true
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ defmodule KaldaWeb.Api.V1.NotificationControllerTest do

comment1 = ForumsFixtures.comment(post1, current_user)

{reply1, _notification1} = ForumsFixtures.reply_with_notification(comment1, reply_auth1)
{reply1, notification1} = ForumsFixtures.reply_with_notification(comment1, reply_auth1)

{reply2, _notification2} = ForumsFixtures.reply_with_notification(comment1, reply_auth2)
{reply2, notification2} = ForumsFixtures.reply_with_notification(comment1, reply_auth2)

conn = get(conn, "/v1/notifications")

Expand All @@ -50,6 +50,7 @@ defmodule KaldaWeb.Api.V1.NotificationControllerTest do
"post_notifications" => nil,
"comment_notifications" => [
%{
"notification_id" => notification1.id,
"parent_post_id" => post1.id,
"comment_id" => comment1.id,
"comment_content" => comment1.content,
Expand All @@ -62,6 +63,7 @@ defmodule KaldaWeb.Api.V1.NotificationControllerTest do
}
},
%{
"notification_id" => notification2.id,
"parent_post_id" => post1.id,
"comment_id" => comment1.id,
"comment_content" => comment1.content,
Expand Down Expand Up @@ -93,9 +95,9 @@ defmodule KaldaWeb.Api.V1.NotificationControllerTest do

comment1 = ForumsFixtures.comment(post1, current_user)

{reply1, _notification1} = ForumsFixtures.reply_with_notification(comment1, reply_auth1)
{reply1, notification1} = ForumsFixtures.reply_with_notification(comment1, reply_auth1)

{reply2, _notification2} = ForumsFixtures.reply_with_notification(comment1, reply_auth2)
{reply2, notification2} = ForumsFixtures.reply_with_notification(comment1, reply_auth2)

user2 = AccountsFixtures.user()
comment2 = ForumsFixtures.comment(post1, user2)
Expand All @@ -113,6 +115,7 @@ defmodule KaldaWeb.Api.V1.NotificationControllerTest do
"post_notifications" => nil,
"comment_notifications" => [
%{
"notification_id" => notification1.id,
"parent_post_id" => post1.id,
"comment_id" => comment1.id,
"comment_content" => comment1.content,
Expand All @@ -125,6 +128,7 @@ defmodule KaldaWeb.Api.V1.NotificationControllerTest do
}
},
%{
"notification_id" => notification2.id,
"parent_post_id" => post1.id,
"comment_id" => comment1.id,
"comment_content" => comment1.content,
Expand Down
Loading