Merge branch 'master' of github.com:ornicar/lila
* 'master' of github.com:ornicar/lila: tweak relation caches show full list of friends remove timeline entries about people following me Revert "no longer send follow to friends timelines" no longer send follow to friends timelines ublog post image alt & credit analyse/embed/css: button cursor: pointer analyse/embed/css: Fix zh layout on mobile Prettier broadcast: Fix error URL ublog: Translate follow button properly css: Reduce page h2 line-height css: Make .button display: inline-block ublog/css: Prevent linebreak inside view count Fix zulip chat panic link Make shepherd close button visible Fix shepherd themes being swappedpull/9850/head
commit
2fe175294e
|
@ -117,8 +117,9 @@ final class Relation(
|
|||
OptionFuResult(env.user.repo named username) { user =>
|
||||
RelatedPager(api.followingPaginatorAdapter(user.id), page) flatMap { pag =>
|
||||
negotiate(
|
||||
html = api countFollowers user.id map { nbFollowers =>
|
||||
Ok(html.relation.bits.following(user, pag, nbFollowers))
|
||||
html = {
|
||||
if (ctx is user) Ok(html.relation.bits.friends(user, pag)).fuccess
|
||||
else ctx.me.fold(notFound)(me => Redirect(routes.Relation.following(me.username)).fuccess)
|
||||
},
|
||||
api = _ => Ok(jsonRelatedPaginator(pag)).fuccess
|
||||
)
|
||||
|
@ -129,18 +130,15 @@ final class Relation(
|
|||
|
||||
def followers(username: String, page: Int) =
|
||||
Open { implicit ctx =>
|
||||
Reasonable(page, 20) {
|
||||
OptionFuResult(env.user.repo named username) { user =>
|
||||
RelatedPager(api.followersPaginatorAdapter(user.id), page) flatMap { pag =>
|
||||
negotiate(
|
||||
html = api countFollowing user.id map { nbFollowing =>
|
||||
Ok(html.relation.bits.followers(user, pag, nbFollowing))
|
||||
},
|
||||
api = _ => Ok(jsonRelatedPaginator(pag)).fuccess
|
||||
)
|
||||
negotiate(
|
||||
html = notFound,
|
||||
api = _ =>
|
||||
Reasonable(page, 20) {
|
||||
RelatedPager(api.followersPaginatorAdapter(UserModel normalize username), page) flatMap { pag =>
|
||||
Ok(jsonRelatedPaginator(pag)).fuccess
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def apiFollowing(name: String) = apiRelation(name, Direction.Following)
|
||||
|
|
|
@ -512,7 +512,7 @@ final class User(
|
|||
}
|
||||
}
|
||||
.sequenceFu
|
||||
} yield html.user.opponents(me, relateds)
|
||||
} yield html.relation.bits.opponents(me, relateds)
|
||||
}
|
||||
|
||||
def perfStat(username: String, perfKey: String) =
|
||||
|
|
|
@ -1,44 +1,23 @@
|
|||
package views.html.relation
|
||||
|
||||
import controllers.routes
|
||||
import play.api.mvc.Call
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.paginator.Paginator
|
||||
import lila.game.FavoriteOpponents
|
||||
import lila.relation.Related
|
||||
import lila.user.User
|
||||
|
||||
import controllers.routes
|
||||
|
||||
object bits {
|
||||
|
||||
def followers(u: User, pag: Paginator[Related], nbFollowing: Int)(implicit ctx: Context) =
|
||||
layout(s"${u.username} • ${trans.nbFollowers.pluralSameTxt(pag.nbResults)}")(
|
||||
div(cls := "box__top")(
|
||||
h1(userLink(u, withOnline = false)),
|
||||
div(cls := "actions")(
|
||||
trans.nbFollowers.pluralSame(pag.nbResults),
|
||||
" ",
|
||||
amp,
|
||||
" ",
|
||||
a(href := routes.Relation.following(u.username))(trans.nbFollowing.pluralSame(nbFollowing))
|
||||
)
|
||||
),
|
||||
pagTable(pag, routes.Relation.followers(u.username))
|
||||
)
|
||||
|
||||
def following(u: User, pag: Paginator[Related], nbFollowers: Int)(implicit ctx: Context) =
|
||||
layout(s"${u.username} • ${trans.nbFollowing.pluralSameTxt(pag.nbResults)}")(
|
||||
div(cls := "box__top")(
|
||||
h1(userLink(u, withOnline = false)),
|
||||
div(cls := "actions")(
|
||||
trans.nbFollowing.pluralSame(pag.nbResults),
|
||||
" ",
|
||||
amp,
|
||||
" ",
|
||||
a(href := routes.Relation.followers(u.username))(trans.nbFollowers.pluralSame(nbFollowers))
|
||||
)
|
||||
def friends(u: User, pag: Paginator[Related])(implicit ctx: Context) =
|
||||
layout(s"${u.username} • ${trans.friends.txt()}")(
|
||||
h1(
|
||||
a(href := routes.User.show(u.username), dataIcon := "", cls := "text"),
|
||||
trans.friends()
|
||||
),
|
||||
pagTable(pag, routes.Relation.following(u.username))
|
||||
)
|
||||
|
@ -54,6 +33,38 @@ object bits {
|
|||
pagTable(pag, routes.Relation.blocks())
|
||||
)
|
||||
|
||||
def opponents(u: User, sugs: List[lila.relation.Related])(implicit ctx: Context) =
|
||||
layout(s"${u.username} • ${trans.favoriteOpponents.txt()}")(
|
||||
h1(
|
||||
a(href := routes.User.show(u.username), dataIcon := "", cls := "text"),
|
||||
trans.favoriteOpponents(),
|
||||
" (",
|
||||
trans.nbGames.pluralSame(FavoriteOpponents.gameLimit),
|
||||
")"
|
||||
),
|
||||
table(cls := "slist")(
|
||||
tbody(
|
||||
if (sugs.nonEmpty) sugs.map { r =>
|
||||
tr(
|
||||
td(userLink(r.user)),
|
||||
td(showBestPerf(r.user)),
|
||||
td(
|
||||
r.nbGames.filter(_ > 0).map { nbGames =>
|
||||
a(href := s"${routes.User.games(u.username, "search")}?players.b=${r.user.username}")(
|
||||
trans.nbGames.plural(nbGames, nbGames.localize)
|
||||
)
|
||||
}
|
||||
),
|
||||
td(
|
||||
views.html.relation.actions(r.user.id, r.relation, followable = r.followable, blocked = false)
|
||||
)
|
||||
)
|
||||
}
|
||||
else tr(td(trans.none()))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def layout(title: String)(content: Modifier*)(implicit ctx: Context) =
|
||||
views.html.base.layout(
|
||||
title = title,
|
||||
|
@ -71,7 +82,7 @@ object bits {
|
|||
tr(cls := "paginated")(
|
||||
td(userLink(r.user)),
|
||||
td(showBestPerf(r.user)),
|
||||
td(trans.nbGames.pluralSame(r.user.count.game)),
|
||||
td(trans.nbGames.plural(r.user.count.game, r.user.count.game.localize)),
|
||||
td(actions(r.user.id, relation = r.relation, followable = r.followable, blocked = false))
|
||||
)
|
||||
},
|
||||
|
|
|
@ -26,7 +26,7 @@ object form {
|
|||
) {
|
||||
main(cls := "page-menu page-small")(
|
||||
views.html.blog.bits.menu(none, "mine".some),
|
||||
div(cls := "page-menu__content box box-pad ublog-post-form")(
|
||||
div(cls := "page-menu__content box ublog-post-form")(
|
||||
standardFlash(),
|
||||
h1(trans.ublog.newPost()),
|
||||
etiquette,
|
||||
|
@ -43,7 +43,7 @@ object form {
|
|||
) {
|
||||
main(cls := "page-menu page-small")(
|
||||
views.html.blog.bits.menu(none, "mine".some),
|
||||
div(cls := "page-menu__content box box-pad ublog-post-form")(
|
||||
div(cls := "page-menu__content box ublog-post-form")(
|
||||
standardFlash(),
|
||||
div(cls := "box__top")(
|
||||
h1(
|
||||
|
@ -108,32 +108,41 @@ object form {
|
|||
)
|
||||
)
|
||||
|
||||
def formImage(post: UblogPost) = postView.thumbnail(post, _.Small)
|
||||
def formImage(post: UblogPost) =
|
||||
postView.thumbnail(post, _.Small)(cls := post.image.isDefined.option("user-image"))
|
||||
|
||||
private def inner(form: Form[UblogPostData], post: Either[User, UblogPost], captcha: Option[Captcha])(
|
||||
implicit ctx: Context
|
||||
) =
|
||||
postForm(
|
||||
cls := "form3",
|
||||
cls := "form3 ublog-post-form__main",
|
||||
action := post.fold(_ => routes.Ublog.create, p => routes.Ublog.update(p.id.value))
|
||||
)(
|
||||
form3.globalError(form),
|
||||
post.isRight option form3.split(
|
||||
form3.checkbox(
|
||||
form("live"),
|
||||
trans.ublog.publishOnYourBlog(),
|
||||
help = trans.ublog.publishHelp().some,
|
||||
half = true
|
||||
),
|
||||
form3.group(form("language"), trans.language(), half = true) { field =>
|
||||
form3.select(
|
||||
field,
|
||||
LangList.popularNoRegion.map { l =>
|
||||
l.code -> l.toLocale.getDisplayLanguage
|
||||
post.toOption.map { p =>
|
||||
frag(
|
||||
form3.split(
|
||||
form3.group(form("imageAlt"), trans.ublog.imageAlt(), half = true)(form3.input(_)),
|
||||
form3.group(form("imageCredit"), trans.ublog.imageCredit(), half = true)(form3.input(_))
|
||||
)(cls := s"ublog-post-form__image-text ${p.image.isDefined ?? "visible"}"),
|
||||
form3.split(
|
||||
form3.checkbox(
|
||||
form("live"),
|
||||
trans.ublog.publishOnYourBlog(),
|
||||
help = trans.ublog.publishHelp().some,
|
||||
half = true
|
||||
),
|
||||
form3.group(form("language"), trans.language(), half = true) { field =>
|
||||
form3.select(
|
||||
field,
|
||||
LangList.popularNoRegion.map { l =>
|
||||
l.code -> l.toLocale.getDisplayLanguage
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
},
|
||||
form3.group(form("title"), trans.ublog.postTitle())(form3.input(_)(autofocus)),
|
||||
form3.group(form("intro"), trans.ublog.postIntro())(form3.input(_)(autofocus)),
|
||||
form3.group(
|
||||
|
|
|
@ -44,7 +44,12 @@ object post {
|
|||
main(cls := "page-menu page-small")(
|
||||
views.html.blog.bits.menu(none, (if (ctx is user) "mine" else "community").some),
|
||||
div(cls := "page-menu__content box box-pad ublog-post")(
|
||||
post.image.isDefined option thumbnail(post, _.Large)(cls := "ublog-post__image"),
|
||||
post.image.map { image =>
|
||||
frag(
|
||||
thumbnail(post, _.Large)(cls := "ublog-post__image"),
|
||||
image.credit.map { p(cls := "ublog-post__image-credit")(_) }
|
||||
)
|
||||
},
|
||||
ctx.is(user) || isGranted(_.ModerateBlog) option standardFlash(),
|
||||
h1(cls := "ublog-post__title")(post.title),
|
||||
div(cls := "ublog-post__meta")(
|
||||
|
@ -135,15 +140,15 @@ object post {
|
|||
)
|
||||
)(
|
||||
List(
|
||||
("yes", trans.following, routes.Relation.unfollow _, ""),
|
||||
("no", trans.follow, routes.Relation.follow _, "")
|
||||
("yes", trans.unfollowX, routes.Relation.unfollow _, ""),
|
||||
("no", trans.followX, routes.Relation.follow _, "")
|
||||
).map { case (role, text, route, icon) =>
|
||||
button(
|
||||
cls := s"ublog-post__follow__$role button button-big",
|
||||
dataIcon := icon,
|
||||
dataRel := route(user.id)
|
||||
)(
|
||||
span(cls := "button-label")(text.txt(), " ", user.titleUsername)
|
||||
span(cls := "button-label")(text(user.titleUsername))
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -191,12 +196,13 @@ object post {
|
|||
img(
|
||||
cls := "ublog-post-image",
|
||||
widthA := size(UblogPost.thumbnail).width,
|
||||
heightA := size(UblogPost.thumbnail).height
|
||||
heightA := size(UblogPost.thumbnail).height,
|
||||
alt := post.image.flatMap(_.alt)
|
||||
)(src := url(post, size))
|
||||
|
||||
def url(post: UblogPost.BasePost, size: UblogPost.thumbnail.SizeSelector) =
|
||||
post.image match {
|
||||
case Some(image) => UblogPost.thumbnail(picfitUrl, image, size)
|
||||
case Some(image) => UblogPost.thumbnail(picfitUrl, image.id, size)
|
||||
case _ => assetUrl("images/user-blog-default.png")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
package views.html
|
||||
package user
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.user.User
|
||||
import lila.game.FavoriteOpponents
|
||||
|
||||
import controllers.routes
|
||||
|
||||
object opponents {
|
||||
def apply(u: User, sugs: List[lila.relation.Related])(implicit ctx: Context) =
|
||||
relation.bits.layout(s"${u.username} • ${trans.favoriteOpponents.txt()}")(
|
||||
h1(
|
||||
a(href := routes.User.show(u.username), dataIcon := "", cls := "text"),
|
||||
trans.favoriteOpponents(),
|
||||
" (",
|
||||
trans.nbGames.pluralSame(FavoriteOpponents.gameLimit),
|
||||
")"
|
||||
),
|
||||
table(cls := "slist")(
|
||||
tbody(
|
||||
if (sugs.nonEmpty) sugs.map { r =>
|
||||
tr(
|
||||
td(userLink(r.user)),
|
||||
td(showBestPerf(r.user)),
|
||||
td(
|
||||
r.nbGames.filter(_ > 0).map { nbGames =>
|
||||
a(href := s"${routes.User.games(u.username, "search")}?players.b=${r.user.username}")(
|
||||
trans.nbGames.pluralSame(nbGames)
|
||||
)
|
||||
}
|
||||
),
|
||||
td(relation.actions(r.user.id, r.relation, followable = r.followable, blocked = false))
|
||||
)
|
||||
}
|
||||
else tr(td(trans.none()))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
|
@ -48,9 +48,6 @@ object header {
|
|||
),
|
||||
div(cls := "user-show__social")(
|
||||
div(cls := "number-menu")(
|
||||
a(cls := "nm-item", href := routes.Relation.followers(u.username))(
|
||||
splitNumber(trans.nbFollowers.pluralSame(info.nbFollowers))
|
||||
),
|
||||
u.noBot option a(
|
||||
href := routes.UserTournament.path(u.username, "recent"),
|
||||
cls := "nm-item tournament_stats",
|
||||
|
@ -232,6 +229,8 @@ object header {
|
|||
trans.profileCompletion(s"${profile.completionPercent}%")
|
||||
),
|
||||
br,
|
||||
a(href := routes.Relation.following(u.username))(trans.friends()),
|
||||
br,
|
||||
a(href := routes.User.opponents)(trans.favoriteOpponents())
|
||||
),
|
||||
u.playTime.map { playTime =>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
db.ublog_post
|
||||
.find({ image: { $exists: 1 } }, { image: 1 })
|
||||
.forEach(p => db.ublog_post.update({ _id: p._id }, { $set: { image: { id: p.image } } }));
|
|
@ -161,7 +161,7 @@ final class PersonalDataExport(
|
|||
"title" -> post.title,
|
||||
"intro" -> post.intro,
|
||||
"body" -> post.markdown,
|
||||
"image" -> post.image.??(lila.ublog.UblogPost.thumbnail(picfitUrl, _, _.Large)),
|
||||
"image" -> post.image.??(i => lila.ublog.UblogPost.thumbnail(picfitUrl, i.id, _.Large)),
|
||||
"topics" -> post.topics.map(_.value).mkString(", ")
|
||||
).map { case (k, v) =>
|
||||
s"$k: $v"
|
||||
|
|
|
@ -277,6 +277,8 @@ val `favoriteOpponents` = new I18nKey("favoriteOpponents")
|
|||
val `follow` = new I18nKey("follow")
|
||||
val `following` = new I18nKey("following")
|
||||
val `unfollow` = new I18nKey("unfollow")
|
||||
val `followX` = new I18nKey("followX")
|
||||
val `unfollowX` = new I18nKey("unfollowX")
|
||||
val `block` = new I18nKey("block")
|
||||
val `blocked` = new I18nKey("blocked")
|
||||
val `unblock` = new I18nKey("unblock")
|
||||
|
@ -2281,6 +2283,8 @@ val `noPostsInThisBlogYet` = new I18nKey("ublog:noPostsInThisBlogYet")
|
|||
val `noDrafts` = new I18nKey("ublog:noDrafts")
|
||||
val `latestBlogPosts` = new I18nKey("ublog:latestBlogPosts")
|
||||
val `uploadAnImageForYourPost` = new I18nKey("ublog:uploadAnImageForYourPost")
|
||||
val `imageAlt` = new I18nKey("ublog:imageAlt")
|
||||
val `imageCredit` = new I18nKey("ublog:imageCredit")
|
||||
val `publishedNbBlogPosts` = new I18nKey("ublog:publishedNbBlogPosts")
|
||||
val `nbViews` = new I18nKey("ublog:nbViews")
|
||||
val `viewAllNbPosts` = new I18nKey("ublog:viewAllNbPosts")
|
||||
|
|
|
@ -87,7 +87,7 @@ final class IrcApi(
|
|||
|
||||
def chatPanic(mod: Holder, v: Boolean): Funit =
|
||||
zulip(_.mod.log, "chat panic")(
|
||||
s":stop: ${markdown.modLink(mod.user)} ${if (v) "enabled" else "disabled"} ${markdown.lichessLink("mod/chat-panic", " Chat Panic")}"
|
||||
s":stop: ${markdown.modLink(mod.user)} ${if (v) "enabled" else "disabled"} ${markdown.lichessLink("/mod/chat-panic", " Chat Panic")}"
|
||||
)
|
||||
|
||||
def garbageCollector(msg: String): Funit =
|
||||
|
|
|
@ -80,9 +80,8 @@ final class RelationApi(
|
|||
def fetchAreFriends(u1: ID, u2: ID): Fu[Boolean] =
|
||||
fetchFollows(u1, u2) >>& fetchFollows(u2, u1)
|
||||
|
||||
private val countFollowingCache = cacheApi[ID, Int](8192, "relation.count.following") {
|
||||
_.expireAfterAccess(10 minutes)
|
||||
.maximumSize(32768)
|
||||
private val countFollowingCache = cacheApi[ID, Int](8_192, "relation.count.following") {
|
||||
_.maximumSize(8_192)
|
||||
.buildAsyncFuture { userId =>
|
||||
coll.countSel($doc("u1" -> userId, "r" -> Follow))
|
||||
}
|
||||
|
@ -92,9 +91,8 @@ final class RelationApi(
|
|||
|
||||
def reachedMaxFollowing(userId: ID): Fu[Boolean] = countFollowingCache get userId map (config.maxFollow <=)
|
||||
|
||||
private val countFollowersCache = cacheApi[ID, Int](131_072, "relation.count.followers") {
|
||||
_.expireAfterAccess(10 minutes)
|
||||
.maximumSize(131_072)
|
||||
private val countFollowersCache = cacheApi[ID, Int](32_768, "relation.count.followers") {
|
||||
_.maximumSize(32_768)
|
||||
.buildAsyncFuture { userId =>
|
||||
coll.secondaryPreferred.countSel($doc("u2" -> userId, "r" -> Follow))
|
||||
}
|
||||
|
@ -147,7 +145,7 @@ final class RelationApi(
|
|||
repo.follow(u1, u2) >> limitFollow(u1) >>- {
|
||||
countFollowersCache.update(u2, 1 +)
|
||||
countFollowingCache.update(u1, prev => (prev + 1) atMost config.maxFollow.value)
|
||||
timeline ! Propagate(FollowUser(u1, u2)).toFriendsOf(u1).toUsers(List(u2))
|
||||
timeline ! Propagate(FollowUser(u1, u2)).toFriendsOf(u1)
|
||||
Bus.publish(lila.hub.actorApi.relation.Follow(u1, u2), "relation")
|
||||
lila.mon.relation.follow.increment().unit
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ object JsonView {
|
|||
"log" -> s.log.events
|
||||
) ++
|
||||
s.upstream.?? {
|
||||
case RelayRound.Sync.UpstreamUrl(url) => Json.obj("url" -> url)
|
||||
case url: RelayRound.Sync.UpstreamUrl => Json.obj("url" -> url.withRound.url)
|
||||
case RelayRound.Sync.UpstreamIds(ids) => Json.obj("ids" -> ids)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,10 +110,10 @@ final class UblogApi(
|
|||
|
||||
def uploadImage(user: User, post: UblogPost, picture: PicfitApi.FilePart): Fu[UblogPost] =
|
||||
for {
|
||||
image <- picfitApi
|
||||
.uploadFile(imageRel(post), picture, userId = user.id)
|
||||
_ <- colls.post.update.one($id(post.id), $set("image" -> image.id))
|
||||
} yield post.copy(image = image.id.some)
|
||||
pic <- picfitApi.uploadFile(imageRel(post), picture, userId = user.id)
|
||||
image = post.image.fold(UblogImage(pic.id))(_.copy(id = pic.id))
|
||||
_ <- colls.post.updateField($id(post.id), "image", image)
|
||||
} yield post.copy(image = image.some)
|
||||
|
||||
def deleteImage(post: UblogPost): Fu[UblogPost] =
|
||||
picfitApi.deleteByRel(imageRel(post)) >>
|
||||
|
|
|
@ -25,6 +25,7 @@ private object UblogBsonHandlers {
|
|||
.afterRead(_.filter(t => UblogTopic.exists(t.value)))
|
||||
implicit val langBsonHandler = stringAnyValHandler[Lang](_.code, Lang.apply)
|
||||
implicit val recordedBSONHandler = Macros.handler[Recorded]
|
||||
implicit val imageBSONHandler = Macros.handler[UblogImage]
|
||||
implicit val likesBSONHandler = intAnyValHandler[Likes](_.value, Likes)
|
||||
implicit val viewsBSONHandler = intAnyValHandler[Views](_.value, Views)
|
||||
implicit val rankBSONHandler = dateIsoHandler[Rank](Iso[DateTime, Rank](Rank, _.value))
|
||||
|
|
|
@ -17,14 +17,16 @@ final class UblogForm(markup: UblogMarkup, val captcher: lila.hub.actors.Captche
|
|||
|
||||
private val base =
|
||||
mapping(
|
||||
"title" -> cleanNonEmptyText(minLength = 3, maxLength = 80),
|
||||
"intro" -> cleanNonEmptyText(minLength = 0, maxLength = 1_000),
|
||||
"markdown" -> cleanNonEmptyText(minLength = 0, maxLength = 100_000).verifying(markdownImage.constraint),
|
||||
"language" -> optional(stringIn(LangList.popularNoRegion.map(_.code).toSet)),
|
||||
"topics" -> optional(text),
|
||||
"live" -> boolean,
|
||||
"gameId" -> text,
|
||||
"move" -> text
|
||||
"title" -> cleanNonEmptyText(minLength = 3, maxLength = 80),
|
||||
"intro" -> cleanNonEmptyText(minLength = 0, maxLength = 1_000),
|
||||
"markdown" -> cleanNonEmptyText(minLength = 0, maxLength = 100_000).verifying(markdownImage.constraint),
|
||||
"imageAlt" -> optional(cleanNonEmptyText(minLength = 3, maxLength = 200)),
|
||||
"imageCredit" -> optional(cleanNonEmptyText(minLength = 3, maxLength = 200)),
|
||||
"language" -> optional(stringIn(LangList.popularNoRegion.map(_.code).toSet)),
|
||||
"topics" -> optional(text),
|
||||
"live" -> boolean,
|
||||
"gameId" -> text,
|
||||
"move" -> text
|
||||
)(UblogPostData.apply)(UblogPostData.unapply)
|
||||
|
||||
val create = Form(
|
||||
|
@ -37,6 +39,8 @@ final class UblogForm(markup: UblogMarkup, val captcher: lila.hub.actors.Captche
|
|||
title = post.title,
|
||||
intro = post.intro,
|
||||
markdown = post.markdown,
|
||||
imageAlt = post.image.flatMap(_.alt),
|
||||
imageCredit = post.image.flatMap(_.credit),
|
||||
language = post.language.code.some,
|
||||
topics = post.topics.map(_.value).mkString(", ").some,
|
||||
live = post.live,
|
||||
|
@ -52,6 +56,8 @@ object UblogForm {
|
|||
title: String,
|
||||
intro: String,
|
||||
markdown: String,
|
||||
imageAlt: Option[String],
|
||||
imageCredit: Option[String],
|
||||
language: Option[String],
|
||||
topics: Option[String],
|
||||
live: Boolean,
|
||||
|
@ -84,6 +90,9 @@ object UblogForm {
|
|||
title = title,
|
||||
intro = intro,
|
||||
markdown = markdown,
|
||||
image = prev.image.map { i =>
|
||||
i.copy(alt = imageAlt, credit = imageCredit)
|
||||
},
|
||||
language = LangList.removeRegion(realLanguage | prev.language),
|
||||
topics = topics ?? UblogTopic.fromStrList,
|
||||
live = live,
|
||||
|
|
|
@ -13,7 +13,7 @@ case class UblogPost(
|
|||
intro: String,
|
||||
markdown: String,
|
||||
language: Lang,
|
||||
image: Option[PicfitImage.Id],
|
||||
image: Option[UblogImage],
|
||||
topics: List[UblogTopic],
|
||||
live: Boolean,
|
||||
created: UblogPost.Recorded,
|
||||
|
@ -28,6 +28,8 @@ case class UblogPost(
|
|||
def indexable = live && topics.exists(t => UblogTopic.chessExists(t.value))
|
||||
}
|
||||
|
||||
case class UblogImage(id: PicfitImage.Id, alt: Option[String] = None, credit: Option[String] = None)
|
||||
|
||||
object UblogPost {
|
||||
|
||||
case class Id(value: String) extends AnyVal with StringValue
|
||||
|
@ -50,7 +52,7 @@ object UblogPost {
|
|||
val blog: UblogBlog.Id
|
||||
val title: String
|
||||
val intro: String
|
||||
val image: Option[PicfitImage.Id]
|
||||
val image: Option[UblogImage]
|
||||
val created: Recorded
|
||||
val lived: Option[Recorded]
|
||||
def id = _id
|
||||
|
@ -62,7 +64,7 @@ object UblogPost {
|
|||
blog: UblogBlog.Id,
|
||||
title: String,
|
||||
intro: String,
|
||||
image: Option[PicfitImage.Id],
|
||||
image: Option[UblogImage],
|
||||
created: Recorded,
|
||||
lived: Option[Recorded]
|
||||
) extends BasePost
|
||||
|
|
|
@ -2,7 +2,7 @@ lichess.ratingHistoryChart = function (data, { singlePerfName, perfIndex }) {
|
|||
var oneDay = 86400000;
|
||||
function smoothDates(data) {
|
||||
if (!data.length) return [];
|
||||
|
||||
|
||||
// If last rating wasn't today, add to the array
|
||||
var today = new Date().setUTCHours(0, 0, 0, 0);
|
||||
var lastRating = data[data.length - 1];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
function loadShepherd(f) {
|
||||
var theme = 'shepherd-theme-' + ($('body').hasClass('dark') ? 'default' : 'dark');
|
||||
lichess.loadCss('vendor/shepherd/dist/css/' + theme + '.css');
|
||||
var theme = 'shepherd-theme-' + ($('body').hasClass('dark') ? 'dark' : 'default');
|
||||
lichess.loadCss('vendor/' + theme + '.css');
|
||||
lichess.loadScript('vendor/shepherd/dist/js/tether.js', { noVersion: true }).then(function () {
|
||||
lichess.loadScript('vendor/shepherd/dist/js/shepherd.min.js', { noVersion: true }).then(function () {
|
||||
f(theme);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
function loadShepherd(f) {
|
||||
if (typeof Shepherd === 'undefined' || Shepherd.activeTour === null) {
|
||||
var theme = 'shepherd-theme-' + ($('body').hasClass('dark') ? 'default' : 'dark');
|
||||
lichess.loadCss('vendor/shepherd/dist/css/' + theme + '.css');
|
||||
var theme = 'shepherd-theme-' + ($('body').hasClass('dark') ? 'dark' : 'default');
|
||||
lichess.loadCss('vendor/' + theme + '.css');
|
||||
lichess.loadScript('vendor/shepherd/dist/js/tether.js', { noVersion: true }).then(function () {
|
||||
lichess.loadScript('vendor/shepherd/dist/js/shepherd.min.js', { noVersion: true }).then(function () {
|
||||
f(theme);
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/* https://raw.githubusercontent.com/shipshapecode/shepherd/e3ed0fcbf5c31137ece409d3ad6fea4e264ca1cc/dist/css/shepherd-theme-dark.css */
|
||||
.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before {
|
||||
box-sizing: border-box; }
|
||||
|
||||
.shepherd-element {
|
||||
position: absolute;
|
||||
display: none; }
|
||||
.shepherd-element.shepherd-open {
|
||||
display: block; }
|
||||
|
||||
.shepherd-element.shepherd-theme-dark {
|
||||
max-width: 100%;
|
||||
max-height: 100%; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content {
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
background: #232323;
|
||||
color: #eee;
|
||||
padding: 1em;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5em; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-width: 16px;
|
||||
border-style: solid;
|
||||
pointer-events: none; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-left-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-right-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
top: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
top: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #232323; }
|
||||
|
||||
.shepherd-element.shepherd-theme-dark {
|
||||
z-index: 9999;
|
||||
max-width: 24em;
|
||||
font-size: 1em; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before {
|
||||
border-bottom-color: #303030; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-has-title .shepherd-content header {
|
||||
background: #303030;
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-has-title .shepherd-content header a.shepherd-cancel-link {
|
||||
padding: 0;
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-has-cancel-link .shepherd-content header h3 {
|
||||
float: left; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content {
|
||||
box-shadow: 0 0 1em rgba(0, 0, 0, 0.2);
|
||||
padding: 0; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content * {
|
||||
font-size: inherit; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content header {
|
||||
*zoom: 1;
|
||||
border-radius: 5px 5px 0 0; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content header:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content header h3 {
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
font-weight: normal; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content header a.shepherd-cancel-link {
|
||||
float: right;
|
||||
text-decoration: none;
|
||||
font-size: 1.25em;
|
||||
line-height: .8em;
|
||||
font-weight: normal;
|
||||
color: white;
|
||||
opacity: 0.6;
|
||||
position: relative;
|
||||
top: .1em;
|
||||
padding: .8em;
|
||||
margin-bottom: -.8em; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content header a.shepherd-cancel-link:hover {
|
||||
opacity: 1; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content .shepherd-text {
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content .shepherd-text p {
|
||||
margin: 0 0 .5em 0;
|
||||
line-height: 1.3em; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content .shepherd-text p:last-child {
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer {
|
||||
padding: 0 1em 1em; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons {
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li .shepherd-button {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary {
|
||||
background: #eee;
|
||||
color: #888; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button {
|
||||
margin-right: 0; }
|
||||
|
||||
.shepherd-start-tour-button.shepherd-theme-dark {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
|
@ -0,0 +1,213 @@
|
|||
/* https://raw.githubusercontent.com/shipshapecode/shepherd/e3ed0fcbf5c31137ece409d3ad6fea4e264ca1cc/dist/css/shepherd-theme-default.css */
|
||||
.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before {
|
||||
box-sizing: border-box; }
|
||||
|
||||
.shepherd-element {
|
||||
position: absolute;
|
||||
display: none; }
|
||||
.shepherd-element.shepherd-open {
|
||||
display: block; }
|
||||
|
||||
.shepherd-element.shepherd-theme-default {
|
||||
max-width: 100%;
|
||||
max-height: 100%; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content {
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
background: #f6f6f6;
|
||||
color: #444;
|
||||
padding: 1em;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5em; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-width: 16px;
|
||||
border-style: solid;
|
||||
pointer-events: none; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-left-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-right-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
top: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
top: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #f6f6f6; }
|
||||
|
||||
.shepherd-element.shepherd-theme-default {
|
||||
z-index: 9999;
|
||||
max-width: 24em;
|
||||
font-size: 1em; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before {
|
||||
border-bottom-color: #e6e6e6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-has-title .shepherd-content header {
|
||||
background: #e6e6e6;
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-has-title .shepherd-content header a.shepherd-cancel-link {
|
||||
padding: 0;
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-has-cancel-link .shepherd-content header h3 {
|
||||
float: left; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content {
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17);
|
||||
padding: 0; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content * {
|
||||
font-size: inherit; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content header {
|
||||
*zoom: 1;
|
||||
border-radius: 5px 5px 0 0; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content header:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content header h3 {
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
font-weight: normal; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content header a.shepherd-cancel-link {
|
||||
float: right;
|
||||
text-decoration: none;
|
||||
font-size: 1.25em;
|
||||
line-height: .8em;
|
||||
font-weight: normal;
|
||||
color: black;
|
||||
opacity: 0.6;
|
||||
position: relative;
|
||||
top: .1em;
|
||||
padding: .8em;
|
||||
margin-bottom: -.8em; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content header a.shepherd-cancel-link:hover {
|
||||
opacity: 1; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content .shepherd-text {
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content .shepherd-text p {
|
||||
margin: 0 0 .5em 0;
|
||||
line-height: 1.3em; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content .shepherd-text p:last-child {
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer {
|
||||
padding: 0 1em 1em; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons {
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li .shepherd-button {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary {
|
||||
background: #eee;
|
||||
color: #888; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button {
|
||||
margin-right: 0; }
|
||||
|
||||
.shepherd-start-tour-button.shepherd-theme-default {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
|
@ -382,6 +382,8 @@ computer analysis, game chat and shareable URL.</string>
|
|||
<string name="follow">Follow</string>
|
||||
<string name="following">Following</string>
|
||||
<string name="unfollow">Unfollow</string>
|
||||
<string name="followX">Follow %s</string>
|
||||
<string name="unfollowX">Unfollow %s</string>
|
||||
<string name="block">Block</string>
|
||||
<string name="blocked">Blocked</string>
|
||||
<string name="unblock">Unblock</string>
|
||||
|
|
|
@ -31,4 +31,6 @@
|
|||
<item quantity="other">View all %s posts</item>
|
||||
</plurals>
|
||||
<string name="uploadAnImageForYourPost">Upload an image for your post</string>
|
||||
<string name="imageAlt">Image alternative text</string>
|
||||
<string name="imageCredit">Image credit</string>
|
||||
</resources>
|
||||
|
|
|
@ -42,3 +42,7 @@ cg-board {
|
|||
padding: 2em 4em;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -33,8 +33,10 @@
|
|||
&.gamebook-play {
|
||||
@media (min-width: 400px) and (min-aspect-ratio: 1/1) {
|
||||
grid-template-columns: minmax(200px, calc(100vh - 2.5rem)) minmax(200px, 1fr);
|
||||
grid-template-rows: auto 2.5rem;
|
||||
grid-template-areas: 'board tools' 'board controls';
|
||||
grid-template-rows: auto 3rem;
|
||||
grid-template-areas:
|
||||
'board tools'
|
||||
'board controls';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,11 +57,21 @@
|
|||
}
|
||||
|
||||
.analyse.variant-crazyhouse {
|
||||
grid-template-rows: 60px auto 2.5rem 60px;
|
||||
grid-template-areas:
|
||||
'pocket-top'
|
||||
'board'
|
||||
'pocket-bot'
|
||||
'tools'
|
||||
'controls';
|
||||
grid-template-rows: auto 100vw auto 1fr 3rem;
|
||||
height: calc(100vh - 2.5rem);
|
||||
|
||||
body.supports-max-content & {
|
||||
grid-template-rows: max-content auto 2.5rem max-content;
|
||||
@media (min-width: 400px) and (min-aspect-ratio: 1/1) {
|
||||
grid-template-rows: auto 1fr 3rem auto;
|
||||
grid-template-areas:
|
||||
'board pocket-top'
|
||||
'board tools'
|
||||
'board controls'
|
||||
'board pocket-bot';
|
||||
}
|
||||
|
||||
grid-template-areas: 'board pocket-top' 'board tools' 'board controls' 'board pocket-bot';
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
text-align: center;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
|
||||
@include transition;
|
||||
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
font-size: 1.1em;
|
||||
|
||||
h2 {
|
||||
margin: 1.3em 0 0.5em 0;
|
||||
margin: 1.8em 0 0.5em 0;
|
||||
border-bottom: 1px solid $c-brag;
|
||||
line-height: 2.5em;
|
||||
line-height: 1.5em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
p,
|
||||
|
@ -32,7 +33,8 @@
|
|||
@extend %roboto;
|
||||
|
||||
font-size: 1.5em;
|
||||
line-height: 2.5em;
|
||||
line-height: 1.5em;
|
||||
margin-top: 1.3em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
|
|
|
@ -1,6 +1,35 @@
|
|||
@import 'flash';
|
||||
|
||||
.ublog-post-form {
|
||||
padding-bottom: 5vh;
|
||||
|
||||
&__main .form-split,
|
||||
&__main > .form-group,
|
||||
&__main .form-actions,
|
||||
&__delete .form-actions,
|
||||
&__image {
|
||||
padding-left: var(--box-padding);
|
||||
padding-right: var(--box-padding);
|
||||
}
|
||||
&__image {
|
||||
background: $c-bg-zebra;
|
||||
padding-top: 5vh;
|
||||
}
|
||||
&__image-text {
|
||||
background: $c-bg-zebra;
|
||||
margin-bottom: 5vh;
|
||||
border-bottom: $border;
|
||||
font-size: 0.9em;
|
||||
.form-group {
|
||||
display: none;
|
||||
}
|
||||
&.visible {
|
||||
height: auto;
|
||||
.form-group {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ublog-post-image {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
|
@ -13,10 +42,8 @@
|
|||
}
|
||||
&__image {
|
||||
.form-group {
|
||||
text-align: center;
|
||||
@extend %flex-column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
img {
|
||||
@extend %box-neat;
|
||||
|
|
|
@ -52,9 +52,10 @@
|
|||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 1.3em;
|
||||
margin-top: 1.8em;
|
||||
border-bottom: 1px solid $c-brag;
|
||||
line-height: 2.5em;
|
||||
line-height: 1.5em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
|
|
|
@ -77,10 +77,18 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
&__views {
|
||||
white-space: nowrap;
|
||||
}
|
||||
&__image {
|
||||
@extend %box-neat-force;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
&-credit {
|
||||
font-size: 0.9em;
|
||||
color: $c-font-dim;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
&__intro {
|
||||
@extend %break-word;
|
||||
|
|
|
@ -3,8 +3,6 @@ import exportLichessGlobals from './site.lichess.globals';
|
|||
exportLichessGlobals();
|
||||
|
||||
export default function (opts: any) {
|
||||
document.body.classList.toggle('supports-max-content', !!window.chrome);
|
||||
|
||||
window.LichessAnalyse.start({
|
||||
...opts,
|
||||
socketSend: () => {},
|
||||
|
|
|
@ -28,11 +28,16 @@ const setupTopics = (el: HTMLTextAreaElement) =>
|
|||
});
|
||||
|
||||
const setupImage = (form: HTMLFormElement) => {
|
||||
const showText = () =>
|
||||
$('.ublog-post-form__image-text').toggleClass('visible', $('.ublog-post-image').hasClass('user-image'));
|
||||
const submit = () => {
|
||||
const replace = (html: string) => $(form).find('.ublog-post-image').replaceWith(html);
|
||||
const wrap = (html: string) => '<div class="ublog-post-image">' + html + '</div>';
|
||||
xhr.formToXhr(form).then(
|
||||
html => replace(html),
|
||||
html => {
|
||||
replace(html);
|
||||
showText();
|
||||
},
|
||||
err => replace(wrap(`<bad>${err}</bad>`))
|
||||
);
|
||||
replace(wrap(spinner));
|
||||
|
@ -40,6 +45,7 @@ const setupImage = (form: HTMLFormElement) => {
|
|||
};
|
||||
$(form).on('submit', submit);
|
||||
$(form).find('input[name="image"]').on('change', submit);
|
||||
showText();
|
||||
};
|
||||
|
||||
const setupMarkdownEditor = (el: HTMLTextAreaElement) => {
|
||||
|
|
Loading…
Reference in New Issue