@@ -3,118 +3,127 @@ defmodule GroupherServer.Accounts.Delegate.Fans do
33 user followers / following related
44 """
55 import Ecto.Query , warn: false
6- import Helper.Utils , only: [ done: 1 ]
6+ import Helper.Utils , only: [ done: 1 , ensure: 2 ]
77 import Helper.ErrorCode
88 import ShortMaps
99
1010 alias Helper . { ORM , QueryBuilder , SpecType }
1111 alias GroupherServer . { Accounts , Repo }
1212
13- alias GroupherServer.Accounts . { User , UserFollower , UserFollowing }
13+ alias GroupherServer.Accounts . { User , Embeds , UserFollower , UserFollowing }
1414
1515 alias Ecto.Multi
1616
17+ @ default_user_meta Embeds.UserMeta . default_meta ( )
18+
1719 @ doc """
1820 follow a user
1921 """
2022 @ spec follow ( User . t ( ) , User . t ( ) ) :: { :ok , User . t ( ) } | SpecType . gq_error ( )
2123 def follow ( % User { id: user_id } , % User { id: follower_id } ) do
2224 with true <- to_string ( user_id ) !== to_string ( follower_id ) ,
23- { :ok , _follow_user } <- ORM . find ( User , follower_id ) do
25+ { :ok , target_user } <- ORM . find ( User , follower_id ) do
2426 Multi . new ( )
2527 |> Multi . insert (
2628 :create_follower ,
27- # UserFollower.changeset(%UserFollower{}, ~m(user_id follower_id)a)
28- UserFollower . changeset ( % UserFollower { } , % { user_id: follower_id , follower_id: user_id } )
29+ UserFollower . changeset ( % UserFollower { } , % { user_id: target_user . id , follower_id: user_id } )
2930 )
3031 |> Multi . insert (
3132 :create_following ,
32- UserFollowing . changeset ( % UserFollowing { } , % { user_id: user_id , following_id: follower_id } )
33+ UserFollowing . changeset ( % UserFollowing { } , % {
34+ user_id: user_id ,
35+ following_id: target_user . id
36+ } )
3337 )
38+ |> Multi . run ( :update_user_follow_info , fn _ , _ ->
39+ update_user_follow_info ( target_user , user_id , :add )
40+ end )
3441 |> Multi . run ( :add_achievement , fn _ , _ ->
35- Accounts . achieve ( % User { id: follower_id } , :inc , :follow )
42+ Accounts . achieve ( % User { id: target_user . id } , :inc , :follow )
3643 end )
3744 |> Repo . transaction ( )
38- |> follow_result ( )
45+ |> result ( )
3946 else
40- false ->
41- { :error , [ message: "can't follow yourself" , code: ecode ( :self_conflict ) ] }
42-
43- { :error , reason } ->
44- { :error , [ message: reason , code: ecode ( :not_exsit ) ] }
47+ false -> { :error , [ message: "can't follow yourself" , code: ecode ( :self_conflict ) ] }
48+ { :error , reason } -> { :error , [ message: reason , code: ecode ( :not_exsit ) ] }
4549 end
4650 end
4751
48- @ spec follow_result ( { :ok , map ( ) } ) :: SpecType . done ( )
49- defp follow_result ( { :ok , % { create_follower: user_follower } } ) do
50- User |> ORM . find ( user_follower . user_id )
51- end
52-
53- defp follow_result ( { :error , :create_follower , % Ecto.Changeset { } , _steps } ) do
54- { :error , [ message: "already followed" , code: ecode ( :already_did ) ] }
55- end
56-
57- defp follow_result ( { :error , :create_follower , _result , _steps } ) do
58- { :error , [ message: "already followed" , code: ecode ( :already_did ) ] }
59- end
60-
61- defp follow_result ( { :error , :create_following , _result , _steps } ) do
62- { :error , [ message: "follow fails" , code: ecode ( :react_fails ) ] }
63- end
64-
65- defp follow_result ( { :error , :add_achievement , _result , _steps } ) do
66- { :error , [ message: "follow acieve fails" , code: ecode ( :react_fails ) ] }
67- end
68-
6952 @ doc """
7053 undo a follow action to a user
7154 """
7255 @ spec undo_follow ( User . t ( ) , User . t ( ) ) :: { :ok , User . t ( ) } | SpecType . gq_error ( )
7356 def undo_follow ( % User { id: user_id } , % User { id: follower_id } ) do
7457 with true <- to_string ( user_id ) !== to_string ( follower_id ) ,
75- { :ok , _follow_user } <- ORM . find ( User , follower_id ) do
58+ { :ok , target_user } <- ORM . find ( User , follower_id ) do
7659 Multi . new ( )
7760 |> Multi . run ( :delete_follower , fn _ , _ ->
78- ORM . findby_delete! ( UserFollower , % { user_id: follower_id , follower_id: user_id } )
61+ ORM . findby_delete! ( UserFollower , % { user_id: target_user . id , follower_id: user_id } )
7962 end )
8063 |> Multi . run ( :delete_following , fn _ , _ ->
81- ORM . findby_delete! ( UserFollowing , % { user_id: user_id , following_id: follower_id } )
64+ ORM . findby_delete! ( UserFollowing , % { user_id: user_id , following_id: target_user . id } )
65+ end )
66+ |> Multi . run ( :update_user_follow_info , fn _ , _ ->
67+ update_user_follow_info ( target_user , user_id , :remove )
8268 end )
8369 |> Multi . run ( :minus_achievement , fn _ , _ ->
84- Accounts . achieve ( % User { id: follower_id } , :dec , :follow )
70+ Accounts . achieve ( % User { id: target_user . id } , :dec , :follow )
8571 end )
8672 |> Repo . transaction ( )
87- |> undo_follow_result ( )
73+ |> result ( )
8874 else
89- false ->
90- { :error , [ message: "can't undo follow yourself" , code: ecode ( :self_conflict ) ] }
91-
92- { :error , reason } ->
93- { :error , [ message: reason , code: ecode ( :not_exsit ) ] }
75+ false -> { :error , [ message: "can't undo follow yourself" , code: ecode ( :self_conflict ) ] }
76+ { :error , reason } -> { :error , [ message: reason , code: ecode ( :not_exsit ) ] }
9477 end
9578 end
9679
97- defp undo_follow_result ( { :ok , % { delete_follower: user_follower } } ) do
98- User |> ORM . find ( user_follower . user_id )
99- end
80+ # update follow in user meta
81+ defp update_user_follow_info ( % User { } = target_user , user_id , opt ) do
82+ with { :ok , user } <- ORM . find ( User , user_id ) do
83+ target_user_meta = ensure ( target_user . meta , @ default_user_meta )
84+ user_meta = ensure ( user . meta , @ default_user_meta )
10085
101- defp undo_follow_result ( { :error , :delete_follower , _result , _steps } ) do
102- { :error , [ message: "already unfollowed" , code: ecode ( :already_did ) ] }
103- end
86+ follower_user_ids =
87+ case opt do
88+ :add -> ( target_user_meta . follower_user_ids ++ [ user_id ] ) |> Enum . uniq ( )
89+ :remove -> ( target_user_meta . follower_user_ids -- [ user_id ] ) |> Enum . uniq ( )
90+ end
10491
105- defp undo_follow_result ( { :error , :delete_following , _result , _steps } ) do
106- { :error , [ message: "unfollow fails" , code: ecode ( :react_fails ) ] }
107- end
92+ following_user_ids =
93+ case opt do
94+ :add -> ( user_meta . following_user_ids ++ [ target_user . id ] ) |> Enum . uniq ( )
95+ :remove -> ( user_meta . following_user_ids -- [ target_user . id ] ) |> Enum . uniq ( )
96+ end
10897
109- defp undo_follow_result ( { :error , :minus_achievement , _result , _steps } ) do
110- { :error , [ message: "follow acieve fails" , code: ecode ( :react_fails ) ] }
98+ Multi . new ( )
99+ |> Multi . run ( :update_follower_meta , fn _ , _ ->
100+ followers_count = length ( follower_user_ids )
101+ meta = Map . merge ( target_user_meta , % { follower_user_ids: follower_user_ids } )
102+
103+ ORM . update_meta ( target_user , meta , changes: % { followers_count: followers_count } )
104+ end )
105+ |> Multi . run ( :update_following_meta , fn _ , _ ->
106+ followings_count = length ( following_user_ids )
107+ meta = Map . merge ( user_meta , % { following_user_ids: following_user_ids } )
108+
109+ ORM . update_meta ( user , meta , changes: % { followings_count: followings_count } )
110+ end )
111+ |> Repo . transaction ( )
112+ |> result ( )
113+ end
111114 end
112115
113116 @ doc """
114117 get paged followers of a user
115118 """
116- @ spec fetch_followers ( User . t ( ) , map ( ) ) :: { :ok , map ( ) } | { :error , String . t ( ) }
117- def fetch_followers ( % User { id: user_id } , filter ) do
119+ def paged_followers ( % User { id: user_id } , filter , % User { } = cur_user ) do
120+ paged_followers ( % User { id: user_id } , filter )
121+ |> mark_viewer_follow_status ( cur_user )
122+ |> done
123+ end
124+
125+ @ spec paged_followers ( User . t ( ) , map ( ) ) :: { :ok , map ( ) } | { :error , String . t ( ) }
126+ def paged_followers ( % User { id: user_id } , filter ) do
118127 UserFollower
119128 |> where ( [ uf ] , uf . user_id == ^ user_id )
120129 |> join ( :inner , [ uf ] , u in assoc ( uf , :follower ) )
@@ -124,8 +133,14 @@ defmodule GroupherServer.Accounts.Delegate.Fans do
124133 @ doc """
125134 get paged followings of a user
126135 """
127- @ spec fetch_followings ( User . t ( ) , map ( ) ) :: { :ok , map ( ) } | { :error , String . t ( ) }
128- def fetch_followings ( % User { id: user_id } , filter ) do
136+ def paged_followings ( % User { id: user_id } , filter , % User { } = cur_user ) do
137+ paged_followings ( % User { id: user_id } , filter )
138+ |> mark_viewer_follow_status ( cur_user )
139+ |> done
140+ end
141+
142+ @ spec paged_followings ( User . t ( ) , map ( ) ) :: { :ok , map ( ) } | { :error , String . t ( ) }
143+ def paged_followings ( % User { id: user_id } , filter ) do
129144 UserFollowing
130145 |> where ( [ uf ] , uf . user_id == ^ user_id )
131146 |> join ( :inner , [ uf ] , u in assoc ( uf , :following ) )
@@ -140,4 +155,66 @@ defmodule GroupherServer.Accounts.Delegate.Fans do
140155 |> ORM . paginater ( ~m( page size) a )
141156 |> done ( )
142157 end
158+
159+ @ doc """
160+ mark viewer's follower/followings states
161+ """
162+ def mark_viewer_follow_status ( { :ok , % { entries: entries } = paged_users } , cur_user ) do
163+ entries = Enum . map ( entries , & Map . merge ( & 1 , do_mark_viewer_has_states ( & 1 . id , cur_user ) ) )
164+ Map . merge ( paged_users , % { entries: entries } )
165+ end
166+
167+ def mark_viewer_follow_status ( { :error , reason } ) , do: { :error , reason }
168+
169+ defp do_mark_viewer_has_states ( user_id , % User { meta: nil } ) do
170+ % { viewer_been_followed: false , viewer_has_followed: false }
171+ end
172+
173+ defp do_mark_viewer_has_states ( user_id , % User { meta: meta } ) do
174+ % {
175+ viewer_been_followed: Enum . member? ( meta . follower_user_ids , user_id ) ,
176+ viewer_has_followed: Enum . member? ( meta . following_user_ids , user_id )
177+ }
178+ end
179+
180+ @ spec result ( { :ok , map ( ) } ) :: SpecType . done ( )
181+ defp result ( { :ok , % { create_follower: user_follower } } ) do
182+ User |> ORM . find ( user_follower . user_id )
183+ end
184+
185+ defp result ( { :ok , % { delete_follower: user_follower } } ) do
186+ User |> ORM . find ( user_follower . user_id )
187+ end
188+
189+ defp result ( { :ok , % { update_follower_meta: result } } ) do
190+ { :ok , result }
191+ end
192+
193+ defp result ( { :error , :create_follower , % Ecto.Changeset { } , _steps } ) do
194+ { :error , [ message: "already followed" , code: ecode ( :already_did ) ] }
195+ end
196+
197+ defp result ( { :error , :create_follower , _result , _steps } ) do
198+ { :error , [ message: "already followed" , code: ecode ( :already_did ) ] }
199+ end
200+
201+ defp result ( { :error , :create_following , _result , _steps } ) do
202+ { :error , [ message: "follow fails" , code: ecode ( :react_fails ) ] }
203+ end
204+
205+ defp result ( { :error , :delete_follower , _result , _steps } ) do
206+ { :error , [ message: "already unfollowed" , code: ecode ( :already_did ) ] }
207+ end
208+
209+ defp result ( { :error , :delete_following , _result , _steps } ) do
210+ { :error , [ message: "unfollow fails" , code: ecode ( :react_fails ) ] }
211+ end
212+
213+ defp result ( { :error , :minus_achievement , _result , _steps } ) do
214+ { :error , [ message: "follow acieve fails" , code: ecode ( :react_fails ) ] }
215+ end
216+
217+ defp result ( { :error , :add_achievement , _result , _steps } ) do
218+ { :error , [ message: "follow acieve fails" , code: ecode ( :react_fails ) ] }
219+ end
143220end
0 commit comments