Skip to content
This repository was archived by the owner on Nov 8, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,34 @@ config :groupher_server, :customization,
display_density: "20",
sidebar_communities_index: %{}

config :groupher_server, :article,
emotionable_threads: [:post, :job],
# NOTE: if you want to add/remove emotion, just edit the list below
# and migrate the field to table "articles_users_emotions"
supported_emotions: [
:upvote,
:downvote,
:beer,
:heart,
:biceps,
:orz,
:confused,
:pill,
:popcorn
],
# NOTE: if you want to add/remove emotion, just edit the list below
# and migrate the field to table "articles_comments_users_emotions"
comment_supported_emotions: [
:downvote,
:beer,
:heart,
:biceps,
:orz,
:confused,
:pill,
:popcorn
]

config :groupher_server, GroupherServerWeb.Gettext, default_locale: "zh_CN", locales: ~w(en zh_CN)

config :groupher_server, :cloud_assets,
Expand Down
3 changes: 0 additions & 3 deletions lib/groupher_server/cms/article_comment.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ defmodule GroupherServer.CMS.ArticleComment do
@max_participator_count 5
@max_parent_replies_count 3

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

@delete_hint "this comment is deleted"
Expand Down
9 changes: 4 additions & 5 deletions lib/groupher_server/cms/article_comment_user_emotion.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
defmodule GroupherServer.CMS.ArticleCommentUserEmotion.Macros do
alias GroupherServer.CMS
alias CMS.ArticleComment
import Helper.Utils, only: [get_config: 2]

@supported_emotions ArticleComment.supported_emotions()
@supported_emotions get_config(:article, :comment_supported_emotions)

defmacro emotion_fields() do
@supported_emotions
Expand All @@ -21,14 +20,14 @@ defmodule GroupherServer.CMS.ArticleCommentUserEmotion do
use Ecto.Schema
import Ecto.Changeset
import GroupherServer.CMS.ArticleCommentUserEmotion.Macros
import Helper.Utils, only: [get_config: 2]

alias GroupherServer.{Accounts, CMS}
alias CMS.ArticleComment

@supported_emotions ArticleComment.supported_emotions()
@supported_emotions get_config(:article, :comment_supported_emotions)

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

@type t :: %ArticleCommentUserEmotion{}
Expand Down
68 changes: 68 additions & 0 deletions lib/groupher_server/cms/article_user_emotion.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
defmodule GroupherServer.CMS.ArticleUserEmotion.Macros do
import Helper.Utils, only: [get_config: 2]

alias GroupherServer.CMS

@supported_emotions get_config(:article, :supported_emotions)

defmacro emotion_fields() do
@supported_emotions
|> Enum.map(fn emotion ->
quote do
field(unquote(:"#{emotion}"), :boolean, default: false)
end
end)
end
end

defmodule GroupherServer.CMS.ArticleUserEmotion do
@moduledoc false
alias __MODULE__

use Ecto.Schema
import Ecto.Changeset
import GroupherServer.CMS.ArticleUserEmotion.Macros
import Helper.Utils, only: [get_config: 2]

alias GroupherServer.{Accounts, CMS}
alias CMS.{Post, Job}

@supported_emotions get_config(:article, :supported_emotions)
@supported_threads get_config(:article, :emotionable_threads)

@required_fields ~w(user_id recived_user_id)a
@optional_fields Enum.map(@supported_threads, &:"#{&1}_id") ++
Enum.map(@supported_emotions, &:"#{&1}")

@type t :: %ArticleUserEmotion{}
schema "articles_users_emotions" do
belongs_to(:post, Post, foreign_key: :post_id)
belongs_to(:job, Job, foreign_key: :job_id)
belongs_to(:recived_user, Accounts.User, foreign_key: :recived_user_id)
belongs_to(:user, Accounts.User, foreign_key: :user_id)

emotion_fields()
timestamps(type: :utc_datetime)
end

@doc false
def changeset(%ArticleUserEmotion{} = struct, attrs) do
struct
|> cast(attrs, @required_fields ++ @optional_fields)
|> validate_required(@required_fields)
|> foreign_key_constraint(:post_id)
|> foreign_key_constraint(:job_id)
|> foreign_key_constraint(:user_id)
|> foreign_key_constraint(:recived_user_id)
end

