@@ -3,118 +3,127 @@ defmodule GroupherServer.Accounts.Delegate.Fans do
3
3
user followers / following related
4
4
"""
5
5
import Ecto.Query , warn: false
6
- import Helper.Utils , only: [ done: 1 ]
6
+ import Helper.Utils , only: [ done: 1 , ensure: 2 ]
7
7
import Helper.ErrorCode
8
8
import ShortMaps
9
9
10
10
alias Helper . { ORM , QueryBuilder , SpecType }
11
11
alias GroupherServer . { Accounts , Repo }
12
12
13
- alias GroupherServer.Accounts . { User , UserFollower , UserFollowing }
13
+ alias GroupherServer.Accounts . { User , Embeds , UserFollower , UserFollowing }
14
14
15
15
alias Ecto.Multi
16
16
17
+ @ default_user_meta Embeds.UserMeta . default_meta ( )
18
+
17
19
@ doc """
18
20
follow a user
19
21
"""
20
22
@ spec follow ( User . t ( ) , User . t ( ) ) :: { :ok , User . t ( ) } | SpecType . gq_error ( )
21
23
def follow ( % User { id: user_id } , % User { id: follower_id } ) do
22
24
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
24
26
Multi . new ( )
25
27
|> Multi . insert (
26
28
: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 } )
29
30
)
30
31
|> Multi . insert (
31
32
: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
+ } )
33
37
)
38
+ |> Multi . run ( :update_user_follow_info , fn _ , _ ->
39
+ update_user_follow_info ( target_user , user_id , :add )
40
+ end )
34
41
|> Multi . run ( :add_achievement , fn _ , _ ->
35
- Accounts . achieve ( % User { id: follower_id } , :inc , :follow )
42
+ Accounts . achieve ( % User { id: target_user . id } , :inc , :follow )
36
43
end )
37
44
|> Repo . transaction ( )
38
- |> follow_result ( )
45
+ |> result ( )
39
46
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 ) ] }
45
49
end
46
50
end
47
51
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
-
69
52
@ doc """
70
53
undo a follow action to a user
71
54
"""
72
55
@ spec undo_follow ( User . t ( ) , User . t ( ) ) :: { :ok , User . t ( ) } | SpecType . gq_error ( )
73
56
def undo_follow ( % User { id: user_id } , % User { id: follower_id } ) do
74
57
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
76
59
Multi . new ( )
77
60
|> 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 } )
79
62
end )
80
63
|> 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 )
82
68
end )
83
69
|> Multi . run ( :minus_achievement , fn _ , _ ->
84
- Accounts . achieve ( % User { id: follower_id } , :dec , :follow )
70
+ Accounts . achieve ( % User { id: target_user . id } , :dec , :follow )
85
71
end )
86
72
|> Repo . transaction ( )
87
- |> undo_follow_result ( )
73
+ |> result ( )
88
74
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 ) ] }
94
77
end
95
78
end
96
79
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 )
100
85
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
104
91
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
108
97
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
111
114
end
112
115
113
116
@ doc """
114
117
get paged followers of a user
115
118
"""
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
118
127
UserFollower
119
128
|> where ( [ uf ] , uf . user_id == ^ user_id )
120
129
|> join ( :inner , [ uf ] , u in assoc ( uf , :follower ) )
@@ -124,8 +133,14 @@ defmodule GroupherServer.Accounts.Delegate.Fans do
124
133
@ doc """
125
134
get paged followings of a user
126
135
"""
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
129
144
UserFollowing
130
145
|> where ( [ uf ] , uf . user_id == ^ user_id )
131
146
|> join ( :inner , [ uf ] , u in assoc ( uf , :following ) )
@@ -140,4 +155,66 @@ defmodule GroupherServer.Accounts.Delegate.Fans do
140
155
|> ORM . paginater ( ~m( page size) a )
141
156
|> done ( )
142
157
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
143
220
end
0 commit comments