appeal nav tree WIP - for lichess-org/tavern#37

appeal2
Thibault Duplessis 2021-02-26 12:20:21 +01:00
parent d2002257d2
commit 9207c35137
12 changed files with 209 additions and 107 deletions

View File

@ -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 =>

View File

@ -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)
}

View File

@ -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)(

View File

@ -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.")
)
)
}

View File

@ -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")
}

View File

@ -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(),

View File

@ -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(

View File

@ -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)
)
}

View File

@ -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]
} }

View File

@ -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>

View File

@ -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;
}
} }

View File

@ -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) => {