Skip to content
This repository was archived by the owner on Nov 8, 2022. It is now read-only.

Commit 1b9ae17

Browse files
authored
feat(article-emotions): emotions for article (#348)
* feat(article-emotions): setup && re-org config * refactor(article-emotions): wip * refactor(article-emotions): wip * refactor(article-emotions): wip * refactor(article-emotions): wip * refactor(article-emotions): support job * refactor(article-emotions): mutation for post/job * refactor(article-emotions): fix broken test * refactor(article-emotions): fix broken test
1 parent 9b5139b commit 1b9ae17

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1121
-156
lines changed

config/config.exs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,34 @@ config :groupher_server, :customization,
6060
display_density: "20",
6161
sidebar_communities_index: %{}
6262

63+
config :groupher_server, :article,
64+
emotionable_threads: [:post, :job],
65+
# NOTE: if you want to add/remove emotion, just edit the list below
66+
# and migrate the field to table "articles_users_emotions"
67+
supported_emotions: [
68+
:upvote,
69+
:downvote,
70+
:beer,
71+
:heart,
72+
:biceps,
73+
:orz,
74+
:confused,
75+
:pill,
76+
:popcorn
77+
],
78+
# NOTE: if you want to add/remove emotion, just edit the list below
79+
# and migrate the field to table "articles_comments_users_emotions"
80+
comment_supported_emotions: [
81+
:downvote,
82+
:beer,
83+
:heart,
84+
:biceps,
85+
:orz,
86+
:confused,
87+
:pill,
88+
:popcorn
89+
]
90+
6391
config :groupher_server, GroupherServerWeb.Gettext, default_locale: "zh_CN", locales: ~w(en zh_CN)
6492

6593
config :groupher_server, :cloud_assets,

lib/groupher_server/cms/article_comment.ex

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ defmodule GroupherServer.CMS.ArticleComment do
2525
@max_participator_count 5
2626
@max_parent_replies_count 3
2727

28-
# NOTE: if you want to add/remove emotion, just edit the list below
29-
# and migrate the field to table "articles_comments_users_emotions"
30-
@supported_emotions [:downvote, :beer, :heart, :biceps, :orz, :confused, :pill, :popcorn]
3128
@max_latest_emotion_users_count 5
3229

3330
@delete_hint "this comment is deleted"

lib/groupher_server/cms/article_comment_user_emotion.ex

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
defmodule GroupherServer.CMS.ArticleCommentUserEmotion.Macros do
2-
alias GroupherServer.CMS
3-
alias CMS.ArticleComment
2+
import Helper.Utils, only: [get_config: 2]
43

5-
@supported_emotions ArticleComment.supported_emotions()
4+
@supported_emotions get_config(:article, :comment_supported_emotions)
65

76
defmacro emotion_fields() do
87
@supported_emotions
@@ -21,14 +20,14 @@ defmodule GroupherServer.CMS.ArticleCommentUserEmotion do
2120
use Ecto.Schema
2221
import Ecto.Changeset
2322
import GroupherServer.CMS.ArticleCommentUserEmotion.Macros
23+
import Helper.Utils, only: [get_config: 2]
2424

2525
alias GroupherServer.{Accounts, CMS}
2626
alias CMS.ArticleComment
2727

28-
@supported_emotions ArticleComment.supported_emotions()
28+
@supported_emotions get_config(:article, :comment_supported_emotions)
2929

3030
@required_fields ~w(article_comment_id user_id recived_user_id)a
31-
# @optional_fields ~w(downvote beer heart biceps orz confused pill)a
3231
@optional_fields Enum.map(@supported_emotions, &:"#{&1}")
3332

3433
@type t :: %ArticleCommentUserEmotion{}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
defmodule GroupherServer.CMS.ArticleUserEmotion.Macros do
2+
import Helper.Utils, only: [get_config: 2]
3+
4+
alias GroupherServer.CMS
5+
6+
@supported_emotions get_config(:article, :supported_emotions)
7+
8+
defmacro emotion_fields() do
9+
@supported_emotions
10+
|> Enum.map(fn emotion ->
11+
quote do
12+
field(unquote(:"#{emotion}"), :boolean, default: false)
13+
end
14+
end)
15+
end
16+
end
17+
18+
defmodule GroupherServer.CMS.ArticleUserEmotion do
19+
@moduledoc false
20+
alias __MODULE__
21+
22+
use Ecto.Schema
23+
import Ecto.Changeset
24+
import GroupherServer.CMS.ArticleUserEmotion.Macros
25+
import Helper.Utils, only: [get_config: 2]
26+
27+
alias GroupherServer.{Accounts, CMS}
28+
alias CMS.{Post, Job}
29+
30+
@supported_emotions get_config(:article, :supported_emotions)
31+
@supported_threads get_config(:article, :emotionable_threads)
32+
33+
@required_fields ~w(user_id recived_user_id)a
34+
@optional_fields Enum.map(@supported_threads, &:"#{&1}_id") ++
35+
Enum.map(@supported_emotions, &:"#{&1}")
36+
37+
@type t :: %ArticleUserEmotion{}
38+
schema "articles_users_emotions" do
39+
belongs_to(:post, Post, foreign_key: :post_id)
40+
belongs_to(:job, Job, foreign_key: :job_id)
41+
belongs_to(:recived_user, Accounts.User, foreign_key: :recived_user_id)
42+
belongs_to(:user, Accounts.User, foreign_key: :user_id)
43+
44+
emotion_fields()
45+
timestamps(type: :utc_datetime)
46+
end
47+
48+
@doc false
49+
def changeset(%ArticleUserEmotion{} = struct, attrs) do
50+
struct
51+
|> cast(attrs, @required_fields ++ @optional_fields)
52+
|> validate_required(@required_fields)
53+
|> foreign_key_constraint(:post_id)
54+
|> foreign_key_constraint(:job_id)
55+
|> foreign_key_constraint(:user_id)
56+
|> foreign_key_constraint(:recived_user_id)
57+
end
58+
59+
def update_changeset(%ArticleUserEmotion{} = struct, attrs) do
60+
struct
61+
|> cast(attrs, @required_fields ++ @optional_fields)
62+
|> validate_required(@required_fields)
63+
|> foreign_key_constraint(:post_id)
64+
|> foreign_key_constraint(:job_id)
65+
|> foreign_key_constraint(:user_id)
66+
|> foreign_key_constraint(:recived_user_id)
67+
end
68+
end

lib/groupher_server/cms/author.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ defmodule GroupherServer.CMS.Author do
77

88
import Ecto.Changeset
99

10-
alias GroupherServer.{Accounts, CMS}
11-
alias CMS.Post
10+
alias GroupherServer.Accounts
11+
# alias CMS.Post
1212

1313
@type t :: %Author{}
1414

1515
schema "cms_authors" do
1616
field(:role, :string)
1717
# field(:user_id, :id)
18-
has_many(:posts, Post)
18+
# has_many(:posts, Post)
1919
# user_id filed in own-table
2020
belongs_to(:user, Accounts.User)
2121

lib/groupher_server/cms/cms.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ defmodule GroupherServer.CMS do
1111
AbuseReport,
1212
ArticleCURD,
1313
ArticleOperation,
14+
ArticleEmotion,
1415
ArticleReaction,
1516
ArticleComment,
1617
ArticleCommentAction,
@@ -111,6 +112,9 @@ defmodule GroupherServer.CMS do
111112
defdelegate set_community(community, thread, content_id), to: ArticleOperation
112113
defdelegate unset_community(community, thread, content_id), to: ArticleOperation
113114

115+
defdelegate emotion_to_article(thread, article_id, args, user), to: ArticleEmotion
116+
defdelegate undo_emotion_to_article(thread, article_id, args, user), to: ArticleEmotion
117+
114118
# Comment CURD
115119
defdelegate list_article_comments(thread, article_id, filters, mode), to: ArticleComment
116120
defdelegate list_article_comments(thread, article_id, filters, mode, user), to: ArticleComment

lib/groupher_server/cms/delegates/article_comment.ex

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
33
CURD and operations for article comments
44
"""
55
import Ecto.Query, warn: false
6-
import Helper.Utils, only: [done: 1]
6+
import Helper.Utils, only: [done: 1, get_config: 2]
77
import Helper.ErrorCode
88

9+
import GroupherServer.CMS.Delegate.Helper, only: [mark_viewer_emotion_states: 3]
910
import GroupherServer.CMS.Helper.Matcher2
1011
import ShortMaps
1112

@@ -17,9 +18,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
1718
alias CMS.{ArticleComment, ArticlePinedComment, Embeds}
1819
alias Ecto.Multi
1920

21+
@supported_emotions get_config(:article, :comment_supported_emotions)
2022
@max_participator_count ArticleComment.max_participator_count()
2123
@default_emotions Embeds.ArticleCommentEmotion.default_emotions()
22-
@supported_emotions ArticleComment.supported_emotions()
2324
@delete_hint ArticleComment.delete_hint()
2425

2526
@default_comment_meta Embeds.ArticleCommentMeta.default_meta()
@@ -215,8 +216,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
215216
# |> QueryBuilder.filter_pack(Map.merge(filters, %{sort: :asc_inserted}))
216217
|> QueryBuilder.filter_pack(Map.merge(filters, %{sort: sort}))
217218
|> ORM.paginater(~m(page size)a)
218-
|> set_viewer_emotion_ifneed(user)
219219
|> add_pined_comments_ifneed(thread, article_id, filters)
220+
|> mark_viewer_emotion_states(user, :comment)
220221
|> mark_viewer_has_upvoted(user)
221222
|> done()
222223
end
@@ -233,7 +234,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
233234
|> where(^where_query)
234235
|> QueryBuilder.filter_pack(filters)
235236
|> ORM.paginater(~m(page size)a)
236-
|> set_viewer_emotion_ifneed(user)
237+
|> mark_viewer_emotion_states(user, :comment)
237238
|> mark_viewer_has_upvoted(user)
238239
|> done()
239240
end
@@ -273,30 +274,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
273274

274275
defp add_pined_comments_ifneed(paged_comments, _thread, _article_id, _), do: paged_comments
275276

276-
defp user_in_logins?([], _), do: false
277-
defp user_in_logins?(ids_list, %User{login: login}), do: Enum.member?(ids_list, login)
278-
279-
defp set_viewer_emotion_ifneed(paged_comments, nil), do: paged_comments
280-
defp set_viewer_emotion_ifneed(%{entries: []} = paged_comments, _), do: paged_comments
281-
282-
defp set_viewer_emotion_ifneed(%{entries: entries} = paged_comments, %User{} = user) do
283-
new_entries =
284-
Enum.map(entries, fn comment ->
285-
update_viewed_status =
286-
@supported_emotions
287-
|> Enum.reduce([], fn emotion, acc ->
288-
already_emotioned = user_in_logins?(comment.emotions[:"#{emotion}_user_logins"], user)
289-
acc ++ ["viewer_has_#{emotion}ed": already_emotioned]
290-
end)
291-
|> Enum.into(%{})
292-
293-
updated_emotions = Map.merge(comment.emotions, update_viewed_status)
294-
Map.put(comment, :emotions, updated_emotions)
295-
end)
296-
297-
%{paged_comments | entries: new_entries}
298-
end
299-
300277
defp mark_viewer_has_upvoted(paged_comments, nil), do: paged_comments
301278

302279
defp mark_viewer_has_upvoted(%{entries: entries} = paged_comments, %User{} = user) do

lib/groupher_server/cms/delegates/article_comment_emotion.ex

Lines changed: 26 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentEmotion do
44
"""
55
import Ecto.Query, warn: false
66

7+
import GroupherServer.CMS.Delegate.Helper, only: [update_emotions_field: 4]
8+
79
alias Helper.ORM
810
alias GroupherServer.{Accounts, CMS, Repo}
911

@@ -15,8 +17,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentEmotion do
1517
@type t_user_list :: [%{login: String.t()}]
1618
@type t_mention_status :: %{user_list: t_user_list, user_count: Integer.t()}
1719

18-
@max_latest_emotion_users_count ArticleComment.max_latest_emotion_users_count()
19-
2020
@doc "make emotion to a comment"
2121
def emotion_to_comment(comment_id, emotion, %User{} = user) do
2222
with {:ok, comment} <- ORM.find(ArticleComment, comment_id, preload: :author) do
@@ -31,18 +31,18 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentEmotion do
3131
args = Map.put(target, :"#{emotion}", true)
3232

3333
case ORM.find_by(ArticleCommentUserEmotion, target) do
34-
{:ok, article_comment_user_emotion} -> article_comment_user_emotion |> ORM.update(args)
35-
{:error, _} -> ArticleCommentUserEmotion |> ORM.create(args)
34+
{:ok, article_comment_user_emotion} -> ORM.update(article_comment_user_emotion, args)
35+
{:error, _} -> ORM.create(ArticleCommentUserEmotion, args)
3636
end
3737
end)
38-
|> Multi.run(:query_emotion_status, fn _, _ ->
39-
query_emotion_status(comment, emotion)
38+
|> Multi.run(:query_emotion_states, fn _, _ ->
39+
query_emotion_states(comment, emotion)
4040
end)
41-
|> Multi.run(:update_comment_emotion, fn _, %{query_emotion_status: status} ->
42-
update_comment_emotion(comment, emotion, status, user)
41+
|> Multi.run(:update_emotions_field, fn _, %{query_emotion_states: status} ->
42+
update_emotions_field(comment, emotion, status, user)
4343
end)
4444
|> Repo.transaction()
45-
|> update_emotion_result
45+
|> update_emotions_field_result
4646
end
4747
end
4848

@@ -56,23 +56,28 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentEmotion do
5656
user_id: user.id
5757
}
5858

59-
{:ok, article_comment_user_emotion} = ORM.find_by(ArticleCommentUserEmotion, target)
60-
args = Map.put(target, :"#{emotion}", false)
61-
article_comment_user_emotion |> ORM.update(args)
59+
case ORM.find_by(ArticleCommentUserEmotion, target) do
60+
{:ok, article_comment_user_emotion} ->
61+
args = Map.put(target, :"#{emotion}", false)
62+
article_comment_user_emotion |> ORM.update(args)
63+
64+
{:error, _} ->
65+
ORM.create(ArticleCommentUserEmotion, target)
66+
end
6267
end)
63-
|> Multi.run(:query_emotion_status, fn _, _ ->
64-
query_emotion_status(comment, emotion)
68+
|> Multi.run(:query_emotion_states, fn _, _ ->
69+
query_emotion_states(comment, emotion)
6570
end)
66-
|> Multi.run(:update_comment_emotion, fn _, %{query_emotion_status: status} ->
67-
update_comment_emotion(comment, emotion, status, user)
71+
|> Multi.run(:update_emotions_field, fn _, %{query_emotion_states: status} ->
72+
update_emotions_field(comment, emotion, status, user)
6873
end)
6974
|> Repo.transaction()
70-
|> update_emotion_result
75+
|> update_emotions_field_result
7176
end
7277
end
7378

74-
@spec query_emotion_status(ArticleComment.t(), Atom.t()) :: {:ok, t_mention_status}
75-
defp query_emotion_status(comment, emotion) do
79+
@spec query_emotion_states(ArticleComment.t(), Atom.t()) :: {:ok, t_mention_status}
80+
defp query_emotion_states(comment, emotion) do
7681
# 每次被 emotion 动作触发后重新查询,主要原因
7782
# 1.并发下保证数据准确,类似 views 阅读数的统计
7883
# 2. 前端使用 nickname 而非 login 展示,如果用户改了 nickname, 可以"自动纠正"
@@ -91,41 +96,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentEmotion do
9196
{:ok, %{user_list: emotioned_user_info_list, user_count: emotioned_user_count}}
9297
end
9398

94-
@spec update_comment_emotion(ArticleComment.t(), Atom.t(), t_mention_status, User.t()) ::
95-
{:ok, ArticleComment.t()} | {:error, any}
96-
defp update_comment_emotion(comment, emotion, status, user) do
97-
%{user_count: user_count, user_list: user_list} = status
98-
99-
emotions =
100-
%{}
101-
|> Map.put(:"#{emotion}_count", user_count)
102-
|> Map.put(:"#{emotion}_user_logins", user_list |> Enum.map(& &1.login))
103-
|> Map.put(
104-
:"latest_#{emotion}_users",
105-
Enum.slice(user_list, 0, @max_latest_emotion_users_count)
106-
)
107-
108-
viewer_has_emotioned = user.login in Map.get(emotions, :"#{emotion}_user_logins")
109-
emotions = emotions |> Map.put(:"viewer_has_#{emotion}ed", viewer_has_emotioned)
110-
111-
comment
112-
|> Ecto.Changeset.change()
113-
|> Ecto.Changeset.put_embed(:emotions, emotions)
114-
|> Repo.update()
115-
# virtual field can not be updated
116-
|> add_viewer_emotioned_ifneed(emotions)
117-
end
118-
119-
defp add_viewer_emotioned_ifneed({:error, error}, _), do: {:error, error}
120-
121-
defp add_viewer_emotioned_ifneed({:ok, comment}, emotions) do
122-
# Map.merge(comment, %{emotion: emotions})
123-
{:ok, Map.merge(comment, %{emotion: emotions})}
124-
end
125-
126-
defp update_emotion_result({:ok, %{update_comment_emotion: result}}), do: {:ok, result}
99+
defp update_emotions_field_result({:ok, %{update_emotions_field: result}}), do: {:ok, result}
127100

128-
defp update_emotion_result({:error, _, result, _steps}) do
101+
defp update_emotions_field_result({:error, _, result, _steps}) do
129102
{:error, result}
130103
end
131104
end

0 commit comments

Comments
 (0)