teams frontend

pull/83/head
Thibault Duplessis 2012-12-13 04:18:59 +01:00
parent 05aabfd2c1
commit fe9847a938
18 changed files with 168 additions and 61 deletions

View File

@ -57,6 +57,7 @@ final class CoreEnv private (application: Application, val settings: Settings) {
sendMessage = message.api.lichessThread,
makeForum = forum.categApi.makeTeam,
getForumNbPosts = forum.categApi.getTeamNbPosts,
getForumPosts = forum.recent.team,
mongodb = mongodb.apply _)
lazy val lobby = new lila.lobby.LobbyEnv(

View File

@ -16,4 +16,6 @@ case class Categ(
def isStaff = slug == "staff"
def isTeam = team.nonEmpty
def id = slug
}

View File

@ -40,7 +40,8 @@ final class CategApi(env: ForumEnv) {
userId = "lichess".some,
ip = none,
text = "Welcome to the %s forum!\nOnly members of the team can post here" format name,
number = 1)
number = 1,
categId = categ.id)
_ env.categRepo saveIO categ
_ env.postRepo saveIO post
// denormalize topic

View File

@ -14,6 +14,10 @@ final class CategRepo(
findOneById(slug)
}
def byIds(ids: Iterable[String]): IO[List[Categ]] = io {
find("_id" $in ids).toList
}
val all: IO[List[Categ]] = io {
find(DBObject()).sort(DBObject("pos" -> 1)).toList
}

View File

@ -10,6 +10,7 @@ import user.User
case class Post(
@Key("_id") id: String,
topicId: String,
categId: String,
author: Option[String],
userId: Option[String],
ip: Option[String],
@ -26,6 +27,7 @@ object Post {
def apply(
topicId: String,
categId: String,
author: Option[String],
userId: Option[String],
ip: Option[String],
@ -38,5 +40,6 @@ object Post {
ip = ip,
text = text,
number = number,
createdAt = DateTime.now)
createdAt = DateTime.now,
categId = categId)
}

View File

@ -33,7 +33,8 @@ final class PostApi(
userId = ctx.me map (_.id),
ip = ctx.isAnon option ctx.req.remoteAddress,
text = data.text,
number = number + 1)
number = number + 1,
categId = categ.id)
_ env.postRepo saveIO post
// denormalize topic
_ env.topicRepo saveIO topic.copy(
@ -55,15 +56,18 @@ final class PostApi(
)
} yield (topicOption |@| postOption).tupled
def view(post: Post): IO[Option[PostView]] = for {
topicOption env.topicRepo byId post.topicId
categOption topicOption.fold(
topic env.categRepo bySlug topic.categId,
io(none[Categ])
)
} yield topicOption |@| categOption apply {
case (topic, categ) PostView(post, topic, categ, lastPageOf(topic))
}
def views(posts: List[Post]): IO[List[PostView]] = for {
topics env.topicRepo byIds posts.map(_.topicId).distinct
categs env.categRepo byIds topics.map(_.categId).distinct
} yield (for {
post posts
} yield for {
topic topics find (_.id == post.topicId)
categ categs find (_.slug == topic.categId)
} yield PostView(post, topic, categ, lastPageOf(topic))
).flatten
def view(post: Post): IO[Option[PostView]] = views(List(post)) map (_.headOption)
def lastNumberOf(topic: Topic): IO[Int] =
env.postRepo lastByTopics List(topic) map (_.number)
@ -96,7 +100,7 @@ final class PostApi(
} yield ()
)
post = view.post
_ modLog.deletePost(mod, post.userId, post.author, post.ip,
_ modLog.deletePost(mod, post.userId, post.author, post.ip,
text = "%s / %s / %s".format(view.categ.name, view.topic.name, post.text))
} yield (),
io()

View File

