PageRenderTime 26ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/app/controllers/Relation.scala

http://github.com/ornicar/lila
Scala | 193 lines | 174 code | 19 blank | 0 comment | 4 complexity | 45c9de80efe309f3136b0d2562dfc139 MD5 | raw file
Possible License(s): AGPL-1.0, AGPL-3.0
  1. package controllers
  2. import play.api.libs.json.Json
  3. import scala.concurrent.duration._
  4. import lila.api.Context
  5. import lila.app._
  6. import lila.common.config.MaxPerSecond
  7. import lila.common.paginator.{ AdapterLike, Paginator, PaginatorJson }
  8. import lila.relation.Related
  9. import lila.relation.RelationStream._
  10. import lila.user.{ User => UserModel }
  11. import views._
  12. final class Relation(
  13. env: Env,
  14. apiC: => Api
  15. ) extends LilaController(env) {
  16. val api = env.relation.api
  17. private def renderActions(userId: String, mini: Boolean)(implicit ctx: Context) =
  18. (ctx.userId ?? { api.fetchRelation(_, userId) }) zip
  19. (ctx.isAuth ?? { env.pref.api followable userId }) zip
  20. (ctx.userId ?? { api.fetchBlocks(userId, _) }) flatMap { case ((relation, followable), blocked) =>
  21. negotiate(
  22. html = fuccess(Ok {
  23. if (mini)
  24. html.relation.mini(userId, blocked = blocked, followable = followable, relation = relation)
  25. else
  26. html.relation.actions(userId, relation = relation, blocked = blocked, followable = followable)
  27. }),
  28. api = _ =>
  29. fuccess(
  30. Ok(
  31. Json.obj(
  32. "followable" -> followable,
  33. "following" -> relation.contains(true),
  34. "blocking" -> relation.contains(false)
  35. )
  36. )
  37. )
  38. )
  39. }
  40. private val FollowLimitPerUser = new lila.memo.RateLimit[lila.user.User.ID](
  41. credits = 150,
  42. duration = 72.hour,
  43. key = "follow.user"
  44. )
  45. def follow(userId: String) =
  46. Auth { implicit ctx => me =>
  47. FollowLimitPerUser(me.id) {
  48. api.reachedMaxFollowing(me.id) flatMap {
  49. case true =>
  50. env.msg.api
  51. .postPreset(
  52. me.id,
  53. lila.msg.MsgPreset.maxFollow(me.username, env.relation.maxFollow.value)
  54. ) inject Ok
  55. case _ =>
  56. api.follow(me.id, UserModel normalize userId).recoverDefault >> renderActions(
  57. userId,
  58. getBool("mini")
  59. )
  60. }
  61. }(rateLimitedFu)
  62. }
  63. def apiFollow(userId: String) =
  64. Scoped(_.Follow.Write) { _ => me =>
  65. FollowLimitPerUser[Fu[Api.ApiResult]](me.id) {
  66. api.reachedMaxFollowing(me.id) flatMap {
  67. case true =>
  68. fuccess(
  69. Api.ClientError(lila.msg.MsgPreset.maxFollow(me.username, env.relation.maxFollow.value).text)
  70. )
  71. case _ =>
  72. api.follow(me.id, UserModel normalize userId).recoverDefault inject Api.Done
  73. }
  74. }(fuccess(Api.Limited)) map apiC.toHttp
  75. }
  76. def unfollow(userId: String) =
  77. Auth { implicit ctx => me =>
  78. FollowLimitPerUser(me.id) {
  79. api.unfollow(me.id, UserModel normalize userId).recoverDefault >> renderActions(
  80. userId,
  81. getBool("mini")
  82. )
  83. }(rateLimitedFu)
  84. }
  85. def apiUnfollow(userId: String) =
  86. Scoped(_.Follow.Write) { _ => me =>
  87. FollowLimitPerUser[Fu[Api.ApiResult]](me.id) {
  88. api.unfollow(me.id, UserModel normalize userId) inject Api.Done
  89. }(fuccess(Api.Limited)) map apiC.toHttp
  90. }
  91. def block(userId: String) =
  92. Auth { implicit ctx => me =>
  93. FollowLimitPerUser(me.id) {
  94. api.block(me.id, UserModel normalize userId).recoverDefault >> renderActions(userId, getBool("mini"))
  95. }(rateLimitedFu)
  96. }
  97. def unblock(userId: String) =
  98. Auth { implicit ctx => me =>
  99. api.unblock(me.id, UserModel normalize userId).recoverDefault >> renderActions(userId, getBool("mini"))
  100. }
  101. def following(username: String, page: Int) =
  102. Open { implicit ctx =>
  103. Reasonable(page, 20) {
  104. OptionFuResult(env.user.repo named username) { user =>
  105. RelatedPager(api.followingPaginatorAdapter(user.id), page) flatMap { pag =>
  106. negotiate(
  107. html = {
  108. if (ctx.is(user) || isGranted(_.CloseAccount))
  109. Ok(html.relation.bits.friends(user, pag)).fuccess
  110. else ctx.me.fold(notFound)(me => Redirect(routes.Relation.following(me.username)).fuccess)
  111. },
  112. api = _ => Ok(jsonRelatedPaginator(pag)).fuccess
  113. )
  114. }
  115. }
  116. }
  117. }
  118. def followers(username: String, page: Int) =
  119. Open { implicit ctx =>
  120. negotiate(
  121. html = notFound,
  122. api = _ =>
  123. Reasonable(page, 20) {
  124. RelatedPager(api.followersPaginatorAdapter(UserModel normalize username), page) flatMap { pag =>
  125. Ok(jsonRelatedPaginator(pag)).fuccess
  126. }
  127. }
  128. )
  129. }
  130. def apiFollowing = Scoped() { implicit req => me =>
  131. apiC.jsonStream {
  132. env.relation.stream
  133. .follow(me, Direction.Following, MaxPerSecond(30))
  134. .map(env.api.userApi.one(_, withOnline = false))
  135. }.fuccess
  136. }
  137. private def jsonRelatedPaginator(pag: Paginator[Related]) = {
  138. import lila.user.JsonView.nameWrites
  139. import lila.relation.JsonView.relatedWrites
  140. Json.obj("paginator" -> PaginatorJson(pag.mapResults { r =>
  141. relatedWrites.writes(r) ++ Json
  142. .obj(
  143. "perfs" -> r.user.perfs.bestPerfType.map { best =>
  144. lila.user.JsonView.perfs(r.user, best.some)
  145. }
  146. )
  147. .add("online" -> env.socket.isOnline(r.user.id))
  148. }))
  149. }
  150. def blocks(page: Int) =
  151. Auth { implicit ctx => me =>
  152. Reasonable(page, 20) {
  153. RelatedPager(api.blockingPaginatorAdapter(me.id), page) map { pag =>
  154. html.relation.bits.blocks(me, pag)
  155. }
  156. }
  157. }
  158. private def RelatedPager(adapter: AdapterLike[String], page: Int)(implicit ctx: Context) =
  159. Paginator(
  160. adapter = adapter mapFutureList followship,
  161. currentPage = page,
  162. maxPerPage = lila.common.config.MaxPerPage(30)
  163. )
  164. private def followship(userIds: Seq[String])(implicit ctx: Context): Fu[List[Related]] =
  165. env.user.repo usersFromSecondary userIds.map(UserModel.normalize) flatMap { users =>
  166. (ctx.isAuth ?? { env.pref.api.followableIds(users map (_.id)) }) flatMap { followables =>
  167. users.map { u =>
  168. ctx.userId ?? { api.fetchRelation(_, u.id) } map { rel =>
  169. lila.relation.Related(u, none, followables(u.id), rel)
  170. }
  171. }.sequenceFu
  172. }
  173. }
  174. }