more work on messaging
This commit is contained in:
parent
988714bb0d
commit
bdb199b936
|
@ -24,7 +24,21 @@ object Message extends LilaController {
|
|||
IOptionOk(api.thread(id, me)) { html.message.thread(_) }
|
||||
}
|
||||
|
||||
def form = TODO
|
||||
def create = TODO
|
||||
def form = Auth { implicit ctx ⇒
|
||||
implicit me ⇒
|
||||
Ok(html.message.form(forms.thread))
|
||||
}
|
||||
|
||||
def create = AuthBody { implicit ctx ⇒
|
||||
implicit me ⇒
|
||||
implicit val req = ctx.body
|
||||
forms.thread.bindFromRequest.fold(
|
||||
err ⇒ BadRequest(html.message.form(err)),
|
||||
data ⇒ api.makeThread(data, me).map(thread ⇒
|
||||
Redirect(routes.Message.thread(thread.id))
|
||||
).unsafePerformIO
|
||||
)
|
||||
}
|
||||
|
||||
def delete(id: String) = TODO
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ final class DataForm(captcher: Captcha) {
|
|||
|
||||
import DataForm._
|
||||
|
||||
def postMapping = mapping(
|
||||
val postMapping = mapping(
|
||||
"text" -> text(minLength = 3),
|
||||
"author" -> optional(text),
|
||||
"gameId" -> nonEmptyText,
|
||||
|
@ -20,11 +20,11 @@ final class DataForm(captcher: Captcha) {
|
|||
data ⇒ captcher get data.gameId valid data.move.trim.toLowerCase
|
||||
)
|
||||
|
||||
def post = Form(postMapping)
|
||||
val post = Form(postMapping)
|
||||
|
||||
def postWithCaptcha = post -> captchaCreate
|
||||
|
||||
def topic = Form(mapping(
|
||||
val topic = Form(mapping(
|
||||
"name" -> text(minLength = 3),
|
||||
"post" -> postMapping
|
||||
)(TopicData.apply)(TopicData.unapply))
|
||||
|
|
|
@ -9,19 +9,32 @@ import scala.math.ceil
|
|||
|
||||
final class Api(
|
||||
threadRepo: ThreadRepo,
|
||||
unreadCache: UnreadCache,
|
||||
maxPerPage: Int) {
|
||||
|
||||
def inbox(user: User, page: Int): Paginator[Thread] = Paginator(
|
||||
def inbox(me: User, page: Int): Paginator[Thread] = Paginator(
|
||||
SalatAdapter(
|
||||
dao = threadRepo,
|
||||
query = threadRepo visibleByUserQuery user,
|
||||
query = threadRepo visibleByUserQuery me,
|
||||
sort = threadRepo.sortQuery),
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage
|
||||
) | inbox(user, 1)
|
||||
) | inbox(me, 1)
|
||||
|
||||
def thread(id: String, user: User): IO[Option[Thread]] =
|
||||
threadRepo byId id map {
|
||||
_ filter (_ hasUser user)
|
||||
def thread(id: String, me: User): IO[Option[Thread]] =
|
||||
threadRepo byId id map {
|
||||
_ filter (_ hasUser me)
|
||||
}
|
||||
|
||||
def makeThread(data: DataForm.ThreadData, me: User) = {
|
||||
val thread = Thread(
|
||||
name = data.subject,
|
||||
text = data.text,
|
||||
creator = me,
|
||||
invited = data.user)
|
||||
for {
|
||||
_ ← threadRepo saveIO thread
|
||||
_ ← io(unreadCache invalidate data.user)
|
||||
} yield thread
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,40 @@
|
|||
package lila
|
||||
package message
|
||||
|
||||
import user.{ User, UserRepo }
|
||||
|
||||
import play.api.data._
|
||||
import play.api.data.Forms._
|
||||
|
||||
object DataForm {
|
||||
final class DataForm(userRepo: UserRepo) {
|
||||
|
||||
import DataForm._
|
||||
|
||||
val thread = Form(mapping(
|
||||
"username" -> nonEmptyText.verifying("Unknown username", usernameExists _),
|
||||
"subject" -> text(minLength = 3),
|
||||
"text" -> text(minLength = 3)
|
||||
)({
|
||||
case (username, subject, text) ⇒ ThreadData(
|
||||
user = fetchUser(username) err "Unknown username " + username,
|
||||
subject = subject,
|
||||
text = text)
|
||||
})(_.export.some))
|
||||
|
||||
private def fetchUser(username: String) =
|
||||
(userRepo byUsername username).unsafePerformIO
|
||||
|
||||
private def usernameExists(username: String) =
|
||||
fetchUser(username).isDefined
|
||||
}
|
||||
|
||||
object DataForm {
|
||||
|
||||
case class ThreadData(
|
||||
user: User,
|
||||
subject: String,
|
||||
text: String) {
|
||||
|
||||
def export = (user.username, subject, text)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,12 @@ final class MessageEnv(
|
|||
|
||||
lazy val threadRepo = new ThreadRepo(mongodb(MongoCollectionMessageThread))
|
||||
|
||||
lazy val unreadCache = new UnreadCache(threadRepo)
|
||||
|
||||
lazy val api = new Api(
|
||||
threadRepo = threadRepo,
|
||||
unreadCache = unreadCache,
|
||||
maxPerPage = MessageThreadMaxPerPage)
|
||||
|
||||
lazy val unread = new UnreadCache(threadRepo)
|
||||
|
||||
lazy val forms = DataForm
|
||||
lazy val forms = new DataForm(userRepo)
|
||||
}
|
||||
|
|
13
app/message/MessageHelper.scala
Normal file
13
app/message/MessageHelper.scala
Normal file
|
@ -0,0 +1,13 @@
|
|||
package lila
|
||||
package message
|
||||
|
||||
import core.CoreEnv
|
||||
import http.Context
|
||||
|
||||
trait MessageHelper {
|
||||
|
||||
protected def env: CoreEnv
|
||||
|
||||
def messageNbUnread(ctx: Context) =
|
||||
ctx.me.fold(env.message.unreadCache.get, 0)
|
||||
}
|
|
@ -41,7 +41,7 @@ case class Thread(
|
|||
def nonEmptyName = (name.trim.some filter ("" !=)) | "No subject"
|
||||
}
|
||||
|
||||
object Topic {
|
||||
object Thread {
|
||||
|
||||
val idSize = 8
|
||||
|
||||
|
|
|
@ -15,5 +15,7 @@ final class UnreadCache(threadRepo: ThreadRepo) {
|
|||
(threadRepo userNbUnread user).unsafePerformIO
|
||||
})
|
||||
|
||||
def invalidate(user: User) = cache - user.usernameCanonical
|
||||
def invalidate(user: User) {
|
||||
cache - user.usernameCanonical
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ object Environment
|
|||
with JsonHelper
|
||||
with PaginatorHelper
|
||||
with FormHelper
|
||||
with message.MessageHelper
|
||||
with round.RoundHelper
|
||||
with game.GameHelper
|
||||
with user.UserHelper
|
||||
|
|
|
@ -10,18 +10,19 @@ final class SiteMenu(trans: I18nKeys) {
|
|||
|
||||
import SiteMenu._
|
||||
|
||||
val play = new Elem(routes.Lobby.home, trans.play)
|
||||
val game = new Elem(routes.Game.realtime, trans.games)
|
||||
val user = new Elem(routes.User.list(page = 1), trans.people)
|
||||
val forum = new Elem(routes.ForumCateg.index, trans.forum)
|
||||
val message = new Elem(routes.Message.inbox(page = 1), trans.inbox)
|
||||
val play = new Elem("play", routes.Lobby.home, trans.play)
|
||||
val game = new Elem("game", routes.Game.realtime, trans.games)
|
||||
val user = new Elem("user", routes.User.list(page = 1), trans.people)
|
||||
val forum = new Elem("forum", routes.ForumCateg.index, trans.forum)
|
||||
val message = new Elem("message", routes.Message.inbox(page = 1), trans.inbox)
|
||||
|
||||
val all = List(play, game, user, message, forum)
|
||||
val all = List(play, game, user, forum, message)
|
||||
}
|
||||
|
||||
object SiteMenu {
|
||||
|
||||
sealed class Elem(
|
||||
val code: String,
|
||||
val route: Call,
|
||||
val name: I18nKeys#Key) {
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(form: Form[Option[User]])(implicit flash: Flash)
|
||||
@(form: Form[_])(implicit flash: Flash)
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<meta content="@trans.freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents()" name="description">
|
||||
<meta name="google-site-verification" content="fZ08Imok7kcLaGcJg7BKQExO6vXGgSgsJUsW6JalUCo" />
|
||||
<link rel="shortcut icon" href="@routes.Assets.at("favicon.ico")" type="image/x-icon" />
|
||||
@if(!robots) {
|
||||
@if(!robots) {
|
||||
<meta content="noindex, nofollow" name="robots">
|
||||
}
|
||||
</head>
|
||||
|
@ -39,8 +39,15 @@
|
|||
</div>
|
||||
<a href="@routes.Setting.color()" class="colorpicker" data-color="@setting.color">@trans.color()</a>
|
||||
@siteMenu.all.map { elem =>
|
||||
@if(elem.code == "message") {
|
||||
<a class="goto_nav blank_if_play @elem.currentClass(active)" href="@elem.route">
|
||||
@elem.name()
|
||||
<span id="nb_messages" class="new_messages @{ (messageNbUnread(ctx) > 0).fold("unread", "") }">@messageNbUnread(ctx)</span>
|
||||
</a>
|
||||
} else {
|
||||
<a class="goto_nav blank_if_play @elem.currentClass(active)" href="@elem.route">@elem.name()</a>
|
||||
}
|
||||
}
|
||||
<div id="nb_connected_players" class="nb_connected_players none">
|
||||
@trans.nbConnectedPlayers("<strong>0</strong>")
|
||||
</div>
|
||||
|
@ -85,12 +92,11 @@
|
|||
<a href="@routes.Wiki.home()" class="blank_if_play">@trans.learnMoreAboutLichess()</a>
|
||||
</div>
|
||||
<a href="@routes.Wiki.home()">Wiki</a> |
|
||||
<a href="#" title="Having a suggestion, feature request or bug report? Let me know">Feedback</a> |
|
||||
<a href="@routes.ForumCateg.show("lichess-feedback", 1)" title="Having a suggestion, feature request or bug report? Let me know">Feedback</a> |
|
||||
<a href="http://github.com/ornicar/lila" target="_blank" title="See what's inside, fork and contribute">Source Code</a><br />
|
||||
<a href="#">Translate Lichess</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@jsTag("deps.min.js")
|
||||
@jsTag("socket.js")
|
||||
@jsTag("ctrl.js")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(categ: lila.forum.Categ, form: Form[lila.forum.DataForm.TopicData], captcha: lila.site.Captcha.Challenge)(implicit ctx: Context)
|
||||
@(categ: lila.forum.Categ, form: Form[_], captcha: lila.site.Captcha.Challenge)(implicit ctx: Context)
|
||||
|
||||
@forum.layout(
|
||||
title = "New forum topic") {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(categ: lila.forum.Categ, topic: lila.forum.Topic, posts: Paginator[lila.forum.Post], formWithCaptcha: Option[(Form[lila.forum.DataForm.PostData], lila.site.Captcha.Challenge)])(implicit ctx: Context)
|
||||
@(categ: lila.forum.Categ, topic: lila.forum.Topic, posts: Paginator[lila.forum.Post], formWithCaptcha: Option[(Form[_], lila.site.Captcha.Challenge)])(implicit ctx: Context)
|
||||
|
||||
@forum.layout(
|
||||
title = topic.name) {
|
||||
|
|
47
app/views/message/form.scala.html
Normal file
47
app/views/message/form.scala.html
Normal file
|
@ -0,0 +1,47 @@
|
|||
@(form: Form[_])(implicit ctx: Context)
|
||||
|
||||
@message.layout(
|
||||
title = trans.composeMessage.str()) {
|
||||
<div class="message_new">
|
||||
<h1>@trans.composeMessage()</h1>
|
||||
<div class="center form">
|
||||
<form action="@routes.Message.create()" method="post">
|
||||
<div class="field_to">
|
||||
<label for="@form("username").id">To:</label>
|
||||
<input
|
||||
type="text"
|
||||
data-provider="@routes.User.autocomplete"
|
||||
class="autocomplete"
|
||||
required="required"
|
||||
name="@form("username").name"
|
||||
id="@form("username").id"
|
||||
value="@form("username").value">
|
||||
@errMsg(form("username"))
|
||||
</div>
|
||||
<div class="field_subject">
|
||||
<label for="@form("subject").id">Subject:</label>
|
||||
<input
|
||||
type="text"
|
||||
required="required"
|
||||
name="@form("subject").name"
|
||||
id="@form("subject").id"
|
||||
value="@form("subject").value">
|
||||
@errMsg(form("username"))
|
||||
</div>
|
||||
<div class="field_body">
|
||||
<textarea
|
||||
name="@form("text").name"
|
||||
required="required"
|
||||
id="@form("text").id">
|
||||
@form("text").value
|
||||
</textarea>
|
||||
@errMsg(form("text"))
|
||||
</div>
|
||||
<div class="actions">
|
||||
<input class="send button" type="submit" value="Send" />
|
||||
<a class="cancel" href="@routes.Message.inbox()">@trans.cancel()</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
|
@ -6,12 +6,21 @@
|
|||
|
||||
@moreJs = {
|
||||
@jsTag("vendor/jquery.infinitescroll.min.js")
|
||||
@jsTag("vendor/jquery.ui.autocomplete.min.js")
|
||||
}
|
||||
|
||||
@goodies = {
|
||||
<div class="sidebar">
|
||||
<a href="@routes.Message.inbox(1)" class="goto_inbox">@trans.inbox()</a>
|
||||
<a href="@routes.Message.form" class="goto_compose">@trans.composeMessage()</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
@base.layout(
|
||||
title = title,
|
||||
moreCss = moreCss,
|
||||
moreJs = moreJs,
|
||||
goodies = goodies.some,
|
||||
active = siteMenu.message.some) {
|
||||
<div id="lichess_message" class="content_box">
|
||||
@body
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(form: Form[lila.setup.AiConfig])(implicit ctx: Context)
|
||||
@(form: Form[_])(implicit ctx: Context)
|
||||
|
||||
@fields = {
|
||||
<div class="variants buttons">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(form: Form[lila.setup.FriendConfig])(implicit ctx: Context)
|
||||
@(form: Form[_])(implicit ctx: Context)
|
||||
|
||||
@import lila.setup.FriendConfig
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@(form: Form[lila.setup.HookConfig])(implicit ctx: Context)
|
||||
@(form: Form[_])(implicit ctx: Context)
|
||||
|
||||
@import lila.setup.HookConfig
|
||||
@import lila.elo.EloRange
|
||||
|
|
|
@ -233,7 +233,7 @@ a.goto_nav:hover, #top a.toggle:hover, a#sound_state:hover {
|
|||
margin-top: 4px;
|
||||
line-height: 16px;
|
||||
}
|
||||
a.goto_message span.new_messages {
|
||||
a.goto_nav span.new_messages {
|
||||
padding: 1px 4px;
|
||||
background: #bbb;
|
||||
color: white;
|
||||
|
@ -243,7 +243,7 @@ a.goto_message span.new_messages {
|
|||
-webkit-border-radius:4px 4px 4px 4px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
a.goto_message span.new_messages.unread {
|
||||
a.goto_nav span.new_messages.unread {
|
||||
font-size: 1em;
|
||||
}
|
||||
a.goto_logout span.s16 {
|
||||
|
|
|
@ -98,7 +98,7 @@ div.sidebar a {
|
|||
padding: 10px;
|
||||
border: 1px solid #D4D4D4;
|
||||
}
|
||||
#lichess_message ul {
|
||||
#lichess_message .error {
|
||||
color: red;
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue