remove friend list stuff

fl2
Thibault Duplessis 2020-03-25 12:36:14 -06:00
parent a291f1838a
commit 911ff5b750
16 changed files with 28 additions and 329 deletions

View File

@ -85,15 +85,12 @@ final class Api(
def usersStatus = ApiRequest { req =>
val ids = get("ids", req).??(_.split(',').take(50).toList map lila.user.User.normalize)
env.user.lightUserApi asyncMany ids dmap (_.flatten) map { users =>
val actualIds = users.map(_.id)
val playingIds = env.relation.online.playing intersect actualIds
val streamingIds = env.streamer.liveStreamApi.userIds
toApiResult {
users.map { u =>
lila.common.LightUser.lightUserWrites
.writes(u)
.add("online" -> env.socket.isOnline(u.id))
.add("playing" -> playingIds(u.id))
.add("streaming" -> streamingIds(u.id))
}
}

View File

@ -17,7 +17,6 @@ final class Dev(env: Env) extends LilaController(env) {
env.streamer.alwaysFeaturedSetting,
env.rating.ratingFactorsSetting,
env.plan.donationGoalSetting,
env.relation.friendListToggle,
env.apiTimelineSetting,
env.game.crosstableInit,
env.game.playTimeInit

View File

@ -483,27 +483,24 @@ abstract private[controllers] class LilaController(val env: Env)
val isPage = HTTPRequest isSynchronousHttp ctx.req
val nonce = isPage option Nonce.random
ctx.me.fold(fuccess(PageData.anon(ctx.req, nonce, blindMode(ctx)))) { me =>
import lila.relation.actorApi.OnlineFriends
env.pref.api.getPref(me, ctx.req) zip
(if (isGranted(_.Teacher, me)) fuccess(true) else env.clas.api.student.isStudent(me.id)) zip {
if (isPage) {
env.user.lightUserApi preloadUser me
env.relation.online.friendsOf(me.id) zip
env.team.api.nbRequests(me.id) zip
env.team.api.nbRequests(me.id) zip
env.challenge.api.countInFor.get(me.id) zip
env.notifyM.api.unreadCount(Notifies(me.id)).dmap(_.value) zip
env.mod.inquiryApi.forMod(me)
} else
fuccess {
((((OnlineFriends.empty, 0), 0), 0), none)
(((0, 0), 0), none)
}
} map {
case (
(pref, hasClas),
(onlineFriends ~ teamNbRequests ~ nbChallenges ~ nbNotifications ~ inquiry)
(teamNbRequests ~ nbChallenges ~ nbNotifications ~ inquiry)
) =>
PageData(
onlineFriends,
teamNbRequests,
nbChallenges,
nbNotifications,

View File

@ -26,10 +26,9 @@ final class Streamer(
def live = apiC.ApiRequest { _ =>
env.user.lightUserApi asyncMany env.streamer.liveStreamApi.userIds.toList dmap (_.flatten) map { users =>
val playingIds = env.relation.online.playing intersect users.map(_.id)
apiC.toApiResult {
users.map { u =>
lila.common.LightUser.lightUserWrites.writes(u).add("playing" -> playingIds(u.id))
lila.common.LightUser.lightUserWrites.writes(u)
}
}
}

View File

@ -54,8 +54,6 @@ object Environment
def blockingReportNbOpen: Int = env.report.api.nbOpen.awaitOrElse(10.millis, "nbReports", 0)
def friendListEnabled = env.relation.friendListEnabled()
val spinner: Frag = raw(
"""<div class="spinner"><svg viewBox="0 0 40 40"><circle cx=20 cy=20 r=18 fill="none"></circle></svg></div>"""
)

View File

@ -227,35 +227,13 @@ object layout {
"is3d" -> ctx.pref.is3d
)
)(body),
(ctx.isAuth && friendListEnabled) option div(
ctx.isAuth option div(
id := "friend_box",
dataPreload := safeJsonValue(
Json.obj(
"users" -> ctx.onlineFriends.users.map(_.titleName),
"playing" -> ctx.onlineFriends.playing,
"patrons" -> ctx.onlineFriends.patrons,
"studying" -> ctx.onlineFriends.studying,
"i18n" -> i18nJsObject(i18nKeys)
)
)
dataPreload := safeJsonValue(Json.obj("i18n" -> i18nJsObject(i18nKeys)))
)(
div(cls := "friend_box_title") {
val count = ctx.onlineFriends.users.size
trans.nbFriendsOnline.plural(count, strong(count))
},
div(cls := "friend_box_title"),
div(cls := "content_wrap")(
div(cls := "content list"),
div(
cls := List(
"nobody" -> true,
"none" -> ctx.onlineFriends.users.nonEmpty
)
)(
span(trans.noFriendsOnline()),
a(cls := "find button", href := routes.User.opponents)(
span(cls := "is3 text", dataIcon := "h")(trans.findFriends())
)
)
div(cls := "content list")
)
),
a(id := "reconnecting", cls := "link text", dataIcon := "B")(trans.reconnecting()),

View File

@ -5,11 +5,9 @@ import play.api.i18n.Lang
import lila.common.{ HTTPRequest, Nonce }
import lila.pref.Pref
import lila.relation.actorApi.OnlineFriends
import lila.user.{ BodyUserContext, HeaderUserContext, UserContext }
case class PageData(
onlineFriends: OnlineFriends,
teamNbRequests: Int,
nbChallenges: Int,
nbNotifications: Int,
@ -25,7 +23,6 @@ case class PageData(
object PageData {
def anon(req: RequestHeader, nonce: Option[Nonce], blindMode: Boolean = false) = PageData(
OnlineFriends.empty,
teamNbRequests = 0,
nbChallenges = 0,
nbNotifications = 0,
@ -47,8 +44,6 @@ sealed trait Context extends lila.user.UserContextWrapper {
def lang = userContext.lang
def onlineFriends = pageData.onlineFriends
def teamNbRequests = pageData.teamNbRequests
def nbChallenges = pageData.nbChallenges
def nbNotifications = pageData.nbNotifications

View File

@ -16,7 +16,6 @@ final private[api] class UserApi(
userRepo: lila.user.UserRepo,
prefApi: lila.pref.PrefApi,
liveStreamApi: lila.streamer.LiveStreamApi,
onlineDoing: lila.relation.OnlineDoing,
gameProxyRepo: lila.round.GameProxyRepo,
net: NetConfig
)(implicit ec: scala.concurrent.ExecutionContext) {
@ -97,8 +96,7 @@ final private[api] class UserApi(
}
private def addPlayingStreaming(js: JsObject, id: User.ID) =
js.add("playing", onlineDoing.isPlaying(id))
.add("streaming", liveStreamApi.isStreaming(id))
js.add("streaming", liveStreamApi.isStreaming(id))
private def makeUrl(path: String): String = s"${net.baseUrl}/$path"
}

View File

@ -271,10 +271,10 @@ package bookmark {
}
package relation {
case class ReloadOnlineFriends(userId: String)
case class Block(u1: String, u2: String)
case class UnBlock(u1: String, u2: String)
case class Follow(u1: String, u2: String)
case class UnFollow(u1: String, u2: String)
}
package study {

View File

@ -16,10 +16,6 @@ private class RelationConfig(
@ConfigName("limit.block") val maxBlock: Max
)
final class FriendListEnabled(f: () => Boolean) extends (() => Boolean) {
def apply() = f()
}
@Module
final class Env(
appConfig: Configuration,
@ -45,18 +41,4 @@ final class Env(
lazy val api: RelationApi = wire[RelationApi]
lazy val stream = wire[RelationStream]
lazy val online: OnlineDoing = wire[OnlineDoing]
lazy val friendListToggle = settingStore[Boolean](
"friendListToggle",
default = true,
text = "Enable the live friend list".some
)
lazy val friendListEnabled = new FriendListEnabled(friendListToggle.get _)
def isPlaying(userId: lila.user.User.ID): Boolean = online.playing.get(userId)
system.actorOf(Props(wire[RelationActor]), name = config.actorName)
}

View File

@ -1,50 +0,0 @@
package lila.relation
import scala.concurrent.duration._
import actorApi.OnlineFriends
import lila.user.User
import lila.memo.CacheApi
final class OnlineDoing(
api: RelationApi,
lightUser: lila.common.LightUser.GetterSync,
enabled: FriendListEnabled,
val userIds: () => Set[User.ID]
)(implicit ec: scala.concurrent.ExecutionContext) {
private type StudyId = String
val playing = new lila.memo.ExpireSetMemo(4 hours)
def isPlaying(userId: User.ID) = playing get userId
// people with write access in public studies
private[relation] val studying = CacheApi.scaffeineNoScheduler
.expireAfterAccess(20 minutes)
.build[ID, StudyId]
// people with write or read access in public and private studies
private[relation] val studyingAll = CacheApi.scaffeineNoScheduler
.expireAfterAccess(20 minutes)
.build[ID, StudyId]
private[relation] def isStudying(userId: User.ID) = studying.getIfPresent(userId).isDefined
private[relation] def isStudying(userId: User.ID, studyId: StudyId) =
studying.getIfPresent(userId) has studyId
private[relation] def isStudyingOrWatching(userId: User.ID, studyId: StudyId) =
studyingAll.getIfPresent(userId) has studyId
def friendsOf(userId: User.ID): Fu[OnlineFriends] =
if (enabled()) api fetchFollowing userId map userIds().intersect map { friends =>
if (friends.isEmpty) OnlineFriends.empty
else
OnlineFriends(
users = friends.view.flatMap { lightUser(_) }.toList,
playing = playing intersect friends,
studying = friends filter studying.getAllPresent(friends).contains
)
} else fuccess(OnlineFriends.empty)
}

View File

@ -1,187 +0,0 @@
package lila.relation
import akka.actor.Actor
import scala.concurrent.duration._
import actorApi._
import lila.common.{ Bus, LightUser }
import lila.hub.actorApi.relation._
import lila.hub.actorApi.socket.{ SendTo, SendTos }
import lila.user.User
final private[relation] class RelationActor(
lightUser: LightUser.GetterSync,
api: RelationApi,
online: OnlineDoing,
enabled: FriendListEnabled
)(implicit ec: scala.concurrent.ExecutionContext)
extends Actor {
private var previousOnlineIds = Set.empty[ID]
private val subs = List("startGame", "finishGame", "study", "reloadOnlineFriends")
override def preStart(): Unit = {
Bus.subscribe(self, subs)
context.system.scheduler.scheduleOnce(15 seconds, self, ComputeMovement)
}
override def postStop(): Unit = {
super.postStop()
Bus.unsubscribe(self, subs)
}
def scheduleNext =
context.system.scheduler.scheduleOnce(1 seconds, self, ComputeMovement)
def receive = {
case ComputeMovement =>
lila.common.Chronometer.syncMon(_.relation.actor.computeMovementSync) {
if (enabled()) {
val curIds = online.userIds()
val leaveUsers: List[LightUser] =
(previousOnlineIds diff curIds).view.flatMap { lightUser(_) }.to(List)
val enterUsers: List[LightUser] =
(curIds diff previousOnlineIds).view.flatMap { lightUser(_) }.to(List)
val friendsEntering = enterUsers map { u =>
FriendEntering(u, online.playing get u.id, online isStudying u.id)
}
previousOnlineIds = curIds
notifyFollowersFriendEnters(friendsEntering, curIds)
.>>(notifyFollowersFriendLeaves(leaveUsers, curIds))
.mon(_.relation.actor.computeMovement)
.addEffectAnyway(scheduleNext)
} else {
previousOnlineIds = online.userIds()
scheduleNext
}
}
// triggers following reloading for this user id
case ReloadOnlineFriends(userId) if enabled() =>
online friendsOf userId map { res =>
// the mobile app requests this on every WS connection
// we can skip it if empty
if (!res.isEmpty) Bus.publish(SendTo(userId, JsonView writeOnlineFriends res), "socketUsers")
} mon (_.relation.actor.reloadFriends)
case lila.game.actorApi.FinishGame(game, _, _) if game.hasClock =>
game.userIds.some.filter(_.nonEmpty) foreach { usersPlaying =>
online.playing removeAll usersPlaying
if (enabled()) notifyFollowersGameStateChanged(usersPlaying, "following_stopped_playing")
}
case lila.game.actorApi.StartGame(game) if game.hasClock =>
game.userIds.some.filter(_.nonEmpty) foreach { usersPlaying =>
online.playing putAll usersPlaying
if (enabled()) notifyFollowersGameStateChanged(usersPlaying, "following_playing")
}
case lila.hub.actorApi.study.StudyDoor(userId, studyId, contributor, public, true) =>
online.studyingAll.put(userId, studyId)
if (contributor && public) {
val wasAlreadyInStudy = online isStudying userId
online.studying.put(userId, studyId)
if (!wasAlreadyInStudy && enabled())
notifyFollowersFriendInStudyStateChanged(userId, "following_joined_study")
}
case lila.hub.actorApi.study.StudyDoor(userId, _, contributor, public, false) =>
online.studyingAll invalidate userId
if (contributor && public) {
online.studying invalidate userId
if (enabled()) notifyFollowersFriendInStudyStateChanged(userId, "following_left_study")
}
case lila.hub.actorApi.study.StudyBecamePrivate(studyId, contributors) =>
studyBecamePrivateOrDeleted(studyId, contributors)
case lila.hub.actorApi.study.RemoveStudy(studyId, contributors) =>
studyBecamePrivateOrDeleted(studyId, contributors)
case lila.hub.actorApi.study.StudyBecamePublic(studyId, contributors) =>
contributorsIn(contributors, studyId) foreach { c =>
online.studying.put(c, studyId)
if (enabled()) notifyFollowersFriendInStudyStateChanged(c, "following_joined_study")
}
case lila.hub.actorApi.study.StudyMemberGotWriteAccess(userId, studyId) =>
if (online.isStudyingOrWatching(userId, studyId)) {
online.studying.put(userId, studyId)
if (enabled()) notifyFollowersFriendInStudyStateChanged(userId, "following_joined_study")
}
case lila.hub.actorApi.study.StudyMemberLostWriteAccess(userId, studyId) =>
if (online.isStudying(userId, studyId)) {
online.studying invalidate userId
if (enabled()) notifyFollowersFriendInStudyStateChanged(userId, "following_left_study")
}
}
private def studyBecamePrivateOrDeleted(studyId: String, contributors: Set[ID]) = {
contributorsIn(contributors, studyId) foreach { c =>
online.studying invalidate c
if (enabled()) notifyFollowersFriendInStudyStateChanged(c, "following_left_study")
}
}
private def contributorsIn(contributors: Set[ID], studyId: String) = {
val found = online.studying.getAllPresent(contributors).filter(_._2 == studyId)
contributors filter found.contains
}
private def notifyFollowersFriendEnters(
friendsEntering: List[FriendEntering],
onlineUserIds: Set[User.ID]
): Funit =
friendsEntering
.map { entering =>
api fetchFollowersFromSecondary entering.user.id map onlineUserIds.intersect map { ids =>
if (ids.nonEmpty) Bus.publish(SendTos(ids, JsonView.writeFriendEntering(entering)), "socketUsers")
}
}
.sequenceFu
.void
private def notifyFollowersFriendLeaves(
friendsLeaving: List[LightUser],
onlineUserIds: Set[User.ID]
): Funit =
friendsLeaving
.map { leaving =>
api fetchFollowersFromSecondary leaving.id map onlineUserIds.intersect map { ids =>
if (ids.nonEmpty) Bus.publish(SendTos(ids, "following_leaves", leaving.titleName), "socketUsers")
}
}
.sequenceFu
.void
private def notifyFollowersGameStateChanged(userIds: Iterable[ID], message: String) = {
val onlineIds = online.userIds()
userIds
.map { userId =>
api.fetchFollowersFromSecondary(userId) map onlineIds.intersect map { ids =>
if (ids.nonEmpty) Bus.publish(SendTos(ids, message, userId), "socketUsers")
}
}
.sequenceFu
.mon(_.relation.actor.gameStateChanged)
}
private def notifyFollowersFriendInStudyStateChanged(userId: ID, message: String) =
api
.fetchFollowersFromSecondary(userId)
.map {
online.userIds().intersect
}
.map { ids =>
if (ids.nonEmpty) Bus.publish(SendTos(ids, message, userId), "socketUsers")
}
.mon(_.relation.actor.studyStateChanged)
}

View File

@ -17,7 +17,6 @@ import lila.user.User
final class RelationApi(
coll: Coll,
repo: RelationRepo,
actor: actors.Relation,
timeline: actors.Timeline,
prefApi: lila.pref.PrefApi,
cacheApi: lila.memo.CacheApi,
@ -144,7 +143,6 @@ final class RelationApi(
repo.follow(u1, u2) >> limitFollow(u1) >>- {
countFollowersCache.update(u2, 1 +)
countFollowingCache.update(u1, prev => (prev + 1) atMost config.maxFollow.value)
reloadOnlineFriends(u1, u2)
timeline ! Propagate(FollowUser(u1, u2)).toFriendsOf(u1).toUsers(List(u2))
Bus.publish(lila.hub.actorApi.relation.Follow(u1, u2), "relation")
lila.mon.relation.follow.increment()
@ -181,7 +179,6 @@ final class RelationApi(
case true => funit
case _ =>
repo.block(u1, u2) >> limitBlock(u1) >> unfollow(u2, u1) >>- {
reloadOnlineFriends(u1, u2)
Bus.publish(lila.hub.actorApi.relation.Block(u1, u2), "relation")
Bus.publish(
lila.hub.actorApi.socket.SendTo(u2, lila.socket.Socket.makeMessage("blockedBy", u1)),
@ -198,7 +195,7 @@ final class RelationApi(
repo.unfollow(u1, u2) >>- {
countFollowersCache.update(u2, _ - 1)
countFollowingCache.update(u1, _ - 1)
reloadOnlineFriends(u1, u2)
Bus.publish(lila.hub.actorApi.relation.UnFollow(u1, u2), "relation")
lila.mon.relation.unfollow.increment()
}
case _ => funit
@ -211,7 +208,6 @@ final class RelationApi(
fetchBlocks(u1, u2) flatMap {
case true =>
repo.unblock(u1, u2) >>- {
reloadOnlineFriends(u1, u2)
Bus.publish(lila.hub.actorApi.relation.UnBlock(u1, u2), "relation")
Bus.publish(
lila.hub.actorApi.socket.SendTo(u2, lila.socket.Socket.makeMessage("unblockedBy", u1)),
@ -225,9 +221,4 @@ final class RelationApi(
def searchFollowedBy(u: User, term: String, max: Int): Fu[List[User.ID]] =
repo.followingLike(u.id, term) map { _.sorted take max }
private def reloadOnlineFriends(u1: ID, u2: ID): Unit = {
import lila.hub.actorApi.relation.ReloadOnlineFriends
List(u1, u2).foreach(actor ! ReloadOnlineFriends(_))
}
}

View File

@ -3,9 +3,6 @@ package actorApi
import lila.common.LightUser
private[relation] case class AllOnlineFriends(onlines: Map[ID, LightUser])
private[relation] case object ComputeMovement
case class OnlineFriends(users: List[LightUser], playing: Set[String], studying: Set[String]) {
def isEmpty = users.isEmpty
def patrons: List[String] = users collect {

View File

@ -12,11 +12,11 @@ import scala.concurrent.duration._
import lila.common.{ Bus, Lilakka }
import lila.hub.actorApi.Announce
import lila.hub.actorApi.relation.ReloadOnlineFriends
import lila.hub.actorApi.round.Mlat
import lila.hub.actorApi.security.CloseAccount
import lila.hub.actorApi.socket.remote.{ TellSriIn, TellSriOut, TellUserIn }
import lila.hub.actorApi.socket.{ BotIsOnline, SendTo, SendTos }
import lila.hub.actorApi.relation.{ Follow, UnFollow }
import Socket.Sri
final class RemoteSocket(
@ -49,10 +49,6 @@ final class RemoteSocket(
case In.DisconnectUsers(userIds) =>
onlineUserIds.getAndUpdate((x: UserIds) => x -- userIds)
case In.NotifiedBatch(userIds) => notification ! lila.hub.actorApi.notify.NotifiedBatch(userIds)
case In.FriendsBatch(userIds) =>
userIds foreach { userId =>
Bus.publish(ReloadOnlineFriends(userId), "reloadOnlineFriends")
}
case In.Lags(lags) =>
lags foreach (UserLagCache.put _).tupled
// this shouldn't be necessary... ensure that users are known to be online
@ -80,7 +76,8 @@ final class RemoteSocket(
"accountClose",
"shadowban",
"impersonate",
"botIsOnline"
"botIsOnline",
"relation"
) {
case SendTos(userIds, payload) =>
val connectedUsers = userIds intersect onlineUserIds.get
@ -103,6 +100,8 @@ final class RemoteSocket(
send(Out.impersonate(userId, modId))
case BotIsOnline(userId, value) =>
onlineUserIds.getAndUpdate((x: UserIds) => { if (value) x + userId else x - userId })
case Follow(u1, u2) => send(Out.follow(u1, u2))
case UnFollow(u1, u2) => send(Out.unfollow(u1, u2))
}
final class StoppableSender(conn: StatefulRedisPubSubConnection[String, String], channel: Channel)
@ -182,7 +181,6 @@ object RemoteSocket {
case class NotifiedBatch(userIds: Iterable[String]) extends In
case class Lag(userId: String, lag: Centis) extends In
case class Lags(lags: Map[String, Centis]) extends In
case class FriendsBatch(userIds: Iterable[String]) extends In
case class TellSri(sri: Sri, userId: Option[String], typ: String, msg: JsObject) extends In
case class TellUser(userId: String, typ: String, msg: JsObject) extends In
case class ReqResponse(reqId: Int, response: String) extends In
@ -213,8 +211,7 @@ object RemoteSocket {
case _ => None
}
}.toMap).some
case "friends/batch" => FriendsBatch(commas(raw.args)).some
case "tell/sri" => raw.get(3)(tellSriMapper)
case "tell/sri" => raw.get(3)(tellSriMapper)
case "tell/user" =>
raw.get(2) {
case Array(user, payload) =>
@ -265,8 +262,10 @@ object RemoteSocket {
s"mod/troll/set $userId ${boolean(v)}"
def impersonate(userId: String, by: Option[String]) =
s"mod/impersonate $userId ${optional(by)}"
def boot = "boot"
def stop(reqId: Int) = s"lila/stop $reqId"
def follow(u1: String, u2: String) = s"rel/follow $u1 $u2"
def unfollow(u1: String, u2: String) = s"rel/unfollow $u1 $u2"
def boot = "boot"
def stop(reqId: Int) = s"lila/stop $reqId"
def commas(strs: Iterable[Any]): String = if (strs.isEmpty) "-" else strs mkString ","
def boolean(v: Boolean): String = if (v) "+" else "-"

View File

@ -729,7 +729,13 @@
self.$nobody = el.find(".nobody");
const data = el.data('preload');
const data = {
users: [],
playing: [],
patrons: [],
studying: [],
... el.data('preload')
};
self.trans = lichess.trans(data.i18n);
self.set(data);
},