Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Join and kick from all broadcast at the same time #13531

Merged
merged 16 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/controllers/RelayRound.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ final class RelayRound(
apiC: => Api
) extends LilaController(env):

def form(tourId: String) = Auth { ctx ?=> _ ?=>
def form(tourId: TourModel.Id) = Auth { ctx ?=> _ ?=>
NoLameOrBot:
WithTourAndRoundsCanUpdate(tourId): trs =>
Ok.page:
html.relay.roundForm.create(env.relay.roundForm.create(trs), trs.tour)
}

def create(tourId: String) = AuthOrScopedBody(_.Study.Write) { ctx ?=> me ?=>
def create(tourId: TourModel.Id) = AuthOrScopedBody(_.Study.Write) { ctx ?=> me ?=>
NoLameOrBot:
WithTourAndRoundsCanUpdate(tourId): trs =>
val tour = trs.tour
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/RelayTour.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import views.*

import lila.app.{ given, * }
import lila.common.config.MaxPerSecond
import lila.common.{ config, IpAddress }
import lila.common.{ config, IpAddress, HTTPRequest }
import lila.relay.{ RelayTour as TourModel }

final class RelayTour(env: Env, apiC: => Api, prismicC: => Prismic) extends LilaController(env):
Expand Down
7 changes: 4 additions & 3 deletions app/controllers/Study.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import scala.util.chaining.*

import lila.app.{ given, * }
import lila.common.paginator.{ Paginator, PaginatorJson }
import lila.common.{ HTTPRequest, IpAddress }
import lila.study.actorApi.Who
import lila.common.{ Bus, HTTPRequest, IpAddress }
import lila.study.actorApi.{ AdminStudy, Who }
import lila.study.JsonView.JsData
import lila.study.Study.WithChapter
import lila.study.{ Order, StudyForm, Study as StudyModel }
Expand Down Expand Up @@ -314,8 +314,9 @@ final class Study(
}

def admin(id: StudyId) = Secure(_.StudyAdmin) { ctx ?=> me ?=>
Bus.publish(AdminStudy(id, me.userId), "adminStudy")
env.study.api
.adminInvite(id)
.adminInvite(id, me.userId)
.inject:
if HTTPRequest.isXhr(ctx.req) then NoContent else Redirect(routes.Study.show(id))
}
Expand Down
14 changes: 14 additions & 0 deletions modules/relay/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ final class Env(
_ so api.requestPlay(id into RelayRoundId, v)
}
},
"kickStudy" -> { case lila.study.actorApi.Kick(studyId, userId, who) =>
roundRepo
.tourIdByStudyId(studyId)
.foreach(_.foreach: tourId =>
api.kickBroadcast(userId, tourId, who))
},
"adminStudy" -> { case lila.study.actorApi.AdminStudy(studyId, by) =>
roundRepo
.tourIdByStudyId(studyId)
.foreach(_.foreach: tourId =>
api
.roundIdsById(tourId)
.foreach(_.foreach(studyId => studyApi.adminInvite(studyId, by))))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using foreach, I can't use .parallel anymore but I assume the futures will be parallelised

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing as for kick, adminInvite is called twice for the original studyId when part of a broadcast, which eventually boils down to

  def admin(by: UserId)(study: Study): Funit =
    studyRepo.coll:
      _.update
        .one(
          $id(study.id),
          $set(s"members.${by}" -> $doc("role" -> "w", "admin" -> true)) ++
            $addToSet("uids" -> by)
        )
        .void

which is to be idempotent (sequence is a set). Maybe we want to add additional check though.

},
"isOfficialRelay" -> { case lila.study.actorApi.IsOfficialRelay(studyId, promise) =>
promise completeWith api.isOfficial(studyId)
}
Expand Down
6 changes: 6 additions & 0 deletions modules/relay/src/main/RelayApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ final class RelayApi(
def byTourOrdered(tour: RelayTour): Fu[List[RelayRound.WithTour]] =
roundRepo.byTourOrdered(tour).dmap(_.map(_ withTour tour))

def roundIdsById(tourId: RelayTour.Id): Fu[List[StudyId]] =
roundRepo.idsByTourId(tourId)

def kickBroadcast(userId: UserId, tourId: RelayTour.Id, who: UserId): Unit =
roundIdsById(tourId).foreach(_.foreach(studyId => studyApi.kick(studyId, userId, who)))

def withRounds(tour: RelayTour) = roundRepo.byTourOrdered(tour).dmap(tour.withRounds)

def denormalizeTourActive(tourId: RelayTour.Id): Funit =
Expand Down
1 change: 1 addition & 0 deletions modules/relay/src/main/RelayRound.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import lila.study.Study
import lila.common.Seconds

case class RelayRound(
/* Same as the Study id it refers to */
_id: RelayRoundId,
tourId: RelayTour.Id,
name: RelayRoundName,
Expand Down
10 changes: 10 additions & 0 deletions modules/relay/src/main/RelayRoundRepo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ final private class RelayRoundRepo(val coll: Coll)(using Executor):
.list(RelayTour.maxRelays)
.map(_.flatMap(_.getAsOpt[RelayRoundId]("_id")))

def tourIdByStudyId(studyId: StudyId): Fu[Option[RelayTour.Id]] =
coll.byId[Bdoc](studyId).map(_.flatMap(_.getAsOpt[RelayTour.Id]("tourId")))

def idsByTourId(tourId: RelayTour.Id): Fu[List[StudyId]] =
coll
.find(selectors.tour(tourId))
.cursor[Bdoc]()
.list(RelayTour.maxRelays)
.map(_.flatMap(_.getAsOpt[StudyId]("_id")))

def lastByTour(tour: RelayTour): Fu[Option[RelayRound]] =
coll
.find(selectors tour tour.id)
Expand Down
10 changes: 5 additions & 5 deletions modules/study/src/main/StudyApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -362,13 +362,13 @@ final class StudyApi(
)
.void

def kick(studyId: StudyId, userId: UserId)(who: Who) =
def kick(studyId: StudyId, userId: UserId, who: UserId) =
sequenceStudy(studyId): study =>
studyRepo
.isAdminMember(study, who.u)
.isAdminMember(study, who)
.flatMap: isAdmin =>
val allowed = study.isMember(userId) && {
(isAdmin && !study.isOwner(userId)) || (study.isOwner(who.u) ^ (who.u == userId))
(isAdmin && !study.isOwner(userId)) || (study.isOwner(who) ^ (who == userId))
}
allowed.so:
studyRepo.removeMember(study, userId) andDo
Expand Down Expand Up @@ -777,8 +777,8 @@ final class StudyApi(
Contribute(by.id, study):
chapterRepo deleteByStudy study

def adminInvite(studyId: StudyId)(using Me): Funit =
sequenceStudy(studyId)(inviter.admin)
def adminInvite(studyId: StudyId, by: UserId): Funit =
sequenceStudy(studyId)(inviter.admin(by))

private def indexStudy(study: Study) =
Bus.publish(actorApi.SaveStudy(study), "study")
Expand Down
6 changes: 3 additions & 3 deletions modules/study/src/main/StudyInvite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ final private class StudyInvite(
.void
yield invited

def admin(study: Study)(using me: Me): Funit =
def admin(by: UserId)(study: Study): Funit =
studyRepo.coll:
_.update
.one(
$id(study.id),
$set(s"members.${me.userId}" -> $doc("role" -> "w", "admin" -> true)) ++
$addToSet("uids" -> me.userId)
$set(s"members.${by}" -> $doc("role" -> "w", "admin" -> true)) ++
$addToSet("uids" -> by)
)
.void
9 changes: 5 additions & 4 deletions modules/study/src/main/StudySocket.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,13 @@ final private class StudySocket(
case "kick" =>
o.get[UserStr]("d")
.foreach: username =>
applyWho(api.kick(studyId, username.id))
applyWho: w =>
api.kick(studyId, username.id, w.u)
Bus.publish(actorApi.Kick(studyId, username.id, w.u), "kickStudy")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mean api.kick is called twice for studyId when it's in a broadcast, but should be fine since api.kick check if the user is present.


case "leave" =>
who.foreach: w =>
api.kick(studyId, w.u)(w)
api.kick(studyId, w.u, w.u)

case "shapes" =>
reading[AtPosition](o): position =>
Expand Down Expand Up @@ -237,9 +239,8 @@ final private class StudySocket(
)

case "relaySync" =>
who.foreach(w =>
applyWho: w =>
Bus.publish(actorApi.RelayToggle(studyId, ~(o \ "d").asOpt[Boolean], w), "relayToggle")
)

case t => logger.warn(s"Unhandled study socket message: $t")

Expand Down
2 changes: 2 additions & 0 deletions modules/study/src/main/actorApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ case class ExplorerGame(ch: StudyChapterId, path: UciPath, gameId: GameId, inser

case class Who(u: UserId, sri: lila.socket.Socket.Sri)
case class RelayToggle(studyId: StudyId, v: Boolean, who: Who)
case class Kick(studyId: StudyId, userId: UserId, who: UserId)
case class AdminStudy(studyId: StudyId, by: UserId)
case class IsOfficialRelay(studyId: StudyId, promise: Promise[Boolean])