def update_changeset(%ArticleUserEmotion{} = struct, attrs) do
struct
|> cast(attrs, @required_fields ++ @optional_fields)
|> validate_required(@required_fields)
|> foreign_key_constraint(:post_id)
|> foreign_key_constraint(:job_id)
|> foreign_key_constraint(:user_id)
|> foreign_key_constraint(:recived_user_id)
end
end
6 changes: 3 additions & 3 deletions lib/groupher_server/cms/author.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ defmodule GroupherServer.CMS.Author do

import Ecto.Changeset

alias GroupherServer.{Accounts, CMS}
alias CMS.Post
alias GroupherServer.Accounts
# alias CMS.Post

@type t :: %Author{}

schema "cms_authors" do
field(:role, :string)
# field(:user_id, :id)
has_many(:posts, Post)
# has_many(:posts, Post)
# user_id filed in own-table
belongs_to(:user, Accounts.User)

Expand Down
4 changes: 4 additions & 0 deletions lib/groupher_server/cms/cms.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule GroupherServer.CMS do
AbuseReport,
ArticleCURD,
ArticleOperation,
ArticleEmotion,
ArticleReaction,
ArticleComment,
ArticleCommentAction,
Expand Down Expand Up @@ -111,6 +112,9 @@ defmodule GroupherServer.CMS do
defdelegate set_community(community, thread, content_id), to: ArticleOperation
defdelegate unset_community(community, thread, content_id), to: ArticleOperation

defdelegate emotion_to_article(thread, article_id, args, user), to: ArticleEmotion
defdelegate undo_emotion_to_article(thread, article_id, args, user), to: ArticleEmotion

# Comment CURD
defdelegate list_article_comments(thread, article_id, filters, mode), to: ArticleComment
defdelegate list_article_comments(thread, article_id, filters, mode, user), to: ArticleComment
Expand Down
33 changes: 5 additions & 28 deletions lib/groupher_server/cms/delegates/article_comment.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
CURD and operations for article comments
"""
import Ecto.Query, warn: false
import Helper.Utils, only: [done: 1]
import Helper.Utils, only: [done: 1, get_config: 2]
import Helper.ErrorCode

import GroupherServer.CMS.Delegate.Helper, only: [mark_viewer_emotion_states: 3]
import GroupherServer.CMS.Helper.Matcher2
import ShortMaps

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

@supported_emotions get_config(:article, :comment_supported_emotions)
@max_participator_count ArticleComment.max_participator_count()
@default_emotions Embeds.ArticleCommentEmotion.default_emotions()
@supported_emotions ArticleComment.supported_emotions()
@delete_hint ArticleComment.delete_hint()

@default_comment_meta Embeds.ArticleCommentMeta.default_meta()
Expand Down Expand Up @@ -215,8 +216,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
# |> QueryBuilder.filter_pack(Map.merge(filters, %{sort: :asc_inserted}))
|> QueryBuilder.filter_pack(Map.merge(filters, %{sort: sort}))
|> ORM.paginater(~m(page size)a)
|> set_viewer_emotion_ifneed(user)
|> add_pined_comments_ifneed(thread, article_id, filters)
|> mark_viewer_emotion_states(user, :comment)
|> mark_viewer_has_upvoted(user)
|> done()
end
Expand All @@ -233,7 +234,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do
|> where(^where_query)
|> QueryBuilder.filter_pack(filters)
|> ORM.paginater(~m(page size)a)
|> set_viewer_emotion_ifneed(user)
|> mark_viewer_emotion_states(user, :comment)
|> mark_viewer_has_upvoted(user)
|> done()
end
Expand Down Expand Up @@ -273,30 +274,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do

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

defp user_in_logins?([], _), do: false
defp user_in_logins?(ids_list, %User{login: login}), do: Enum.member?(ids_list, login)

defp set_viewer_emotion_ifneed(paged_comments, nil), do: paged_comments
defp set_viewer_emotion_ifneed(%{entries: []} = paged_comments, _), do: paged_comments

defp set_viewer_emotion_ifneed(%{entries: entries} = paged_comments, %User{} = user) do
new_entries =
Enum.map(entries, fn comment ->
update_viewed_status =
@supported_emotions
|> Enum.reduce([], fn emotion, acc ->
already_emotioned = user_in_logins?(comment.emotions[:"#{emotion}_user_logins"], user)
acc ++ ["viewer_has_#{emotion}ed": already_emotioned]
end)
|> Enum.into(%{})

updated_emotions = Map.merge(comment.emotions, update_viewed_status)
Map.put(comment, :emotions, updated_emotions)
end)

%{paged_comments | entries: new_entries}
end

defp mark_viewer_has_upvoted(paged_comments, nil), do: paged_comments

defp mark_viewer_has_upvoted(%{entries: entries} = paged_comments, %User{} = user) do
Expand Down
79 changes: 26 additions & 53 deletions lib/groupher_server/cms/delegates/article_comment_emotion.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentEmotion do
"""
import Ecto.Query, warn: false

import GroupherServer.CMS.Delegate.Helper, only: [update_emotions_field: 4]

alias Helper.ORM
alias GroupherServer.{Accounts, CMS, Repo}

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

@max_latest_emotion_users_count ArticleComment.max_latest_emotion_users_count()

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

case ORM.find_by(ArticleCommentUserEmotion, target) do
{:ok, article_comment_user_emotion} -> article_comment_user_emotion |> ORM.update(args)
{:error, _} -> ArticleCommentUserEmotion |> ORM.create(args)
{:ok, article_comment_user_emotion} -> ORM.update(article_comment_user_emotion, args)
{:error, _} -> ORM.create(ArticleCommentUserEmotion, args)
end
end)
|> Multi.run(:query_emotion_status, fn _, _ ->
query_emotion_status(comment, emotion)
|> Multi.run(:query_emotion_states, fn _, _ ->
query_emotion_states(comment, emotion)
end)
|> Multi.run(:update_comment_emotion, fn _, %{query_emotion_status: status} ->
update_comment_emotion(comment, emotion, status, user)
|> Multi.run(:update_emotions_field, fn _, %{query_emotion_states: status} ->
update_emotions_field(comment, emotion, status, user)
end)
|> Repo.transaction()
|> update_emotion_result
|> update_emotions_field_result
end
end

Expand All @@ -56,23 +56,28 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommentEmotion do
user_id: user.id
}

{:ok, article_comment_user_emotion} = ORM.find_by(ArticleCommentUserEmotion, target)
args = Map.put(target, :"#{emotion}", false)
article_comment_user_emotion |> ORM.update(args)
case ORM.find_by(ArticleCommentUserEmotion, target) do
{:ok, article_comment_user_emotion} ->
args = Map.put(target, :"#{emotion}", false)
article_comment_user_emotion |> ORM.update(args)

{:error, _} ->
ORM.create(ArticleCommentUserEmotion, target)
end
end)
|> Multi.run(:query_emotion_status, fn _, _ ->
query_emotion_status(comment, emotion)
|> Multi.run(:query_emotion_states, fn _, _ ->
query_emotion_states(comment, emotion)
end)
|> Multi.run(:update_comment_emotion, fn _, %{query_emotion_status: status} ->
update_comment_emotion(comment, emotion, status, user)
|> Multi.run(:update_emotions_field, fn _, %{query_emotion_states: status} ->
update_emotions_field(comment, emotion, status, user)
end)
|> Repo.transaction()
|> update_emotion_result
|> update_emotions_field_result
end
end

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

@spec update_comment_emotion(ArticleComment.t(), Atom.t(), t_mention_status, User.t()) ::
{:ok, ArticleComment.t()} | {:error, any}
defp update_comment_emotion(comment, emotion, status, user) do
%{user_count: user_count, user_list: user_list} = status

emotions =
%{}
|> Map.put(:"#{emotion}_count", user_count)
|> Map.put(:"#{emotion}_user_logins", user_list |> Enum.map(& &1.login))
|> Map.put(
:"latest_#{emotion}_users",
Enum.slice(user_list, 0, @max_latest_emotion_users_count)
)

viewer_has_emotioned = user.login in Map.get(emotions, :"#{emotion}_user_logins")
emotions = emotions |> Map.put(:"viewer_has_#{emotion}ed", viewer_has_emotioned)

comment
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_embed(:emotions, emotions)
|> Repo.update()
# virtual field can not be updated
|> add_viewer_emotioned_ifneed(emotions)
end

defp add_viewer_emotioned_ifneed({:error, error}, _), do: {:error, error}

defp add_viewer_emotioned_ifneed({:ok, comment}, emotions) do
# Map.merge(comment, %{emotion: emotions})
{:ok, Map.merge(comment, %{emotion: emotions})}
end

defp update_emotion_result({:ok, %{update_comment_emotion: result}}), do: {:ok, result}
defp update_emotions_field_result({:ok, %{update_emotions_field: result}}), do: {:ok, result}

defp update_emotion_result({:error, _, result, _steps}) do
defp update_emotions_field_result({:error, _, result, _steps}) do
{:error, result}
end
end
Loading