appeal nav tree WIP - for lichess-org/tavern#37
parent
d2002257d2
commit
9207c35137
|
@ -6,26 +6,32 @@ import views._
|
||||||
import lila.api.Context
|
import lila.api.Context
|
||||||
import lila.app._
|
import lila.app._
|
||||||
import lila.report.Suspect
|
import lila.report.Suspect
|
||||||
|
import play.api.data.Form
|
||||||
|
|
||||||
final class Appeal(env: Env, reportC: => Report) extends LilaController(env) {
|
final class Appeal(env: Env, reportC: => Report) extends LilaController(env) {
|
||||||
|
|
||||||
|
import lila.appeal.Appeal.form
|
||||||
|
|
||||||
def home =
|
def home =
|
||||||
Auth { implicit ctx => me =>
|
Auth { implicit ctx => me =>
|
||||||
env.appeal.api.mine(me) map { appeal =>
|
renderAppealOrTree(me) map { Ok(_) }
|
||||||
Ok(html.appeal.discussion(appeal, env.appeal.forms.text))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def renderAppealOrTree(
|
||||||
|
me: lila.user.User,
|
||||||
|
err: Option[Form[String]] = None
|
||||||
|
)(implicit ctx: Context) = env.appeal.api.mine(me) map {
|
||||||
|
case None => html.appeal.tree(me)
|
||||||
|
case Some(a) => html.appeal.discussion(a, err | form)
|
||||||
|
}
|
||||||
|
|
||||||
def post =
|
def post =
|
||||||
AuthBody { implicit ctx => me =>
|
AuthBody { implicit ctx => me =>
|
||||||
implicit val req = ctx.body
|
implicit val req = ctx.body
|
||||||
env.appeal.forms.text
|
form
|
||||||
.bindFromRequest()
|
.bindFromRequest()
|
||||||
.fold(
|
.fold(
|
||||||
err =>
|
err => renderAppealOrTree(me, err.some) map { BadRequest(_) },
|
||||||
env.appeal.api.mine(me) map { appeal =>
|
|
||||||
BadRequest(html.appeal.discussion(appeal, err))
|
|
||||||
},
|
|
||||||
text => env.appeal.api.post(text, me) inject Redirect(routes.Appeal.home).flashSuccess
|
text => env.appeal.api.post(text, me) inject Redirect(routes.Appeal.home).flashSuccess
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +49,7 @@ final class Appeal(env: Env, reportC: => Report) extends LilaController(env) {
|
||||||
Secure(_.Appeals) { implicit ctx => me =>
|
Secure(_.Appeals) { implicit ctx => me =>
|
||||||
asMod(username) { (appeal, suspect) =>
|
asMod(username) { (appeal, suspect) =>
|
||||||
env.report.api.inquiries.ofSuspectId(suspect.user.id) map { inquiry =>
|
env.report.api.inquiries.ofSuspectId(suspect.user.id) map { inquiry =>
|
||||||
Ok(html.appeal.discussion.show(appeal, suspect, inquiry, env.appeal.forms.text, getPresets))
|
Ok(html.appeal.discussion.show(appeal, suspect, inquiry, form, getPresets))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +58,7 @@ final class Appeal(env: Env, reportC: => Report) extends LilaController(env) {
|
||||||
SecureBody(_.Appeals) { implicit ctx => me =>
|
SecureBody(_.Appeals) { implicit ctx => me =>
|
||||||
asMod(username) { (appeal, suspect) =>
|
asMod(username) { (appeal, suspect) =>
|
||||||
implicit val req = ctx.body
|
implicit val req = ctx.body
|
||||||
env.appeal.forms.text
|
form
|
||||||
.bindFromRequest()
|
.bindFromRequest()
|
||||||
.fold(
|
.fold(
|
||||||
err =>
|
err =>
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package views.html
|
||||||
|
package appeal
|
||||||
|
|
||||||
|
import lila.api.Context
|
||||||
|
import lila.app.templating.Environment._
|
||||||
|
import lila.app.ui.ScalatagsTemplate._
|
||||||
|
|
||||||
|
object bits {
|
||||||
|
|
||||||
|
def layout(title: String)(body: Frag)(implicit ctx: Context) =
|
||||||
|
views.html.base.layout(
|
||||||
|
title = title,
|
||||||
|
moreCss = frag(
|
||||||
|
cssTag("form3"),
|
||||||
|
cssTag("appeal")
|
||||||
|
),
|
||||||
|
moreJs = jsModule("appeal")
|
||||||
|
)(body)
|
||||||
|
}
|
|
@ -16,35 +16,21 @@ import lila.user.User
|
||||||
|
|
||||||
object discussion {
|
object discussion {
|
||||||
|
|
||||||
def apply(appeal: Option[Appeal], textForm: Form[_])(implicit ctx: Context) =
|
def apply(appeal: Appeal, textForm: Form[String])(implicit ctx: Context) =
|
||||||
layout("Appeal") {
|
bits.layout("Appeal") {
|
||||||
main(cls := "page-small box box-pad page appeal")(
|
main(cls := "page-small box box-pad page appeal")(
|
||||||
appeal match {
|
renderAppeal(appeal, textForm, asMod = false, presets = none)
|
||||||
case Some(a) => renderAppeal(a, textForm, asMod = false, presets = none)
|
|
||||||
case None => newAppeal(textForm)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def newAppeal(textForm: Form[_])(implicit ctx: Context) =
|
|
||||||
frag(
|
|
||||||
h1("Appeal a moderation decision"),
|
|
||||||
renderHelp,
|
|
||||||
div(cls := "body")(
|
|
||||||
renderForm(textForm, action = routes.Appeal.post.url, isNew = true, presets = none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def show(
|
def show(
|
||||||
appeal: Appeal,
|
appeal: Appeal,
|
||||||
suspect: Suspect,
|
suspect: Suspect,
|
||||||
inquiry: Option[Inquiry],
|
inquiry: Option[Inquiry],
|
||||||
textForm: Form[_],
|
textForm: Form[String],
|
||||||
presets: ModPresets
|
presets: ModPresets
|
||||||
)(implicit
|
)(implicit ctx: Context) =
|
||||||
ctx: Context
|
bits.layout(s"Appeal by ${suspect.user.username}") {
|
||||||
) =
|
|
||||||
layout(s"Appeal by ${suspect.user.username}") {
|
|
||||||
main(cls := "page-small box box-pad page appeal")(
|
main(cls := "page-small box box-pad page appeal")(
|
||||||
renderAppeal(
|
renderAppeal(
|
||||||
appeal,
|
appeal,
|
||||||
|
@ -81,19 +67,9 @@ object discussion {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def layout(title: String)(body: Frag)(implicit ctx: Context) =
|
|
||||||
views.html.base.layout(
|
|
||||||
title = title,
|
|
||||||
moreCss = frag(
|
|
||||||
cssTag("form3"),
|
|
||||||
cssTag("appeal")
|
|
||||||
),
|
|
||||||
moreJs = jsModule("appeal")
|
|
||||||
)(body)
|
|
||||||
|
|
||||||
private def renderAppeal(
|
private def renderAppeal(
|
||||||
appeal: Appeal,
|
appeal: Appeal,
|
||||||
textForm: Form[_],
|
textForm: Form[String],
|
||||||
asMod: Boolean,
|
asMod: Boolean,
|
||||||
inquiry: Boolean = false,
|
inquiry: Boolean = false,
|
||||||
presets: Option[ModPresets]
|
presets: Option[ModPresets]
|
||||||
|
@ -125,19 +101,6 @@ object discussion {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
private def renderHelp =
|
|
||||||
div(cls := "appeal__help")(
|
|
||||||
p(
|
|
||||||
"If your account has been restricted for violation of ",
|
|
||||||
a(href := routes.Page.tos)("the Lichess rules"),
|
|
||||||
" you may file an appeal here."
|
|
||||||
),
|
|
||||||
p(
|
|
||||||
"You can read more about the appeal process ",
|
|
||||||
a(href := routes.Page.loneBookmark("appeal"))("here.")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private def renderUser(appeal: Appeal, userId: User.ID, asMod: Boolean)(implicit ctx: Context) =
|
private def renderUser(appeal: Appeal, userId: User.ID, asMod: Boolean)(implicit ctx: Context) =
|
||||||
if (appeal isAbout userId) userIdLink(userId.some)
|
if (appeal isAbout userId) userIdLink(userId.some)
|
||||||
else
|
else
|
||||||
|
@ -150,7 +113,7 @@ object discussion {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
private def renderForm(form: Form[_], action: String, isNew: Boolean, presets: Option[ModPresets])(implicit
|
def renderForm(form: Form[String], action: String, isNew: Boolean, presets: Option[ModPresets])(implicit
|
||||||
ctx: Context
|
ctx: Context
|
||||||
) =
|
) =
|
||||||
postForm(st.action := action)(
|
postForm(st.action := action)(
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
package views.html
|
||||||
|
package appeal
|
||||||
|
|
||||||
|
import controllers.routes
|
||||||
|
import play.api.data.Form
|
||||||
|
|
||||||
|
import lila.api.Context
|
||||||
|
import lila.app.templating.Environment._
|
||||||
|
import lila.app.ui.ScalatagsTemplate._
|
||||||
|
import lila.report.Report.Inquiry
|
||||||
|
import lila.user.User
|
||||||
|
|
||||||
|
object tree {
|
||||||
|
|
||||||
|
import trans.contact.doNotMessageModerators
|
||||||
|
import views.html.base.navTree._
|
||||||
|
|
||||||
|
private def cleanMenu(implicit ctx: Context): Branch =
|
||||||
|
Branch(
|
||||||
|
"root",
|
||||||
|
"Your account is not marked or restricted. You're all good!",
|
||||||
|
List(
|
||||||
|
Leaf(
|
||||||
|
"clean-other-account",
|
||||||
|
"I want to appeal for another account",
|
||||||
|
frag(
|
||||||
|
p(
|
||||||
|
"Sorry we don't take appeals from other accounts. The appeal should come from nowhere else, but the concerned account."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Leaf(
|
||||||
|
"clean-warning",
|
||||||
|
"I want to discuss a warning I received",
|
||||||
|
frag(
|
||||||
|
p(
|
||||||
|
"Please note that warnings are only warnings, and that your account has not been restricted currently.",
|
||||||
|
br,
|
||||||
|
"If you still want to file an appeal, use the following form:"
|
||||||
|
),
|
||||||
|
newAppeal
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Leaf(
|
||||||
|
"clean-other-issue",
|
||||||
|
"I have another issue to discuss",
|
||||||
|
p(
|
||||||
|
"This channel of communication is for appealing moderation related issues.",
|
||||||
|
br,
|
||||||
|
"Please use ",
|
||||||
|
a(href := routes.Main.contact)("the contact page"),
|
||||||
|
" or ",
|
||||||
|
a(href := "https://discordapp.com/invite/pvHanhg")("our discord server"),
|
||||||
|
" to contact us about other issues.",
|
||||||
|
br,
|
||||||
|
"You can also ",
|
||||||
|
a(href := routes.Page.loneBookmark("appeal"))("find here more information about appeals.")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def apply(me: User)(implicit ctx: Context) =
|
||||||
|
bits.layout("Appeal a moderation decision") {
|
||||||
|
main(cls := "page page-small box box-pad appeal")(
|
||||||
|
h1("Appeal"),
|
||||||
|
div(cls := "nav-tree")(
|
||||||
|
renderNode(
|
||||||
|
{
|
||||||
|
if (me.marks.clean) cleanMenu
|
||||||
|
else ???
|
||||||
|
},
|
||||||
|
none
|
||||||
|
)
|
||||||
|
),
|
||||||
|
p(cls := "appeal__moderators text", dataIcon := "")(doNotMessageModerators())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def newAppeal(implicit ctx: Context) =
|
||||||
|
discussion.renderForm(
|
||||||
|
lila.appeal.Appeal.form,
|
||||||
|
action = routes.Appeal.post.url,
|
||||||
|
isNew = true,
|
||||||
|
presets = none
|
||||||
|
)
|
||||||
|
|
||||||
|
private def renderHelp =
|
||||||
|
div(cls := "appeal__help")(
|
||||||
|
p(
|
||||||
|
"If your account has been restricted for violation of ",
|
||||||
|
a(href := routes.Page.tos)("the Lichess rules"),
|
||||||
|
" you may file an appeal here."
|
||||||
|
),
|
||||||
|
p(
|
||||||
|
"You can read more about the appeal process ",
|
||||||
|
a(href := routes.Page.loneBookmark("appeal"))("here.")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package views.html
|
||||||
|
package base
|
||||||
|
|
||||||
|
import lila.api.Context
|
||||||
|
import lila.app.templating.Environment._
|
||||||
|
import lila.app.ui.ScalatagsTemplate._
|
||||||
|
|
||||||
|
object navTree {
|
||||||
|
|
||||||
|
sealed trait Node {
|
||||||
|
val id: String
|
||||||
|
val name: Frag
|
||||||
|
}
|
||||||
|
case class Branch(id: String, name: Frag, children: List[Node]) extends Node
|
||||||
|
case class Leaf(id: String, name: Frag, content: Frag) extends Node
|
||||||
|
|
||||||
|
def renderNode(node: Node, parent: Option[Node])(implicit ctx: Context): Frag =
|
||||||
|
node match {
|
||||||
|
case Leaf(_, _, content) =>
|
||||||
|
List(
|
||||||
|
div(makeId(node.id), cls := "node leaf")(
|
||||||
|
h2(parent map goBack, node.name),
|
||||||
|
div(cls := "content")(content)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case b @ Branch(id, _, children) =>
|
||||||
|
frag(
|
||||||
|
div(makeId(node.id), cls := s"node branch $id")(
|
||||||
|
h2(parent map goBack, node.name),
|
||||||
|
div(cls := "links")(
|
||||||
|
children map { child =>
|
||||||
|
a(makeLink(child.id))(child.name)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
children map { renderNode(_, b.some) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def makeId(id: String) = st.id := s"help-$id"
|
||||||
|
|
||||||
|
private def makeLink(id: String) = href := s"#help-$id"
|
||||||
|
|
||||||
|
private def goBack(parent: Node): Frag =
|
||||||
|
a(makeLink(parent.id), cls := "back", dataIcon := "I", title := "Go back")
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
package views
|
package views.html
|
||||||
package html.site
|
package site
|
||||||
|
|
||||||
import controllers.routes
|
import controllers.routes
|
||||||
import scala.util.chaining._
|
import scala.util.chaining._
|
||||||
|
@ -11,13 +11,7 @@ import lila.app.ui.ScalatagsTemplate._
|
||||||
object contact {
|
object contact {
|
||||||
|
|
||||||
import trans.contact._
|
import trans.contact._
|
||||||
|
import views.html.base.navTree._
|
||||||
sealed private trait Node {
|
|
||||||
val id: String
|
|
||||||
val name: Frag
|
|
||||||
}
|
|
||||||
private case class Branch(id: String, name: Frag, children: List[Node]) extends Node
|
|
||||||
private case class Leaf(id: String, name: Frag, content: Frag) extends Node
|
|
||||||
|
|
||||||
private def reopenLeaf(prefix: String)(implicit ctx: Context) =
|
private def reopenLeaf(prefix: String)(implicit ctx: Context) =
|
||||||
Leaf(
|
Leaf(
|
||||||
|
@ -347,35 +341,6 @@ object contact {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
private def renderNode(node: Node, parent: Option[Node])(implicit ctx: Context): Frag =
|
|
||||||
node match {
|
|
||||||
case Leaf(_, _, content) =>
|
|
||||||
List(
|
|
||||||
div(makeId(node.id), cls := "node leaf")(
|
|
||||||
h2(parent map goBack, node.name),
|
|
||||||
div(cls := "content")(content)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
case b @ Branch(id, _, children) =>
|
|
||||||
frag(
|
|
||||||
div(makeId(node.id), cls := s"node branch $id")(
|
|
||||||
h2(parent map goBack, node.name),
|
|
||||||
div(cls := "links")(
|
|
||||||
children map { child =>
|
|
||||||
a(makeLink(child.id))(child.name)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
),
|
|
||||||
children map { renderNode(_, b.some) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def makeId(id: String) = st.id := s"help-$id"
|
|
||||||
private def makeLink(id: String) = href := s"#help-$id"
|
|
||||||
|
|
||||||
private def goBack(parent: Node): Frag =
|
|
||||||
a(makeLink(parent.id), cls := "back", dataIcon := "I", title := "Go back")
|
|
||||||
|
|
||||||
def apply()(implicit ctx: Context) =
|
def apply()(implicit ctx: Context) =
|
||||||
page.layout(
|
page.layout(
|
||||||
title = trans.contact.contact.txt(),
|
title = trans.contact.contact.txt(),
|
||||||
|
|
|
@ -59,6 +59,13 @@ object Appeal {
|
||||||
def apply(key: String) = all.find(_.key == key)
|
def apply(key: String) = all.find(_.key == key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val form = {
|
||||||
|
import play.api.data._
|
||||||
|
import play.api.data.Forms._
|
||||||
|
Form[String](
|
||||||
|
single("text" -> nonEmptyText)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class AppealMsg(
|
case class AppealMsg(
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
package lila.appeal
|
|
||||||
|
|
||||||
import play.api.data._
|
|
||||||
import play.api.data.Forms._
|
|
||||||
|
|
||||||
final class AppealForm {
|
|
||||||
|
|
||||||
val text = Form(
|
|
||||||
single("text" -> nonEmptyText)
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -12,7 +12,5 @@ final class Env(
|
||||||
|
|
||||||
private val coll = db(CollName("appeal"))
|
private val coll = db(CollName("appeal"))
|
||||||
|
|
||||||
lazy val forms = wire[AppealForm]
|
|
||||||
|
|
||||||
lazy val api: AppealApi = wire[AppealApi]
|
lazy val api: AppealApi = wire[AppealApi]
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
<string name="youCanAlsoReachReportPage">You can also reach that page by clicking the %s report button on a profile page.</string>
|
<string name="youCanAlsoReachReportPage">You can also reach that page by clicking the %s report button on a profile page.</string>
|
||||||
<string name="doNotReportInForum">Do not report players in the forum.</string>
|
<string name="doNotReportInForum">Do not report players in the forum.</string>
|
||||||
<string name="doNotSendReportEmails">Do not send us report emails.</string>
|
<string name="doNotSendReportEmails">Do not send us report emails.</string>
|
||||||
<string name="doNotMessageModerators">Do not send direct messages to moderators.</string>
|
<string name="doNotMessageModerators">Please do not send direct messages to moderators.</string>
|
||||||
<string name="onlyReports">Only reporting players through the report form is effective.</string>
|
<string name="onlyReports">Only reporting players through the report form is effective.</string>
|
||||||
<string name="wantReportBug">I want to report a bug</string>
|
<string name="wantReportBug">I want to report a bug</string>
|
||||||
<string name="reportBugInForum">In the Lichess Feedback section of the forum</string>
|
<string name="reportBugInForum">In the Lichess Feedback section of the forum</string>
|
||||||
|
|
|
@ -52,4 +52,11 @@
|
||||||
.appeal-presets {
|
.appeal-presets {
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__moderators {
|
||||||
|
@extend %box-radius;
|
||||||
|
margin-top: 3em;
|
||||||
|
padding: 2em 3em;
|
||||||
|
background: $c-bg-zebra;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { formToXhr } from 'common/xhr';
|
import { formToXhr } from 'common/xhr';
|
||||||
|
|
||||||
lichess.load.then(() => {
|
lichess.load.then(() => {
|
||||||
|
if ($('.nav-tree').length) location.hash = location.hash || '#help-root';
|
||||||
|
|
||||||
$('select.appeal-presets').on('change', (e: Event) => $('#form3-text').val((e.target as HTMLTextAreaElement).value));
|
$('select.appeal-presets').on('change', (e: Event) => $('#form3-text').val((e.target as HTMLTextAreaElement).value));
|
||||||
|
|
||||||
$('form.appeal__actions__slack').on('submit', (e: Event) => {
|
$('form.appeal__actions__slack').on('submit', (e: Event) => {
|
||||||
|
|
Loading…
Reference in New Issue