@ -26,8 +26,8 @@ final class PostRepo(
find(DBObject()).toList
}
def recent(nb: Int): IO[List[Post]] = io {
find(DBObject()).sort(sortQuery(-1)).limit(nb).toList.reverse
def recentInCategs(nb: Int)(categIds: List[String]): IO[List[Post]] = io {
find("categId" $in categIds).sort(sortQuery(-1)).limit(nb).toList
}
val sortQuery: DBObject = sortQuery(1)

View File

@ -9,22 +9,40 @@ import scalaz.effects._
final class Recent(postRepo: PostRepo, postApi: PostApi, timeout: Int) {
val nb = 30
private val nb = 20
private val cache = Builder.cache[Boolean, List[PostView]](timeout, staff
fetch(staff).unsafePerformIO
private val Public = "$public"
private val Staff = "$staff"
private def teamSlug(id: String) = "team-" + id
private lazy val publicCategIds = List(
"game-analysis",
"general-chess-discussion",
"lichess-feedback",
"off-topic-discussion")
private lazy val staffCategIds = "staff" :: publicCategIds
private val cache = Builder.cache[String, List[PostView]](timeout, key
fetch(key).unsafePerformIO
)
def apply(user: Option[User]): IO[List[PostView]] = io {
cache get Granter.option(Permission.StaffForum)(user)
cache get Granter.option(Permission.StaffForum)(user).fold(Staff, Public)
}
def team(teamId: String): IO[List[PostView]] = io {
cache get teamId
}
val invalidate: IO[Unit] = io(cache.invalidateAll)
private def fetch(staff: Boolean): IO[List[PostView]] = for {
posts postRepo.recent(nb)
views (posts map postApi.view).sequence
} yield views collect {
case Some(v) if (staff || v.categ.slug != "staff") v
}
private def fetch(key: String): IO[List[PostView]] = for {
posts postRepo.recentInCategs(nb)(key match {
case Public publicCategIds
case Staff staffCategIds
case teamId List(teamSlug(teamId))
})
views postApi views posts
} yield views
}

View File

@ -33,7 +33,8 @@ final class TopicApi(env: ForumEnv, maxPerPage: Int) {
userId = ctx.me map (_.id),
ip = ctx.isAnon option ctx.req.remoteAddress,
text = data.post.text,
number = 1)
number = 1,
categId = categ.id)
_ env.postRepo saveIO post
// denormalize topic
_ env.topicRepo saveIO topic.copy(

View File

@ -14,6 +14,10 @@ final class TopicRepo(
findOneById(id)
}
def byIds(ids: Iterable[String]): IO[List[Topic]] = io {
find("_id" $in ids).toList
}
def byCateg(categ: Categ): IO[List[Topic]] = io {
find(DBObject("categId" -> categ.slug)).toList
}

View File

@ -5,7 +5,7 @@ import core.Settings
import site.Captcha
import user.UserRepo
import message.LichessThread
import forum.Categ
import forum.{ Categ, PostView }
import com.mongodb.casbah.MongoCollection
import scalaz.effects._
@ -17,6 +17,7 @@ final class TeamEnv(
sendMessage: LichessThread IO[Unit],
makeForum: (String, String) IO[Unit],
getForumNbPosts: String IO[Int],
getForumPosts: String IO[List[PostView]],
mongodb: String MongoCollection) {
import settings._
@ -51,7 +52,8 @@ final class TeamEnv(
memberRepo = memberRepo,
requestRepo = requestRepo,
userRepo = userRepo,
getForumNbPosts = getForumNbPosts) _
getForumNbPosts = getForumNbPosts,
getForumPosts = getForumPosts) _
lazy val forms = new DataForm(teamRepo, captcha)

View File

@ -3,7 +3,7 @@ package team
import user.{ User, UserRepo }
import game.{ GameRepo, DbGame }
import forum.Categ
import forum.PostView
import http.Context
import scalaz.effects._
@ -15,15 +15,8 @@ case class TeamInfo(
requests: List[RequestWithUser],
bestPlayers: List[User],
averageElo: Int,
forumNbPosts: Int) {
// rank: Option[(Int, Int)],
// nbPlaying: Int,
// nbWithMe: Option[Int],
// nbBookmark: Int,
// eloWithMe: Option[List[(String, Int)]],
// eloChart: Option[EloChart],
// winChart: Option[WinChart],
// spy: Option[TeamSpy]) {
forumNbPosts: Int,
forumPosts: List[PostView]) {
def hasRequests = requests.nonEmpty
}
@ -35,7 +28,8 @@ object TeamInfo {
memberRepo: MemberRepo,
requestRepo: RequestRepo,
userRepo: UserRepo,
getForumNbPosts: String IO[Int])(team: Team, me: Option[User]): IO[TeamInfo] = for {
getForumNbPosts: String IO[Int],
getForumPosts: String IO[List[PostView]])(team: Team, me: Option[User]): IO[TeamInfo] = for {
mine ~me.map(api.belongsTo(team, _))
requestedByMe ~me.map(m requestRepo.exists(team.id, m.id)) doUnless mine
requests api.requestsWithUsers(team) doIf {
@ -45,6 +39,7 @@ object TeamInfo {
bestPlayers userRepo.byIdsSortByElo(userIds, 5)
averageElo userRepo.idsAverageElo(userIds)
forumNbPosts getForumNbPosts(team.id)
forumPosts getForumPosts(team.id)
} yield TeamInfo(
mine = mine,
createdByMe = ~me.map(m team.isCreator(m.id)),
@ -52,5 +47,6 @@ object TeamInfo {
requests = requests,
bestPlayers = bestPlayers,
averageElo = averageElo,
forumNbPosts = forumNbPosts)
forumNbPosts = forumNbPosts,
forumPosts = forumPosts)
}

View File

@ -7,7 +7,7 @@ import play.api.templates.Html
trait AssetHelper {
val assetVersion = 17
val assetVersion = 18
def cssTag(name: String) = css("stylesheets/" + name)

View File

@ -1,5 +1,5 @@
@(posts: List[lila.forum.PostView])(implicit ctx: Context)
@for(v <- posts) {
@for(v <- posts.reverse) {
@defining(routes.ForumTopic.show(v.categ.slug, v.topic.slug, v.topicLastPage) + "#" + v.post.number) { postUrl =>
<li>
<a class="post_link" href="@postUrl">@v.topic.name</a>

View File

@ -18,7 +18,6 @@
<section class="infos">
<p>@trans.averageElo(): <strong>@info.averageElo</strong></p>
<p>@trans.teamLeader(): @userIdLinkMini(t.createdBy)</p>
<p><a href="@teamForumUrl(t.id)">@trans.forum() (@info.forumNbPosts)</a>
</section>
<h2>@trans.teamBestPlayers()</h2>
<ol class="userlist">
@ -50,14 +49,34 @@
</form>
}
}
@if(info.mine && !info.createdByMe) {
<form class="inline" method="post" action="@routes.Team.quit(t.id)">
<input class="submit button" type="submit" value="@trans.quitTeam()" />
</form>
}
@if(info.createdByMe) {
<a href="@routes.Team.edit(t.id)" class="submit button">@trans.settings()</a>
}
</div>
<div class="forum">
<h2><a href="@teamForumUrl(t.id)">@trans.forum() (@info.forumNbPosts)</a></h2>
<ol class="posts">
@info.forumPosts.take(5).map { v =>
@defining(routes.ForumTopic.show(v.categ.slug, v.topic.slug, v.topicLastPage) + "#" + v.post.number) { postUrl =>
<li>
<p class="meta clearfix">
<a href="@postUrl">@v.topic.name</a>
@authorLink(v.post, withOnline = false)
<span class="date">@showDate(v.post.createdAt)</span>
</p>
<p>@shorten(v.post.text, 200)</p>
</li>
}
}
</ol>
<a class="more" href="@teamForumUrl(t.id)">@t.name @trans.forum() »</a>
<div class="actions">
@if(info.mine && !info.createdByMe) {
<form class="quit" method="post" action="@routes.Team.quit(t.id)">
<input class="submit button small confirm" type="submit" value="@trans.quitTeam()" />
</form>
}
@if(info.createdByMe) {
<a href="@routes.Team.edit(t.id)" class="submit button small">@trans.settings()</a>
}
</div>
</div>
</div>
</div>

View File

@ -93,7 +93,8 @@ body.dark div.notifications > div,
body.dark form.wide input[type="text"],
body.dark form.wide textarea,
body.dark #team .team-left,
body.dark #team .team-left h2
body.dark #team .team-right,
body.dark #team h2
{
border-color: #3e3e3e;
}
@ -117,6 +118,7 @@ body.dark div.anon_chat span,
body.dark div.anon_chat a.user_link,
body.dark div.undertable a.user_link,
body.dark div.new_posts li span,
body.dark #team .forum a.user_link,
body.dark span.board_mark
{
color: #808080;
@ -214,7 +216,7 @@ body.dark div.user_show .elo_with_me,
body.dark div.content_box_inter,
body.dark #GameText tr:nth-child(even),
body.dark table.slist tbody tr:nth-child(even),
body.dark #team .userlist li:nth-child(even),
body.dark #team .forum li:nth-child(odd),
body.dark table.translations tbody tr:nth-child(even),
body.dark form.translation_form div.message:nth-child(even),
body.dark div.content_box table.datatable tr:nth-child(odd),

View File

@ -27,7 +27,7 @@
#team form textarea {
height: 8em;
}
#team form .submit {
#team form.wide .submit {
margin-left: 110px;
}
@ -37,12 +37,16 @@
color: red;
}
#team table a.team-name {
#team table.slist td {
padding-top: 1em;
padding-bottom: 1em;
}
#team table.slist a.team-name {
font-size: 1.3em;
display: block;
margin-bottom: 4px;
}
#team table .info {
#team table.slist .info {
font-size: 0.9em;
white-space: nowrap;
}
@ -66,17 +70,19 @@
#team .team-left h2 {
font-size: 1.0em;
font-weight: bold;
padding-left: 25px;
padding: 0 0 5px 25px;
border-bottom: 1px solid #ccc;
}
#team .team-left .infos {
margin: 20px 0 20px 25px;
}
#team .team-right {
margin-left: 300px;
margin-left: 280px;
border-left: 1px solid #ccc;
}
#team .description {
margin: 20px 0;
font-size: 1.2em;
margin: 20px 0 20px 20px;
}
#team .userlist {
@ -87,9 +93,48 @@
padding: 10px 0 10px 25px;
}
#team .userlist li:nth-child(even) {
#team .actions {
margin-left: 20px;
}
#team .forum {
margin-top: 30px;
width: 100%;
}
#team .forum h2 {
font-size: 1.2em;
font-weight: bold;
border-bottom: 1px solid #ccc;
padding: 0 0 5px 20px;
}
#team .forum h2 a {
text-decoration: none;
}
#team .forum li {
padding: 20px 10px 20px 20px;
}
#team .forum li:nth-child(odd) {
background: none repeat scroll 0 0 #F4F4F4;
}
#team .forum a.user_link {
font-weight: bold;
margin-right: 6px;
color: #aaa;
text-decoration: none;
}
#team .forum .date {
font-size: 0.8em;
}
#team .forum .meta > * {
display: block;
float: left;
margin-right: 1em;
}
#team .forum a.more {
display: block;
text-align: right;
margin: 10px 10px 0 0;
}
#team table.requests form {
margin: 0;
@ -111,3 +156,7 @@
display: inline;
margin-left: 5px;
}
#team .button.small {
font-size: 12px;
}

3
todo
View File

@ -54,5 +54,6 @@ db.team.ensureIndex({createdAt: -1})
db.team_member.ensureIndex({team:1})
db.team_member.ensureIndex({user:1})
db.team_member.ensureIndex({date: -1})
db.team_request.ensureIndex({team:1})
db.team_request.ensureIndex({team: 1})
db.team_request.ensureIndex({date: -1})
db.f_post.ensureIndex({categId: 1})