Added forum post editing
This commit is contained in:
parent
9de86a83f9
commit
5070600964
@ -1,5 +1,5 @@
|
||||
component Postable(post arn.Postable, highlightAuthorID string)
|
||||
.post.mountable(id=post.ID(), data-highlight=post.Author().ID == highlightAuthorID)
|
||||
component Postable(post arn.Postable, user *arn.User, highlightAuthorID string)
|
||||
.post.mountable(id=strings.ToLower(post.Type()) + "-" + toString(post.ID()), data-highlight=post.Author().ID == highlightAuthorID, data-api="/api/" + strings.ToLower(post.Type()) + "/" + post.ID())
|
||||
.post-author
|
||||
Avatar(post.Author())
|
||||
|
||||
@ -9,34 +9,38 @@ component Postable(post arn.Postable, highlightAuthorID string)
|
||||
.post-content
|
||||
div(id="render-" + post.ID())!= post.HTML()
|
||||
|
||||
//- if user && user.ID === post.authorId
|
||||
//- textarea.post-input.hidden(id="source-" + post.ID)= post.text
|
||||
//- a.post-save.hidden(id="save-" + post.ID, onclick=`$.saveEdit("${type.toLowerCase()}", "${post.ID}")`)
|
||||
//- i.fa.fa-save
|
||||
//- span Save
|
||||
if user != nil && user.ID == post.Author().ID
|
||||
textarea.post-input.hidden(id="source-" + post.ID())= post.Text()
|
||||
.buttons.hidden(id="edit-toolbar-" + post.ID())
|
||||
a.button.post-save.action(data-action="savePost", data-trigger="click", data-id=post.ID())
|
||||
Icon("save")
|
||||
span Save
|
||||
|
||||
a.button.post-cancel-edit.action(data-action="editPost", data-trigger="click", data-id=post.ID())
|
||||
Icon("close")
|
||||
span Cancel
|
||||
|
||||
.post-toolbar(id="toolbar-" + post.ID())
|
||||
.spacer
|
||||
.post-likes(id="likes-" + post.ID(), title="Likes")= len(post.Likes())
|
||||
|
||||
//- if user != nil
|
||||
//- if user.ID !== post.authorId
|
||||
//- - var liked = post.likes && post.likes.indexOf(user.ID) !== -1
|
||||
if user != nil
|
||||
//- if user.ID !== post.authorId
|
||||
//- - var liked = post.likes && post.likes.indexOf(user.ID) !== -1
|
||||
|
||||
//- a.post-tool.post-like(id="like-" + post.ID, onclick=`$.like("${type.toLowerCase()}", "${post.ID}")`, title="Like", class=liked ? "hidden" : ")
|
||||
//- i.fa.fa-thumbs-up.fa-fw
|
||||
//- a.post-tool.post-like(id="like-" + post.ID, onclick=`$.like("${type.toLowerCase()}", "${post.ID}")`, title="Like", class=liked ? "hidden" : ")
|
||||
//- i.fa.fa-thumbs-up.fa-fw
|
||||
|
||||
//- a.post-tool.post-unlike(id="unlike-" + post.ID, onclick=`$.unlike("${type.toLowerCase()}", "${post.ID}")`, title="Unlike", class=!liked ? "hidden" : ")
|
||||
//- i.fa.fa-thumbs-down.fa-fw
|
||||
//- a.post-tool.post-unlike(id="unlike-" + post.ID, onclick=`$.unlike("${type.toLowerCase()}", "${post.ID}")`, title="Unlike", class=!liked ? "hidden" : ")
|
||||
//- i.fa.fa-thumbs-down.fa-fw
|
||||
|
||||
//- if type === "Posts" || type === "Threads"
|
||||
//- if user.ID === post.authorId
|
||||
//- a.post-tool.post-edit(onclick=`$.edit("${post.ID}")`, title="Edit")
|
||||
//- i.fa.fa-pencil.fa-fw
|
||||
if user.ID == post.Author().ID
|
||||
a.post-tool.post-edit.action(data-action="editPost", data-trigger="click", data-id=post.ID(), title="Edit")
|
||||
RawIcon("pencil")
|
||||
|
||||
if post.Type() != "Thread"
|
||||
a.post-tool.post-permalink.ajax(href=post.Link(), title="Permalink")
|
||||
Icon("link")
|
||||
RawIcon("link")
|
||||
|
||||
//- if type === "Messages" && user && (user.ID === post.authorId || user.ID === post.recipientId)
|
||||
//- a.post-tool.post-delete(onclick=`if(confirm("Do you really want to delete this ${typeSingular.toLowerCase()} from ${post.author.nick}?")) $.delete${typeSingular}("${post.ID}")`, title="Delete")
|
||||
|
@ -1,5 +1,5 @@
|
||||
component PostableList(postables []arn.Postable)
|
||||
component PostableList(postables []arn.Postable, user *arn.User)
|
||||
.thread
|
||||
.posts
|
||||
each post in postables
|
||||
Postable(post, "")
|
||||
Postable(post, user, "")
|
||||
|
@ -6,16 +6,18 @@ import (
|
||||
"github.com/aerogo/aero"
|
||||
"github.com/animenotifier/arn"
|
||||
"github.com/animenotifier/notify.moe/components"
|
||||
"github.com/animenotifier/notify.moe/utils"
|
||||
)
|
||||
|
||||
// Get post.
|
||||
func Get(ctx *aero.Context) string {
|
||||
id := ctx.Get("id")
|
||||
user := utils.GetUser(ctx)
|
||||
post, err := arn.GetPost(id)
|
||||
|
||||
if err != nil {
|
||||
return ctx.Error(http.StatusNotFound, "Post not found", err)
|
||||
}
|
||||
|
||||
return ctx.HTML(components.Post(post))
|
||||
return ctx.HTML(components.Post(post, user))
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
component Post(post *arn.Post)
|
||||
Postable(post.ToPostable(), "")
|
||||
component Post(post *arn.Post, user *arn.User)
|
||||
Postable(post.ToPostable(), user, "")
|
||||
|
||||
.side-note
|
||||
a.ajax(href=post.Thread().Link())= post.Thread().Title
|
||||
|
@ -3,6 +3,6 @@ component LatestPosts(postables []arn.Postable, viewUser *arn.User, user *arn.Us
|
||||
|
||||
if len(postables) > 0
|
||||
h2.page-title= len(postables), " latest posts by ", postables[0].Author().Nick
|
||||
PostableList(postables)
|
||||
PostableList(postables, user)
|
||||
else
|
||||
p.no-data.mountable= viewUser.Nick + " hasn't written any posts yet."
|
@ -3,10 +3,10 @@ component Thread(thread *arn.Thread, posts []*arn.Post, user *arn.User)
|
||||
|
||||
#thread.thread(data-id=thread.ID)
|
||||
.posts
|
||||
Postable(thread.ToPostable(), thread.Author().ID)
|
||||
Postable(thread.ToPostable(), user, thread.Author().ID)
|
||||
|
||||
each post in posts
|
||||
Postable(post.ToPostable(), thread.Author().ID)
|
||||
Postable(post.ToPostable(), user, thread.Author().ID)
|
||||
|
||||
// Reply
|
||||
if user != nil
|
||||
|
@ -43,7 +43,7 @@ func init() {
|
||||
}
|
||||
|
||||
if requestURI == "/dark-flame-master" {
|
||||
ctx.SetURI("/api/analytics/new")
|
||||
ctx.SetURI("/api/new/analytics")
|
||||
return
|
||||
}
|
||||
})
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Application } from "./Application"
|
||||
import { AnimeNotifier } from "./AnimeNotifier"
|
||||
import { Diff } from "./Diff"
|
||||
import { findAll } from "./Utils"
|
||||
|
||||
// Save new data from an input field
|
||||
export function save(arn: AnimeNotifier, input: HTMLInputElement | HTMLTextAreaElement) {
|
||||
@ -20,29 +21,15 @@ export function save(arn: AnimeNotifier, input: HTMLInputElement | HTMLTextAreaE
|
||||
obj[input.dataset.field] = value
|
||||
}
|
||||
|
||||
// console.log(input.type, input.dataset.api, obj, JSON.stringify(obj))
|
||||
|
||||
let apiObject: HTMLElement
|
||||
let parent = input as HTMLElement
|
||||
|
||||
while(parent = parent.parentElement) {
|
||||
if(parent.dataset.api !== undefined) {
|
||||
apiObject = parent
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if(!apiObject) {
|
||||
throw "API object not found"
|
||||
}
|
||||
|
||||
if(isContentEditable) {
|
||||
input.contentEditable = "false"
|
||||
} else {
|
||||
input.disabled = true
|
||||
}
|
||||
|
||||
fetch(apiObject.dataset.api, {
|
||||
let apiEndpoint = arn.findAPIEndpoint(input)
|
||||
|
||||
fetch(apiEndpoint, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(obj),
|
||||
credentials: "same-origin"
|
||||
@ -82,40 +69,46 @@ export function soon() {
|
||||
export function diff(arn: AnimeNotifier, element: HTMLElement) {
|
||||
let url = element.dataset.url || (element as HTMLAnchorElement).getAttribute("href")
|
||||
|
||||
arn.diff(url).then(() => {
|
||||
const duration = 250.0
|
||||
const steps = 60
|
||||
const interval = duration / steps
|
||||
const fullSin = Math.PI / 2
|
||||
const contentPadding = 24
|
||||
arn.diff(url).then(() => arn.scrollTo(element))
|
||||
}
|
||||
|
||||
let target = element
|
||||
let scrollHandle: number
|
||||
let oldScroll = arn.app.content.parentElement.scrollTop
|
||||
let newScroll = 0
|
||||
let finalScroll = Math.max(target.offsetTop - contentPadding, 0)
|
||||
let scrollDistance = finalScroll - oldScroll
|
||||
let timeStart = Date.now()
|
||||
let timeEnd = timeStart + duration
|
||||
// Edit post
|
||||
export function editPost(arn: AnimeNotifier, element: HTMLElement) {
|
||||
let postId = element.dataset.id
|
||||
|
||||
let scroll = () => {
|
||||
let time = Date.now()
|
||||
let progress = (time - timeStart) / duration
|
||||
let render = arn.app.find("render-" + postId)
|
||||
let toolbar = arn.app.find("toolbar-" + postId)
|
||||
let source = arn.app.find("source-" + postId)
|
||||
let edit = arn.app.find("edit-toolbar-" + postId)
|
||||
|
||||
if(progress > 1.0) {
|
||||
progress = 1.0
|
||||
}
|
||||
if(!render.classList.contains("hidden")) {
|
||||
render.classList.add("hidden")
|
||||
toolbar.classList.add("hidden")
|
||||
source.classList.remove("hidden")
|
||||
edit.classList.remove("hidden")
|
||||
} else {
|
||||
render.classList.remove("hidden")
|
||||
toolbar.classList.remove("hidden")
|
||||
source.classList.add("hidden")
|
||||
edit.classList.add("hidden")
|
||||
}
|
||||
}
|
||||
|
||||
newScroll = oldScroll + scrollDistance * Math.sin(progress * fullSin)
|
||||
arn.app.content.parentElement.scrollTop = newScroll
|
||||
// Save post
|
||||
export function savePost(arn: AnimeNotifier, element: HTMLElement) {
|
||||
let postId = element.dataset.id
|
||||
let source = arn.app.find("source-" + postId) as HTMLTextAreaElement
|
||||
let text = source.value
|
||||
|
||||
if(time < timeEnd && newScroll != finalScroll) {
|
||||
window.requestAnimationFrame(scroll)
|
||||
}
|
||||
}
|
||||
let updates = {
|
||||
Text: text,
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(scroll)
|
||||
})
|
||||
let apiEndpoint = arn.findAPIEndpoint(element)
|
||||
|
||||
arn.post(apiEndpoint, updates)
|
||||
.then(() => arn.reloadContent())
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
// Forum reply
|
||||
@ -129,7 +122,7 @@ export function forumReply(arn: AnimeNotifier) {
|
||||
tags: []
|
||||
}
|
||||
|
||||
arn.post("/api/post/new", post)
|
||||
arn.post("/api/new/post", post)
|
||||
.then(() => arn.reloadContent())
|
||||
.then(() => textarea.value = "")
|
||||
.catch(console.error)
|
||||
@ -147,7 +140,7 @@ export function createThread(arn: AnimeNotifier) {
|
||||
tags: [category.value]
|
||||
}
|
||||
|
||||
arn.post("/api/thread/new", thread)
|
||||
arn.post("/api/new/thread", thread)
|
||||
.then(() => arn.app.load("/forum/" + thread.tags[0]))
|
||||
.catch(console.error)
|
||||
}
|
||||
@ -168,7 +161,7 @@ export function createSoundTrack(arn: AnimeNotifier, button: HTMLButtonElement)
|
||||
button.innerText = "Adding..."
|
||||
button.disabled = true
|
||||
|
||||
arn.post("/api/soundtrack/new", soundtrack)
|
||||
arn.post("/api/new/soundtrack", soundtrack)
|
||||
.then(() => arn.app.load("/music"))
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
|
@ -321,6 +321,58 @@ export class AnimeNotifier {
|
||||
})
|
||||
}
|
||||
|
||||
scrollTo(target: HTMLElement) {
|
||||
const duration = 250.0
|
||||
const steps = 60
|
||||
const interval = duration / steps
|
||||
const fullSin = Math.PI / 2
|
||||
const contentPadding = 24
|
||||
|
||||
let scrollHandle: number
|
||||
let oldScroll = this.app.content.parentElement.scrollTop
|
||||
let newScroll = 0
|
||||
let finalScroll = Math.max(target.offsetTop - contentPadding, 0)
|
||||
let scrollDistance = finalScroll - oldScroll
|
||||
let timeStart = Date.now()
|
||||
let timeEnd = timeStart + duration
|
||||
|
||||
let scroll = () => {
|
||||
let time = Date.now()
|
||||
let progress = (time - timeStart) / duration
|
||||
|
||||
if(progress > 1.0) {
|
||||
progress = 1.0
|
||||
}
|
||||
|
||||
newScroll = oldScroll + scrollDistance * Math.sin(progress * fullSin)
|
||||
this.app.content.parentElement.scrollTop = newScroll
|
||||
|
||||
if(time < timeEnd && newScroll != finalScroll) {
|
||||
window.requestAnimationFrame(scroll)
|
||||
}
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(scroll)
|
||||
}
|
||||
|
||||
findAPIEndpoint(element: HTMLElement) {
|
||||
let apiObject: HTMLElement
|
||||
let parent = element
|
||||
|
||||
while(parent = parent.parentElement) {
|
||||
if(parent.dataset.api !== undefined) {
|
||||
apiObject = parent
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if(!apiObject) {
|
||||
throw "API object not found"
|
||||
}
|
||||
|
||||
return apiObject.dataset.api
|
||||
}
|
||||
|
||||
onPopState(e: PopStateEvent) {
|
||||
if(e.state) {
|
||||
this.app.load(e.state, {
|
||||
|
@ -32,5 +32,8 @@ a
|
||||
img
|
||||
backface-visibility hidden
|
||||
|
||||
.hidden
|
||||
display none !important
|
||||
|
||||
.spacer
|
||||
flex 1
|
@ -105,6 +105,12 @@
|
||||
.post-unlike
|
||||
color rgb(255, 32, 12) !important
|
||||
|
||||
.post-save
|
||||
//
|
||||
|
||||
.post-input
|
||||
min-height 200px
|
||||
|
||||
// Old
|
||||
|
||||
// #posts
|
||||
|
@ -1,6 +1,5 @@
|
||||
mixin input-focus
|
||||
:focus
|
||||
color black
|
||||
border 1px solid input-focus-border-color
|
||||
// TODO: Replace with alpha(main-color, 20%) function
|
||||
box-shadow 0 0 6px rgba(248, 165, 130, 0.2)
|
||||
|
Loading…
Reference in New Issue
Block a user