Merge branch 'team_declined_requests' of git://github.com/rglbr/lila into rglbr-team_declined_requests
* 'team_declined_requests' of git://github.com/rglbr/lila: add explicit conditions use team_requests collection for the declined requests consistent naming Team Declined requests page Track declined team requestshotfix-tablebase
commit
b702160f81
|
@ -392,12 +392,21 @@ final class Team(
|
|||
.fold(
|
||||
_ => fuccess(routes.Team.show(team.id).toString),
|
||||
{ case (decision, url) =>
|
||||
api.processRequest(team, request, decision == "accept") inject url
|
||||
api.processRequest(team, request, decision) inject url
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def declinedRequests(id: String, page: Int) =
|
||||
Auth { implicit ctx => _ =>
|
||||
WithOwnedTeamEnabled(id) { team =>
|
||||
paginator.declinedRequests(team, page) map { requests =>
|
||||
Ok(html.team.declinedRequest.all(team, requests))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def quit(id: String) =
|
||||
AuthOrScoped(_.Team.Write)(
|
||||
auth = implicit ctx =>
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila.app
|
|||
package mashup
|
||||
|
||||
import lila.forum.MiniForumPost
|
||||
import lila.team.{ RequestRepo, RequestWithUser, Team, TeamApi }
|
||||
import lila.team.{ Request, RequestRepo, RequestWithUser, Team, TeamApi }
|
||||
import lila.tournament.{ Tournament, TournamentApi }
|
||||
import lila.user.User
|
||||
import lila.swiss.{ Swiss, SwissApi }
|
||||
|
@ -11,7 +11,7 @@ import lila.simul.{ Simul, SimulApi }
|
|||
case class TeamInfo(
|
||||
mine: Boolean,
|
||||
ledByMe: Boolean,
|
||||
requestedByMe: Boolean,
|
||||
myRequest: Option[Request],
|
||||
subscribed: Boolean,
|
||||
requests: List[RequestWithUser],
|
||||
forum: Option[List[MiniForumPost]],
|
||||
|
@ -52,17 +52,17 @@ final class TeamInfoApi(
|
|||
|
||||
def apply(team: Team, me: Option[User], withForum: Boolean => Boolean): Fu[TeamInfo] =
|
||||
for {
|
||||
requests <- (team.enabled && me.exists(m => team.leaders(m.id))) ?? api.requestsWithUsers(team)
|
||||
mine <- me.??(m => api.belongsTo(team.id, m.id))
|
||||
requestedByMe <- !mine ?? me.??(m => requestRepo.exists(team.id, m.id))
|
||||
subscribed <- me.ifTrue(mine) ?? { api.isSubscribed(team, _) }
|
||||
forumPosts <- withForum(mine) ?? forumRecent.team(team.id).dmap(some)
|
||||
tours <- tournaments(team, 5, 5)
|
||||
simuls <- simulApi.byTeamLeaders(team.id, team.leaders.toSeq)
|
||||
requests <- (team.enabled && me.exists(m => team.leaders(m.id))) ?? api.requestsWithUsers(team)
|
||||
mine <- me.??(m => api.belongsTo(team.id, m.id))
|
||||
myRequest <- !mine ?? me.??(m => requestRepo.find(team.id, m.id))
|
||||
subscribed <- me.ifTrue(mine) ?? { api.isSubscribed(team, _) }
|
||||
forumPosts <- withForum(mine) ?? forumRecent.team(team.id).dmap(some)
|
||||
tours <- tournaments(team, 5, 5)
|
||||
simuls <- simulApi.byTeamLeaders(team.id, team.leaders.toSeq)
|
||||
} yield TeamInfo(
|
||||
mine = mine,
|
||||
ledByMe = me.exists(m => team.leaders(m.id)),
|
||||
requestedByMe = requestedByMe,
|
||||
myRequest = myRequest,
|
||||
subscribed = subscribed,
|
||||
requests = requests,
|
||||
forum = forumPosts,
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package views.html.team
|
||||
|
||||
import controllers.routes
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.String.html.richText
|
||||
import lila.common.paginator.Paginator
|
||||
|
||||
object declinedRequest {
|
||||
|
||||
def all(team: lila.team.Team, requests: Paginator[lila.team.RequestWithUser])(implicit ctx: Context) = {
|
||||
val title = s"${team.name} • ${trans.team.declinedRequests.txt()}"
|
||||
|
||||
views.html.base.layout(
|
||||
title = title,
|
||||
moreCss = frag(cssTag("team"))
|
||||
) {
|
||||
val pager = views.html.base.bits
|
||||
.paginationByQuery(routes.Team.declinedRequests(team.id, 1), requests, showPost = true)
|
||||
main(cls := "page-menu page-small")(
|
||||
bits.menu(none),
|
||||
div(cls := "page-menu__content box box-pad")(
|
||||
h1(
|
||||
a(href := routes.Team.show(team.id))(
|
||||
team.name
|
||||
),
|
||||
" • ",
|
||||
trans.team.declinedRequests()
|
||||
),
|
||||
pager,
|
||||
table(cls := "slist")(
|
||||
tbody(
|
||||
requests.currentPageResults.map { request =>
|
||||
tr(
|
||||
td(userLink(request.user)),
|
||||
td(richText(request.message)),
|
||||
td(momentFromNow(request.date)),
|
||||
td(cls := "process")(
|
||||
postForm(
|
||||
cls := "process-request",
|
||||
action := routes.Team.requestProcess(request.id)
|
||||
)(
|
||||
input(
|
||||
tpe := "hidden",
|
||||
name := "url",
|
||||
value := routes.Team.declinedRequests(team.id, requests.currentPage)
|
||||
),
|
||||
button(name := "process", cls := "button button-green", value := "accept-declined")(
|
||||
trans.accept()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
),
|
||||
pager
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -48,7 +48,8 @@ object form {
|
|||
t.enabled option postForm(cls := "form3", action := routes.Team.update(t.id))(
|
||||
div(cls := "form-group")(
|
||||
a(cls := "button button-empty", href := routes.Team.leaders(t.id))(teamLeaders()),
|
||||
a(cls := "button button-empty", href := routes.Team.kick(t.id))(kickSomeone())
|
||||
a(cls := "button button-empty", href := routes.Team.kick(t.id))(kickSomeone()),
|
||||
a(cls := "button button-empty", href := routes.Team.declinedRequests(t.id))(declinedRequests())
|
||||
),
|
||||
entryFields(form, t.some),
|
||||
textFields(form),
|
||||
|
|
|
@ -90,7 +90,12 @@ object show {
|
|||
),
|
||||
div(cls := "team-show__actions")(
|
||||
(t.enabled && !info.mine) option frag(
|
||||
if (info.requestedByMe)
|
||||
if (info.myRequest.map(_.declined) | false)
|
||||
frag(
|
||||
strong(requestDeclined()),
|
||||
a(cls := "button disabled button-metal")(joinTeam())
|
||||
)
|
||||
else if (info.myRequest.isDefined)
|
||||
frag(
|
||||
strong(beingReviewed()),
|
||||
postForm(action := routes.Team.quit(t.id))(
|
||||
|
|
|
@ -365,6 +365,7 @@ POST /team/:id/quit controllers.Team.quit(id: String)
|
|||
GET /team/:id/request/new controllers.Team.requestForm(id: String)
|
||||
POST /team/:id/request/new controllers.Team.requestCreate(id: String)
|
||||
POST /team/:id/request/process controllers.Team.requestProcess(id: String)
|
||||
GET /team/:id/declined-requests controllers.Team.declinedRequests(id: String, page: Int ?=1)
|
||||
GET /team/:id/edit controllers.Team.edit(id: String)
|
||||
POST /team/:id/edit controllers.Team.update(id: String)
|
||||
GET /team/:id/kick controllers.Team.kickForm(id: String)
|
||||
|
|
|
@ -1656,6 +1656,7 @@ val `kickSomeone` = new I18nKey("team:kickSomeone")
|
|||
val `whoToKick` = new I18nKey("team:whoToKick")
|
||||
val `willBeReviewed` = new I18nKey("team:willBeReviewed")
|
||||
val `beingReviewed` = new I18nKey("team:beingReviewed")
|
||||
val `requestDeclined` = new I18nKey("team:requestDeclined")
|
||||
val `subToTeamMessages` = new I18nKey("team:subToTeamMessages")
|
||||
val `teamBattle` = new I18nKey("team:teamBattle")
|
||||
val `teamBattleOverview` = new I18nKey("team:teamBattleOverview")
|
||||
|
@ -1677,6 +1678,7 @@ val `incorrectEntryCode` = new I18nKey("team:incorrectEntryCode")
|
|||
val `teamAlreadyExists` = new I18nKey("team:teamAlreadyExists")
|
||||
val `upcomingTourns` = new I18nKey("team:upcomingTourns")
|
||||
val `completedTourns` = new I18nKey("team:completedTourns")
|
||||
val `declinedRequests` = new I18nKey("team:declinedRequests")
|
||||
val `nbMembers` = new I18nKey("team:nbMembers")
|
||||
val `teamLeaders` = new I18nKey("team:teamLeaders")
|
||||
val `xJoinRequests` = new I18nKey("team:xJoinRequests")
|
||||
|
|
|
@ -9,10 +9,13 @@ import lila.db.paginator._
|
|||
final private[team] class PaginatorBuilder(
|
||||
teamRepo: TeamRepo,
|
||||
memberRepo: MemberRepo,
|
||||
requestRepo: RequestRepo,
|
||||
userRepo: lila.user.UserRepo,
|
||||
lightUserApi: lila.user.LightUserApi
|
||||
)(implicit ec: scala.concurrent.ExecutionContext) {
|
||||
private val maxPerPage = MaxPerPage(15)
|
||||
private val maxUserPerPage = MaxPerPage(30)
|
||||
private val maxPerPage = MaxPerPage(15)
|
||||
private val maxUserPerPage = MaxPerPage(30)
|
||||
private val maxRequestsPerPage = MaxPerPage(10)
|
||||
|
||||
import BSONHandlers._
|
||||
|
||||
|
@ -54,4 +57,29 @@ final private[team] class PaginatorBuilder(
|
|||
private def selector = memberRepo teamQuery team.id
|
||||
private def sorting = $sort desc "date"
|
||||
}
|
||||
|
||||
def declinedRequests(team: Team, page: Int): Fu[Paginator[RequestWithUser]] =
|
||||
Paginator(
|
||||
adapter = new DeclinedRequestAdapter(team),
|
||||
page,
|
||||
maxRequestsPerPage
|
||||
)
|
||||
final private class DeclinedRequestAdapter(team: Team) extends AdapterLike[RequestWithUser] {
|
||||
val nbResults = requestRepo countDeclinedByTeam team.id
|
||||
private def selector = requestRepo teamDeclinedQuery team.id
|
||||
private def sorting = $sort desc "date"
|
||||
|
||||
def slice(offset: Int, length: Int): Fu[Seq[RequestWithUser]] = {
|
||||
for {
|
||||
requests <- requestRepo.coll
|
||||
.find(selector)
|
||||
.sort(sorting)
|
||||
.skip(offset)
|
||||
.cursor[Request]()
|
||||
.list(length)
|
||||
users <- userRepo usersFromSecondary requests.map(_.user)
|
||||
} yield requests zip users map { case (request, user) => RequestWithUser(request, user) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@ case class Request(
|
|||
team: String,
|
||||
user: String,
|
||||
message: String,
|
||||
date: DateTime
|
||||
date: DateTime,
|
||||
declined: Boolean
|
||||
) {
|
||||
|
||||
def id = _id
|
||||
|
@ -17,6 +18,7 @@ case class Request(
|
|||
|
||||
object Request {
|
||||
|
||||
type ID = String
|
||||
def makeId(team: Team.ID, user: User.ID) = s"$user@$team"
|
||||
|
||||
def make(team: Team.ID, user: User.ID, message: String): Request =
|
||||
|
@ -25,7 +27,8 @@ object Request {
|
|||
user = user,
|
||||
team = team,
|
||||
message = message.trim,
|
||||
date = DateTime.now
|
||||
date = DateTime.now,
|
||||
declined = false
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package lila.team
|
|||
|
||||
import lila.db.dsl._
|
||||
import lila.user.User
|
||||
|
||||
final class RequestRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionContext) {
|
||||
|
||||
import BSONHandlers._
|
||||
|
@ -15,18 +14,21 @@ final class RequestRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionC
|
|||
def find(teamId: ID, userId: ID): Fu[Option[Request]] =
|
||||
coll.one[Request](selectId(teamId, userId))
|
||||
|
||||
def countByTeam(teamId: ID): Fu[Int] =
|
||||
coll.countSel(teamQuery(teamId))
|
||||
def countDeclinedByTeam(teamId: ID): Fu[Int] =
|
||||
coll.countSel(teamDeclinedQuery(teamId))
|
||||
|
||||
def findByTeam(teamId: ID): Fu[List[Request]] =
|
||||
coll.list[Request](teamQuery(teamId))
|
||||
def findActiveByTeam(teamId: ID): Fu[List[Request]] =
|
||||
coll.list[Request](teamActiveQuery(teamId))
|
||||
|
||||
def findByTeams(teamIds: List[ID]): Fu[List[Request]] =
|
||||
teamIds.nonEmpty ?? coll.list[Request](teamsQuery(teamIds))
|
||||
def findActiveByTeams(teamIds: List[ID]): Fu[List[Request]] =
|
||||
teamIds.nonEmpty ?? coll.list[Request](teamsActiveQuery(teamIds))
|
||||
|
||||
def selectId(teamId: ID, userId: ID) = $id(Request.makeId(teamId, userId))
|
||||
def teamQuery(teamId: ID) = $doc("team" -> teamId)
|
||||
def teamsQuery(teamIds: List[ID]) = $doc("team" $in teamIds)
|
||||
def selectId(teamId: ID, userId: ID) = $id(Request.makeId(teamId, userId))
|
||||
def teamQuery(teamId: ID) = $doc("team" -> teamId)
|
||||
def teamsQuery(teamIds: List[ID]) = $doc("team" $in teamIds)
|
||||
def teamDeclinedQuery(teamId: ID) = $and(teamQuery(teamId), $doc("declined" -> true))
|
||||
def teamActiveQuery(teamId: ID) = $and(teamQuery(teamId), $doc("declined" -> $ne(true)))
|
||||
def teamsActiveQuery(teamIds: List[ID]) = $and(teamsQuery(teamIds), $doc("declined" -> $ne(true)))
|
||||
|
||||
def getByUserId(userId: User.ID) =
|
||||
coll.list[Request]($doc("user" -> userId))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package lila.team
|
||||
|
||||
import actorApi._
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.Period
|
||||
import play.api.libs.json.{ JsSuccess, Json }
|
||||
import reactivemongo.api.{ Cursor, ReadPreference }
|
||||
|
@ -40,7 +41,7 @@ final class TeamApi(
|
|||
|
||||
def lightsByLeader = teamRepo.lightsByLeader _
|
||||
|
||||
def request(id: Team.ID) = requestRepo.coll.byId[Request](id)
|
||||
def request(id: Request.ID) = requestRepo.coll.byId[Request](id)
|
||||
|
||||
def create(setup: TeamSetup, me: User): Fu[Team] = {
|
||||
val bestId = Team.nameToId(setup.name)
|
||||
|
@ -106,7 +107,7 @@ final class TeamApi(
|
|||
|
||||
def requestsWithUsers(team: Team): Fu[List[RequestWithUser]] =
|
||||
for {
|
||||
requests <- requestRepo findByTeam team.id
|
||||
requests <- requestRepo findActiveByTeam team.id
|
||||
users <- userRepo usersFromSecondary requests.map(_.user)
|
||||
} yield requests zip users map { case (request, user) =>
|
||||
RequestWithUser(request, user)
|
||||
|
@ -115,7 +116,7 @@ final class TeamApi(
|
|||
def requestsWithUsers(user: User): Fu[List[RequestWithUser]] =
|
||||
for {
|
||||
teamIds <- teamRepo enabledTeamIdsByLeader user.id
|
||||
requests <- requestRepo findByTeams teamIds
|
||||
requests <- requestRepo findActiveByTeams teamIds
|
||||
users <- userRepo usersFromSecondary requests.map(_.user)
|
||||
} yield requests zip users map { case (request, user) =>
|
||||
RequestWithUser(request, user)
|
||||
|
@ -159,16 +160,19 @@ final class TeamApi(
|
|||
}
|
||||
}
|
||||
|
||||
def processRequest(team: Team, request: Request, accept: Boolean): Funit =
|
||||
for {
|
||||
_ <- requestRepo.coll.delete.one(request)
|
||||
_ = cached.nbRequests invalidate team.createdBy
|
||||
userOption <- userRepo byId request.user
|
||||
_ <-
|
||||
userOption
|
||||
.filter(_ => accept)
|
||||
.??(user => doJoin(team, user) >> notifier.acceptRequest(team, request))
|
||||
} yield ()
|
||||
def processRequest(team: Team, request: Request, decision: String): Funit = {
|
||||
cached.nbRequests invalidate team.createdBy
|
||||
if (decision == "decline")
|
||||
requestRepo.coll.update.one($id(request.id), request.copy(declined = true)).void
|
||||
else if (decision == "accept" | decision == "accept-declined")
|
||||
for {
|
||||
_ <- requestRepo.remove(request.id)
|
||||
userOption <- userRepo byId request.user
|
||||
_ <-
|
||||
userOption.??(user => doJoin(team, user) >> notifier.acceptRequest(team, request))
|
||||
} yield ()
|
||||
else funit
|
||||
}
|
||||
|
||||
def deleteRequestsByUserId(userId: User.ID) =
|
||||
requestRepo.getByUserId(userId) flatMap {
|
||||
|
|
|
@ -108,13 +108,25 @@ final class TeamRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionCont
|
|||
.aggregateOne(readPreference = ReadPreference.secondaryPreferred) { implicit framework =>
|
||||
import framework._
|
||||
Match($doc("leaders" -> userId)) -> List(
|
||||
Group(BSONNull)("ids" -> PushField("_id")),
|
||||
PipelineOperator(
|
||||
$doc(
|
||||
"$lookup" -> $doc(
|
||||
"from" -> requestColl.name,
|
||||
"localField" -> "_id",
|
||||
"foreignField" -> "team",
|
||||
"as" -> "requests"
|
||||
"from" -> requestColl.name,
|
||||
"as" -> "requests",
|
||||
"let" -> $doc("teams" -> "$ids"),
|
||||
"pipeline" -> $arr(
|
||||
$doc(
|
||||
"$match" -> $doc(
|
||||
"$expr" -> $doc(
|
||||
"$and" -> $arr(
|
||||
$doc("$in" -> $arr("$team", "$$teams")),
|
||||
$doc("$ne" -> $arr("$declined", true))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
</plurals>
|
||||
<string name="willBeReviewed">Your join request will be reviewed by a team leader.</string>
|
||||
<string name="beingReviewed">Your join request is being reviewed by a team leader.</string>
|
||||
<string name="requestDeclined">Your join request was declined by a team leader.</string>
|
||||
<string name="subToTeamMessages">Subscribe to team messages</string>
|
||||
<string name="teamBattle">Team Battle</string>
|
||||
<string name="teamBattleOverview">A battle of multiple teams, each player scores points for their team</string>
|
||||
|
@ -52,4 +53,5 @@ Players who don't like receiving your messages might leave the team.</string>
|
|||
<string name="teamAlreadyExists">This team already exists.</string>
|
||||
<string name="upcomingTourns">Upcoming tournaments</string>
|
||||
<string name="completedTourns">Completed tournaments</string>
|
||||
<string name="declinedRequests">Declined Requests</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@import '../../../common/css/plugin';
|
||||
@import '../../../common/css/component/pagination';
|
||||
@import '../../../common/css/component/slist';
|
||||
@import '../../../common/css/form/form3';
|
||||
@import '../../../common/css/form/captcha';
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.pagination {
|
||||
padding: 1em;
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
@import 'list';
|
||||
@import 'show';
|
||||
@import 'tournaments';
|
||||
@import 'declined-request';
|
||||
|
|
Loading…
Reference in New Issue