Study icon for friends in a study
The study icon will appear when: * A friend, who is a contributor, joins a study, or makes a move in a study if he does not have the icon yet (the latter happens if you have two studies open and close one). * A friend is in a study and gets added as contributor. * A friend is a contributor in a private study and the study becomes public. The study icon will disappear when: * A friend, who is a contributor, leaves a study. * A friend, who is a contributor, gets his status revoked, or kicked. * A friend, who is a contributor, is in a public study that becomes private.pull/2659/head
parent
dfd070b62f
commit
8913cd81b3
|
@ -31,6 +31,15 @@ object User extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def studyTv(username: String) = Open { implicit ctx =>
|
||||
OptionFuResult (UserRepo named username) { user =>
|
||||
lila.relation.Env.current.onlineStudying.get(user.id) match {
|
||||
case None => notFound
|
||||
case Some(studyId) => fuccess(Redirect(routes.Study.show(studyId)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def show(username: String) = OpenBody { implicit ctx =>
|
||||
filter(username, none, 1)
|
||||
}
|
||||
|
|
|
@ -179,7 +179,8 @@ withGtm: Boolean = false)(body: Html)(implicit ctx: Context)
|
|||
@ctx.me.map { me =>
|
||||
<div id="friend_box" data-preload="@ctx.onlineFriends.users.map(_.titleName).mkString(",")"
|
||||
data-playing="@ctx.onlineFriends.playing.mkString(",")"
|
||||
data-patrons="@ctx.onlineFriends.patrons.mkString(",")">
|
||||
data-patrons="@ctx.onlineFriends.patrons.mkString(",")"
|
||||
data-studying="@ctx.onlineFriends.studying.mkString(",")">
|
||||
<div class="title"><strong class="online"> </strong> @trans.onlineFriends()</div>
|
||||
<div class="content_wrap">
|
||||
<div class="content list"></div>
|
||||
|
|
|
@ -55,6 +55,7 @@ GET /@/:username/mod controllers.User.mod(username: String)
|
|||
POST /@/:username/note controllers.User.writeNote(username: String)
|
||||
GET /@/:username/mini controllers.User.showMini(username: String)
|
||||
GET /@/:username/tv controllers.User.tv(username: String)
|
||||
GET /@/:username/studyTv controllers.User.studyTv(username: String)
|
||||
GET /@/:username/perf/:perfKey controllers.User.perfStat(username: String, perfKey: String)
|
||||
GET /@/:username/:filterName controllers.User.showFilter(username: String, filterName: String, page: Int ?= 1)
|
||||
GET /@/:username controllers.User.show(username: String)
|
||||
|
|
|
@ -12,6 +12,7 @@ final class Env(
|
|||
hub: lila.hub.Env,
|
||||
getOnlineUserIds: () => Set[String],
|
||||
lightUser: lila.common.LightUser.Getter,
|
||||
lightUserSync: lila.common.LightUser.GetterSync,
|
||||
followable: String => Fu[Boolean],
|
||||
system: ActorSystem,
|
||||
asyncCache: lila.memo.AsyncCache.Builder,
|
||||
|
@ -40,12 +41,17 @@ final class Env(
|
|||
maxBlock = MaxBlock)
|
||||
|
||||
val onlinePlayings = new lila.memo.ExpireSetMemo(4 hour)
|
||||
val onlineStudying = new OnlineStudyingMemo(4 hour) // people with write access in public studies
|
||||
val onlineStudyingAll = new OnlineStudyingMemo(4 hour) // people with write or read access in public and private studies
|
||||
|
||||
private[relation] val actor = system.actorOf(Props(new RelationActor(
|
||||
getOnlineUserIds = getOnlineUserIds,
|
||||
lightUser = lightUser,
|
||||
lightUserSync = lightUserSync,
|
||||
api = api,
|
||||
onlinePlayings
|
||||
onlinePlayings,
|
||||
onlineStudying,
|
||||
onlineStudyingAll
|
||||
)), name = ActorName)
|
||||
|
||||
scheduler.once(15 seconds) {
|
||||
|
@ -63,6 +69,7 @@ object Env {
|
|||
hub = lila.hub.Env.current,
|
||||
getOnlineUserIds = () => lila.user.Env.current.onlineUserIdMemo.keySet,
|
||||
lightUser = lila.user.Env.current.lightUser,
|
||||
lightUserSync = lila.user.Env.current.lightUserSync,
|
||||
followable = lila.pref.Env.current.api.followable _,
|
||||
system = lila.common.PlayApp.system,
|
||||
asyncCache = lila.memo.Env.current.asyncCache,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package lila.relation
|
||||
|
||||
import lila.hub.actorApi.relation.OnlineFriends
|
||||
import lila.common.LightUser
|
||||
import play.api.libs.json._
|
||||
|
||||
object JsonView {
|
||||
|
@ -22,7 +23,9 @@ object JsonView {
|
|||
"t" -> "following_onlines",
|
||||
"d" -> onlineFriends.users.map(_.titleName),
|
||||
"playing" -> onlineFriends.playing,
|
||||
"patrons" -> onlineFriends.patrons)
|
||||
"studying" -> onlineFriends.studying,
|
||||
"patrons" -> onlineFriends.patrons
|
||||
)
|
||||
}
|
||||
|
||||
def writeFriendEntering(friendEntering: FriendEntering) = {
|
||||
|
@ -31,7 +34,16 @@ object JsonView {
|
|||
"t" -> "following_enters",
|
||||
"d" -> friendEntering.user.titleName,
|
||||
"playing" -> friendEntering.isPlaying,
|
||||
"studyId" -> friendEntering.studyId,
|
||||
"patron" -> friendEntering.user.isPatron
|
||||
)
|
||||
}
|
||||
|
||||
def writeFriendJoinedOrQuitStudy(user: LightUser, studyId: String, message: String) = {
|
||||
Json.obj(
|
||||
"t" -> message,
|
||||
"user" -> user.titleName,
|
||||
"studyId" -> studyId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package lila.relation
|
||||
|
||||
import com.github.blemale.scaffeine.{ Cache, Scaffeine }
|
||||
import scala.concurrent.duration._
|
||||
|
||||
final class OnlineStudyingMemo(ttl: Duration) {
|
||||
|
||||
private val cache: Cache[String, String] /* userId, studyId */ = Scaffeine()
|
||||
.expireAfterAccess(ttl)
|
||||
.build[String, String]
|
||||
|
||||
def put(userId: String, studyId: String): Unit =
|
||||
cache.put(userId, studyId)
|
||||
|
||||
def get(userId: String): Option[String] =
|
||||
cache getIfPresent userId
|
||||
|
||||
def remove(userId: String): Unit =
|
||||
cache invalidate userId
|
||||
|
||||
def getMap(): Map[String, String] =
|
||||
scala.collection.immutable.Map() ++ (cache asMap)
|
||||
}
|
|
@ -10,11 +10,16 @@ import lila.common.LightUser
|
|||
import lila.hub.actorApi.relation._
|
||||
import lila.hub.actorApi.{ SendTo, SendTos }
|
||||
|
||||
import play.api.libs.json.Json
|
||||
|
||||
private[relation] final class RelationActor(
|
||||
getOnlineUserIds: () => Set[String],
|
||||
lightUser: LightUser.Getter,
|
||||
lightUserSync: LightUser.GetterSync,
|
||||
api: RelationApi,
|
||||
onlinePlayings: ExpireSetMemo) extends Actor {
|
||||
onlinePlayings: ExpireSetMemo,
|
||||
onlineStudying: OnlineStudyingMemo,
|
||||
onlineStudyingAll: OnlineStudyingMemo) extends Actor {
|
||||
|
||||
private val bus = context.system.lilaBus
|
||||
|
||||
|
@ -23,6 +28,7 @@ private[relation] final class RelationActor(
|
|||
override def preStart(): Unit = {
|
||||
context.system.lilaBus.subscribe(self, 'startGame)
|
||||
context.system.lilaBus.subscribe(self, 'finishGame)
|
||||
context.system.lilaBus.subscribe(self, 'study)
|
||||
}
|
||||
|
||||
override def postStop(): Unit = {
|
||||
|
@ -64,10 +70,52 @@ private[relation] final class RelationActor(
|
|||
val usersPlaying = game.userIds
|
||||
onlinePlayings putAll usersPlaying
|
||||
notifyFollowersGameStateChanged(usersPlaying, "following_playing")
|
||||
|
||||
case lila.hub.actorApi.study.StudyJoin(userId, studyId, contributor, public) =>
|
||||
onlineStudyingAll.put(userId, studyId)
|
||||
if (contributor && public) {
|
||||
val wasAlreadyInStudy = onlineStudying.get(userId) != None
|
||||
onlineStudying.put(userId, studyId)
|
||||
if (!wasAlreadyInStudy) notifyFollowersFriendInStudyStateChanged(userId, studyId, "following_joined_study")
|
||||
}
|
||||
|
||||
case lila.hub.actorApi.study.StudyQuit(userId, studyId, contributor, public) =>
|
||||
onlineStudyingAll remove userId
|
||||
if (contributor && public) {
|
||||
onlineStudying remove userId
|
||||
notifyFollowersFriendInStudyStateChanged(userId, studyId, "following_left_study")
|
||||
}
|
||||
|
||||
case lila.hub.actorApi.study.StudyBecamePrivate(studyId, contributors) =>
|
||||
val contributorsInStudy = contributors.filter(onlineStudying.get(_) != None).toSet
|
||||
for (c <- contributorsInStudy) {
|
||||
onlineStudying remove c
|
||||
notifyFollowersFriendInStudyStateChanged(c, studyId, "following_left_study")
|
||||
}
|
||||
|
||||
case lila.hub.actorApi.study.StudyBecamePublic(studyId, contributors) =>
|
||||
val contributorsInStudy = contributors.filter(onlineStudyingAll.get(_) != None).toSet
|
||||
for (c <- contributorsInStudy) {
|
||||
onlineStudying.put(c, studyId)
|
||||
notifyFollowersFriendInStudyStateChanged(c, studyId, "following_joined_study")
|
||||
}
|
||||
|
||||
case lila.hub.actorApi.study.StudyMemberGotWriteAccess(userId, studyId, public) if public =>
|
||||
if (onlineStudyingAll.get(userId) != None) {
|
||||
onlineStudying.put(userId, studyId)
|
||||
notifyFollowersFriendInStudyStateChanged(userId, studyId, "following_joined_study")
|
||||
}
|
||||
|
||||
case lila.hub.actorApi.study.StudyMemberLostWriteAccess(userId, studyId, public) if public =>
|
||||
if (onlineStudying.get(userId) != None) {
|
||||
onlineStudying remove userId
|
||||
notifyFollowersFriendInStudyStateChanged(userId, studyId, "following_left_study")
|
||||
}
|
||||
}
|
||||
|
||||
private def makeFriendEntering(enters: LightUser) = {
|
||||
FriendEntering(enters, onlinePlayings.get(enters.id))
|
||||
val studyId = onlineStudying.get(enters.id)
|
||||
FriendEntering(enters, onlinePlayings.get(enters.id), studyId)
|
||||
}
|
||||
|
||||
private def onlineIds: Set[ID] = onlines.keySet
|
||||
|
@ -76,13 +124,18 @@ private[relation] final class RelationActor(
|
|||
api fetchFollowing userId map { ids =>
|
||||
val friends = ids.flatMap(onlines.get).toList
|
||||
val friendsPlaying = filterFriendsPlaying(friends)
|
||||
OnlineFriends(friends, friendsPlaying)
|
||||
val friendsStudying = filterFriendsStudying(friends)
|
||||
OnlineFriends(friends, friendsPlaying, friendsStudying)
|
||||
}
|
||||
|
||||
private def filterFriendsPlaying(friends: List[LightUser]): Set[String] = {
|
||||
friends.filter(p => onlinePlayings.get(p.id)).map(_.id).toSet
|
||||
}
|
||||
|
||||
private def filterFriendsStudying(friends: List[LightUser]): Set[String] = {
|
||||
friends.filter(p => onlineStudying.get(p.id) != None).map(_.id).toSet
|
||||
}
|
||||
|
||||
private def notifyFollowersFriendEnters(friendsEntering: List[FriendEntering]) =
|
||||
friendsEntering foreach { entering =>
|
||||
api fetchFollowers entering.user.id map (_ filter onlines.contains) foreach { ids =>
|
||||
|
@ -103,4 +156,9 @@ private[relation] final class RelationActor(
|
|||
if (ids.nonEmpty) bus.publish(SendTos(ids.toSet, message, userId), 'users)
|
||||
}
|
||||
}
|
||||
|
||||
private def notifyFollowersFriendInStudyStateChanged(userId: String, studyId: String, message: String) =
|
||||
api fetchFollowers userId map (_ filter onlines.contains) foreach { ids =>
|
||||
if (ids.nonEmpty) bus.publish(SendTos(ids.toSet, message, userId), 'users)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ case class Blocked(u2: String) {
|
|||
def userId = u2
|
||||
}
|
||||
|
||||
private[relation] case class FriendEntering(user: LightUser, isPlaying: Boolean)
|
||||
private[relation] case class FriendEntering(user: LightUser, isPlaying: Boolean, studyId: Option[String])
|
||||
|
||||
object BSONHandlers {
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@ final class Env(
|
|||
evalCacheHandler: lila.evalCache.EvalCacheSocketHandler,
|
||||
system: ActorSystem,
|
||||
hub: lila.hub.Env,
|
||||
db: lila.db.Env) {
|
||||
db: lila.db.Env,
|
||||
asyncCache: lila.memo.AsyncCache.Builder) {
|
||||
|
||||
private val settings = new {
|
||||
val CollectionStudy = config getString "collection.study"
|
||||
|
@ -46,7 +47,8 @@ final class Env(
|
|||
lightUser = lightUserApi.async,
|
||||
history = new lila.socket.History(ttl = HistoryMessageTtl),
|
||||
uidTimeout = UidTimeout,
|
||||
socketTimeout = SocketTimeout)
|
||||
socketTimeout = SocketTimeout,
|
||||
lightStudyCache = lightStudyCache)
|
||||
}), name = SocketName)
|
||||
|
||||
def version(studyId: Study.Id): Fu[Int] =
|
||||
|
@ -93,7 +95,8 @@ final class Env(
|
|||
chat = hub.actor.chat,
|
||||
bus = system.lilaBus,
|
||||
timeline = hub.actor.timeline,
|
||||
socketHub = socketHub)
|
||||
socketHub = socketHub,
|
||||
lightStudyCache = lightStudyCache)
|
||||
|
||||
lazy val pager = new StudyPager(
|
||||
studyRepo = studyRepo,
|
||||
|
@ -113,6 +116,8 @@ final class Env(
|
|||
logger = logger)
|
||||
}))
|
||||
|
||||
lazy val lightStudyCache = new lila.study.LightStudyCache(4 hour, studyRepo, asyncCache)
|
||||
|
||||
def cli = new lila.common.Cli {
|
||||
def process = {
|
||||
case "study" :: "rank" :: "reset" :: Nil => api.resetAllRanks.map { count => s"$count done" }
|
||||
|
@ -130,5 +135,6 @@ object Env {
|
|||
evalCacheHandler = lila.evalCache.Env.current.socketHandler,
|
||||
system = lila.common.PlayApp.system,
|
||||
hub = lila.hub.Env.current,
|
||||
db = lila.db.Env.current)
|
||||
db = lila.db.Env.current,
|
||||
asyncCache = lila.memo.Env.current.asyncCache)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package lila.study
|
||||
|
||||
import com.github.blemale.scaffeine.{ AsyncLoadingCache, Scaffeine }
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.study._
|
||||
import lila.user.User
|
||||
|
||||
final class LightStudyCache(ttl: Duration,
|
||||
studyRepo: StudyRepo,
|
||||
asyncCache: lila.memo.AsyncCache.Builder) {
|
||||
private val cache = asyncCache.clearable(
|
||||
name = "study.lightStudyCache",
|
||||
f = fetch,
|
||||
expireAfter = _.ExpireAfterWrite(4 hours)
|
||||
)
|
||||
|
||||
def remove(studyId: String): Unit =
|
||||
cache invalidate studyId
|
||||
|
||||
def get(studyId: String): Fu[Option[LightStudy]] =
|
||||
cache get studyId
|
||||
|
||||
private def fetch(studyId: String): Fu[Option[LightStudy]] =
|
||||
studyRepo byId new Study.Id(studyId) flatMap { studyOption =>
|
||||
studyOption match {
|
||||
case Some(study) => fuccess(Some(new LightStudy(study.isPublic, study.members.members.filter(_._2.canContribute).map(_._1).toSet)))
|
||||
case None => fuccess(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class LightStudy(val isPublic: Boolean, val contributors: Set[User.ID])
|
|
@ -20,7 +20,8 @@ private final class Socket(
|
|||
lightUser: lila.common.LightUser.Getter,
|
||||
val history: History[Socket.Messadata],
|
||||
uidTimeout: Duration,
|
||||
socketTimeout: Duration) extends SocketActor[Socket.Member](uidTimeout) with Historical[Socket.Member, Socket.Messadata] {
|
||||
socketTimeout: Duration,
|
||||
lightStudyCache: LightStudyCache) extends SocketActor[Socket.Member](uidTimeout) with Historical[Socket.Member, Socket.Messadata] {
|
||||
|
||||
import Socket._
|
||||
import JsonView._
|
||||
|
@ -41,12 +42,35 @@ private final class Socket(
|
|||
lilaBus.unsubscribe(self)
|
||||
}
|
||||
|
||||
def sendStudyJoin(userId: User.ID) {
|
||||
lightStudyCache.get(studyId.value) foreach { studyOption =>
|
||||
studyOption foreach { study =>
|
||||
val contributor = study.contributors.contains(userId)
|
||||
context.system.lilaBus.publish(lila.hub.actorApi.study.StudyJoin(userId, studyId.value, contributor, study.isPublic), 'study)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def sendStudyQuit(userId: User.ID) {
|
||||
lightStudyCache.get(studyId.value) foreach { studyOption =>
|
||||
studyOption foreach { study =>
|
||||
val contributor = study.contributors.contains(userId)
|
||||
context.system.lilaBus.publish(lila.hub.actorApi.study.StudyQuit(userId, studyId.value, contributor, study.isPublic), 'study)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def receiveSpecific = ({
|
||||
|
||||
case SetPath(pos, uid) => notifyVersion("path", Json.obj(
|
||||
"p" -> pos,
|
||||
"w" -> who(uid).map(whoWriter.writes)
|
||||
), noMessadata)
|
||||
case SetPath(pos, uid) =>
|
||||
uidToUserId(uid) match {
|
||||
case Some(userId) => sendStudyJoin(userId)
|
||||
case None => {}
|
||||
}
|
||||
notifyVersion("path", Json.obj(
|
||||
"p" -> pos,
|
||||
"w" -> who(uid).map(whoWriter.writes)
|
||||
), noMessadata)
|
||||
|
||||
case AddNode(pos, node, uid) =>
|
||||
val dests = AnaDests.Ref(chess.variant.Standard, node.fen.value, pos.path.toString).compute
|
||||
|
@ -146,10 +170,16 @@ private final class Socket(
|
|||
addMember(uid.value, member)
|
||||
notifyCrowd
|
||||
sender ! Socket.Connected(enumerator, member)
|
||||
userId match {
|
||||
case Some(u) => sendStudyJoin(u)
|
||||
case None => {}
|
||||
}
|
||||
|
||||
case Quit(uid) =>
|
||||
members get uid foreach { member =>
|
||||
quit(uid)
|
||||
val userId = member.userId.get
|
||||
sendStudyQuit(userId)
|
||||
notifyCrowd
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ final class StudyApi(
|
|||
chat: ActorSelection,
|
||||
bus: lila.common.Bus,
|
||||
timeline: ActorSelection,
|
||||
socketHub: ActorRef) {
|
||||
socketHub: ActorRef,
|
||||
lightStudyCache: LightStudyCache) {
|
||||
|
||||
def byId = studyRepo byId _
|
||||
|
||||
|
@ -189,7 +190,14 @@ final class StudyApi(
|
|||
|
||||
def setRole(byUserId: User.ID, studyId: Study.Id, userId: User.ID, roleStr: String) = sequenceStudy(studyId) { study =>
|
||||
(study isOwner byUserId) ?? {
|
||||
val previousRole = study.members.get(userId).get.role
|
||||
val role = StudyMember.Role.byId.getOrElse(roleStr, StudyMember.Role.Read)
|
||||
if (previousRole == StudyMember.Role.Read && role == StudyMember.Role.Write) {
|
||||
bus.publish(lila.hub.actorApi.study.StudyMemberGotWriteAccess(userId, studyId.value, study.isPublic), 'study)
|
||||
} else if (previousRole == StudyMember.Role.Write && role == StudyMember.Role.Read) {
|
||||
bus.publish(lila.hub.actorApi.study.StudyMemberLostWriteAccess(userId, studyId.value, study.isPublic), 'study)
|
||||
}
|
||||
lightStudyCache.remove(studyId.value)
|
||||
studyRepo.setRole(study, userId, role) >>- reloadMembers(study)
|
||||
}
|
||||
}
|
||||
|
@ -207,6 +215,10 @@ final class StudyApi(
|
|||
|
||||
def kick(studyId: Study.Id, userId: User.ID) = sequenceStudy(studyId) { study =>
|
||||
study.isMember(userId) ?? {
|
||||
if (study canContribute userId) {
|
||||
bus.publish(lila.hub.actorApi.study.StudyMemberLostWriteAccess(userId, studyId.value, study.isPublic), 'study)
|
||||
}
|
||||
lightStudyCache.remove(studyId.value)
|
||||
studyRepo.removeMember(study, userId)
|
||||
} >>- reloadMembers(study) >>- indexStudy(study)
|
||||
}
|
||||
|
@ -392,10 +404,16 @@ final class StudyApi(
|
|||
name = Study toName data.name,
|
||||
settings = settings,
|
||||
visibility = data.vis)
|
||||
if (study.visibility == Study.Visibility.Private && newStudy.visibility == Study.Visibility.Public) {
|
||||
bus.publish(lila.hub.actorApi.study.StudyBecamePublic(studyId.value, study.members.ids.filter(study.canContribute _).toSet), 'study)
|
||||
} else if (study.visibility == Study.Visibility.Public && newStudy.visibility == Study.Visibility.Private) {
|
||||
bus.publish(lila.hub.actorApi.study.StudyBecamePrivate(studyId.value, study.members.ids.filter(study.canContribute _).toSet), 'study)
|
||||
}
|
||||
(newStudy != study) ?? {
|
||||
studyRepo.updateSomeFields(newStudy) >>-
|
||||
sendTo(study, Socket.ReloadAll) >>-
|
||||
indexStudy(study)
|
||||
indexStudy(study) >>-
|
||||
lightStudyCache.remove(studyId.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -403,7 +421,8 @@ final class StudyApi(
|
|||
def delete(study: Study) = sequenceStudy(study.id) { study =>
|
||||
studyRepo.delete(study) >>
|
||||
chapterRepo.deleteByStudy(study) >>-
|
||||
bus.publish(actorApi.RemoveStudy(study.id), 'study)
|
||||
bus.publish(actorApi.RemoveStudy(study.id), 'study) >>-
|
||||
lightStudyCache.remove(study.id.value)
|
||||
}
|
||||
|
||||
def like(studyId: Study.Id, userId: User.ID, v: Boolean, socket: ActorRef, uid: Uid): Funit =
|
||||
|
|
|
@ -6,3 +6,5 @@ case class RemoveStudy(id: Study.Id)
|
|||
case class SetTag(chapterId: Chapter.Id, name: String, value: String) {
|
||||
def tag = chess.format.pgn.Tag(name, value take 140)
|
||||
}
|
||||
case class UserJoined(userId: String, studyId: String)
|
||||
case class UserLeft(userId: String, studyId: String)
|
||||
|
|
|
@ -154,10 +154,10 @@ lichess.notifyApp = (function() {
|
|||
$.extend(true, lichess.StrongSocket.defaults, {
|
||||
events: {
|
||||
following_onlines: function(d, all) {
|
||||
$('#friend_box').friends("set", all.d, all.playing, all.patrons);
|
||||
$('#friend_box').friends("set", all.d, all.playing, all.studying, all.patrons);
|
||||
},
|
||||
following_enters: function(d, all) {
|
||||
$('#friend_box').friends('enters', all.d, all.playing, all.patron);
|
||||
$('#friend_box').friends('enters', all.d, all.playing, all.studying, all.patron);
|
||||
},
|
||||
following_leaves: function(name) {
|
||||
$('#friend_box').friends('leaves', name);
|
||||
|
@ -168,6 +168,12 @@ lichess.notifyApp = (function() {
|
|||
following_stopped_playing: function(name) {
|
||||
$('#friend_box').friends('stopped_playing', name);
|
||||
},
|
||||
following_joined_study: function(name) {
|
||||
$('#friend_box').friends('study_join', name);
|
||||
},
|
||||
following_left_study: function(name) {
|
||||
$('#friend_box').friends('study_leave', name);
|
||||
},
|
||||
new_notification: function(e) {
|
||||
$('#site_notifications_tag').attr('data-count', e.unread || 0);
|
||||
$.sound.newPM();
|
||||
|
@ -1075,18 +1081,20 @@ lichess.notifyApp = (function() {
|
|||
|
||||
var users = self.element.data('preload').split(',');
|
||||
var playings = self.element.data('playing').split(',');
|
||||
var studyings = self.element.data('studying').split(',');
|
||||
var patrons = self.element.data('patrons').split(',');
|
||||
self.set(users, playings, patrons);
|
||||
self.set(users, playings, studyings, patrons);
|
||||
},
|
||||
_findByUsername: function(n) {
|
||||
return this.users.filter(function(u) {
|
||||
return isSameUser(n.toLowerCase(), u);
|
||||
})[0];
|
||||
},
|
||||
_makeUser: function(name, playing, patron) {
|
||||
_makeUser: function(name, playing, studyId, patron) {
|
||||
return {
|
||||
'name': name,
|
||||
'playing': !!playing,
|
||||
'studyId': studyId,
|
||||
'patron': !!patron
|
||||
}
|
||||
},
|
||||
|
@ -1113,16 +1121,17 @@ lichess.notifyApp = (function() {
|
|||
}).map(this._renderUser).join(""));
|
||||
}.bind(this));
|
||||
},
|
||||
set: function(us, playings, patrons) {
|
||||
set: function(us, playings, studyings, patrons) {
|
||||
this.users = us.map(function(user) {
|
||||
return this._makeUser(user, false, false);
|
||||
return this._makeUser(user, false, null, false);
|
||||
}.bind(this));
|
||||
for (user in playings) this._setPlaying(playings[user], true);
|
||||
for (user in patrons) this._setPatron(patrons[user], true);
|
||||
for (i in playings) this._setPlaying(playings[i], true);
|
||||
for (i in studyings) this._setStudying(studyings[i], true);
|
||||
for (i in patrons) this._setPatron(patrons[i], true);
|
||||
this.repaint();
|
||||
},
|
||||
enters: function(userName, playing, patron) {
|
||||
var user = this._makeUser(userName, playing, patron);
|
||||
enters: function(userName, playing, studying, patron) {
|
||||
var user = this._makeUser(userName, playing, studying, patron);
|
||||
this.users.push(user);
|
||||
this.repaint();
|
||||
},
|
||||
|
@ -1140,6 +1149,10 @@ lichess.notifyApp = (function() {
|
|||
var user = this._findByUsername(userName);
|
||||
if (user) user.patron = patron;
|
||||
},
|
||||
_setStudying: function(userName, studying) {
|
||||
var user = this._findByUsername(userName);
|
||||
if (user) user.studying = studying;
|
||||
},
|
||||
playing: function(userName) {
|
||||
this._setPlaying(userName, true);
|
||||
this.repaint();
|
||||
|
@ -1148,13 +1161,23 @@ lichess.notifyApp = (function() {
|
|||
this._setPlaying(userName, false);
|
||||
this.repaint();
|
||||
},
|
||||
study_join: function(userName) {
|
||||
this._setStudying(userName, true);
|
||||
this.repaint();
|
||||
},
|
||||
study_leave: function(userName) {
|
||||
this._setStudying(userName, false);
|
||||
this.repaint();
|
||||
},
|
||||
_renderUser: function(user) {
|
||||
var icon = '<i class="is-green line' + (user.patron ? ' patron' : '') + '"></i>';
|
||||
var name = $.fp.contains(user.name, ' ') ? user.name.split(' ')[1] : user.name;
|
||||
var url = '/@/' + name;
|
||||
var tvButton = user.playing ? '<a data-icon="1" class="tv is-green ulpt" data-pt-pos="nw" href="' + url + '/tv" data-href="' + url + '"></a>' : '';
|
||||
var studyButton = user.studying ? '<a data-icon="" class="is-green friend-study" data-pt-pos="nw" href="' + url + '/studyTv" data-href="' + '' + '"></a>' : '';
|
||||
var rightButton = tvButton || studyButton;
|
||||
|
||||
return '<div><a class="user_link ulpt" data-pt-pos="nw" href="' + url + '">' + icon + user.name + '</a>' + tvButton + '</div>';
|
||||
return '<div><a class="user_link ulpt" data-pt-pos="nw" href="' + url + '">' + icon + user.name + '</a>' + rightButton + '</div>';
|
||||
}
|
||||
};
|
||||
})());
|
||||
|
|
|
@ -1489,6 +1489,10 @@ body.fpmenu #friend_box {
|
|||
flex: 0 0 auto;
|
||||
padding: 0 5px;
|
||||
}
|
||||
#friend_box .content a.friend-study {
|
||||
flex: 0 0 auto;
|
||||
padding: 2px 5px 0 5px;
|
||||
}
|
||||
#friend_box .content a:hover {
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue