Compare commits
No commits in common. "330385a3571719727a3a5af312894e6d67d853c4" and "73442b212cf6fcf2d2efa892567bd4323a27589a" have entirely different histories.
330385a357
...
73442b212c
src
test-website
web/static
239
src/guestbook.go
239
src/guestbook.go
@ -1,239 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"dutchellie.nl/DutchEllie/proper-website-2/entity"
|
|
||||||
"github.com/maxence-charriere/go-app/v9/pkg/app"
|
|
||||||
)
|
|
||||||
|
|
||||||
type guestbook struct {
|
|
||||||
app.Compo
|
|
||||||
|
|
||||||
comments []entity.Comment
|
|
||||||
|
|
||||||
name string
|
|
||||||
email string
|
|
||||||
website string
|
|
||||||
message string
|
|
||||||
|
|
||||||
gbModalOpen bool
|
|
||||||
OnSubmit func(
|
|
||||||
ctx app.Context,
|
|
||||||
name string,
|
|
||||||
email string,
|
|
||||||
website string,
|
|
||||||
message string,
|
|
||||||
) // Handler to implement which calls the api
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *guestbook) OnMount(ctx app.Context) {
|
|
||||||
g.LoadComments(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *guestbook) OnNav(ctx app.Context) {
|
|
||||||
g.LoadComments(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *guestbook) OnUpdate(ctx app.Context) {
|
|
||||||
g.LoadComments(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g guestbook) Render() app.UI {
|
|
||||||
return app.Div().Body(
|
|
||||||
app.Form().
|
|
||||||
Class("guestbook-form").
|
|
||||||
Body(
|
|
||||||
app.Div().
|
|
||||||
Class("input-groups").
|
|
||||||
Body(
|
|
||||||
app.Div().
|
|
||||||
Class("fr").
|
|
||||||
Body(
|
|
||||||
app.Div().
|
|
||||||
Class("input-group").
|
|
||||||
Class("input-group-name").
|
|
||||||
Body(
|
|
||||||
app.Label().
|
|
||||||
For("name").
|
|
||||||
Text("Name:"),
|
|
||||||
app.Input().
|
|
||||||
Type("text").
|
|
||||||
Name("name").
|
|
||||||
Class("input").
|
|
||||||
Required(true).
|
|
||||||
OnChange(g.ValueTo(&g.name)),
|
|
||||||
),
|
|
||||||
app.Div().
|
|
||||||
Class("input-group").
|
|
||||||
Class("input-group-email").
|
|
||||||
Body(
|
|
||||||
app.Label().
|
|
||||||
For("email").
|
|
||||||
Text("Email: (optional)"),
|
|
||||||
app.Input().
|
|
||||||
Type("text").
|
|
||||||
Name("email").
|
|
||||||
Class("input").
|
|
||||||
Required(false).
|
|
||||||
OnChange(g.ValueTo(&g.email)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
app.Div().
|
|
||||||
Class("input-group").
|
|
||||||
Class("input-group-website").
|
|
||||||
Body(
|
|
||||||
app.Label().
|
|
||||||
For("website").
|
|
||||||
Text("Website: (optional)"),
|
|
||||||
app.Input().
|
|
||||||
Type("text").
|
|
||||||
Name("website").
|
|
||||||
Class("input").
|
|
||||||
Required(false).
|
|
||||||
OnChange(g.ValueTo(&g.website)),
|
|
||||||
),
|
|
||||||
app.Div().
|
|
||||||
Class("input-group").
|
|
||||||
Class("input-group-message").
|
|
||||||
Body(
|
|
||||||
app.Label().
|
|
||||||
For("message").
|
|
||||||
Text("Message:"),
|
|
||||||
app.Textarea().
|
|
||||||
Name("message").
|
|
||||||
Class("input").
|
|
||||||
Rows(5).
|
|
||||||
Cols(30).
|
|
||||||
Required(true).
|
|
||||||
OnChange(g.ValueTo(&g.message)),
|
|
||||||
),
|
|
||||||
app.Div().
|
|
||||||
Class("submit-field").
|
|
||||||
Body(
|
|
||||||
app.Input().
|
|
||||||
Type("submit").
|
|
||||||
Value("Send!"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).OnSubmit(func(ctx app.Context, e app.Event) {
|
|
||||||
// This was to prevent the page from reloading
|
|
||||||
e.PreventDefault()
|
|
||||||
if g.name == "" || g.message == "" {
|
|
||||||
fmt.Printf("Error: one or more field(s) are empty. For now unhandled\n")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(g.name) > 40 || len(g.message) > 360 {
|
|
||||||
fmt.Printf("Error: Your message is too long fucker\n")
|
|
||||||
g.gbModalOpen = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
g.OnSubmit(ctx, g.name, g.email, g.website, g.message)
|
|
||||||
ctx.Dispatch(func(ctx app.Context) {
|
|
||||||
g.clear()
|
|
||||||
})
|
|
||||||
g.LoadComments(ctx)
|
|
||||||
}),
|
|
||||||
app.If(
|
|
||||||
g.gbModalOpen,
|
|
||||||
&guestbookAlertModal{
|
|
||||||
OnClose: func() {
|
|
||||||
g.gbModalOpen = false
|
|
||||||
g.Update()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
app.Div().Body(
|
|
||||||
app.Range(g.comments).Slice(func(i int) app.UI {
|
|
||||||
return &guestbookComment{
|
|
||||||
Comment: g.comments[i],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *guestbook) LoadComments(ctx app.Context) {
|
|
||||||
// TODO: maybe you can put this in a localbrowser storage?
|
|
||||||
url := ApiURL
|
|
||||||
ctx.Async(func() {
|
|
||||||
res, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
app.Log(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
jsondata, err := io.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
app.Log(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Dispatch(func(ctx app.Context) {
|
|
||||||
err = json.Unmarshal(jsondata, &g.comments)
|
|
||||||
if err != nil {
|
|
||||||
app.Log(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *guestbook) clear() {
|
|
||||||
g.name = ""
|
|
||||||
g.message = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type guestbookAlertModal struct {
|
|
||||||
app.Compo
|
|
||||||
|
|
||||||
PreviousAttempts int
|
|
||||||
OnClose func() // For when we close the modal
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *guestbookAlertModal) Render() app.UI {
|
|
||||||
return app.Div().
|
|
||||||
Class("gb-modal").
|
|
||||||
ID("gbModal").
|
|
||||||
OnClick(func(ctx app.Context, e app.Event) {
|
|
||||||
g.OnClose()
|
|
||||||
}).
|
|
||||||
Body(
|
|
||||||
app.Div().
|
|
||||||
Class("gb-modal-content").
|
|
||||||
Body(
|
|
||||||
app.Span().Class("close").Text("X").
|
|
||||||
OnClick(func(ctx app.Context, e app.Event) {
|
|
||||||
//modal := app.Window().GetElementByID("gbModal")
|
|
||||||
//modal.Set("style", "none")
|
|
||||||
g.OnClose()
|
|
||||||
}),
|
|
||||||
app.P().Text("Your name must be <= 40 and your message must be <= 360 characters"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
type guestbookComment struct {
|
|
||||||
app.Compo
|
|
||||||
|
|
||||||
Comment entity.Comment
|
|
||||||
time string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *guestbookComment) Render() app.UI {
|
|
||||||
c.time = c.Comment.PostDate.Format(time.RFC1123)
|
|
||||||
return app.Div().Body(
|
|
||||||
app.Div().Class().Body(
|
|
||||||
app.P().Text(c.Comment.Name).Class("name"),
|
|
||||||
app.P().Text(c.time).Class("date"),
|
|
||||||
).Class("comment-header"),
|
|
||||||
app.Div().Class("comment-message").Body(
|
|
||||||
app.P().Text(c.Comment.Message),
|
|
||||||
),
|
|
||||||
).Class("comment")
|
|
||||||
}
|
|
@ -14,7 +14,6 @@ type guestbookForm struct {
|
|||||||
|
|
||||||
gbModalOpen bool
|
gbModalOpen bool
|
||||||
OnSubmit func(
|
OnSubmit func(
|
||||||
ctx app.Context,
|
|
||||||
name string,
|
name string,
|
||||||
message string,
|
message string,
|
||||||
) // Handler to implement which calls the api
|
) // Handler to implement which calls the api
|
||||||
@ -53,7 +52,7 @@ func (g *guestbookForm) Render() app.UI {
|
|||||||
g.gbModalOpen = true
|
g.gbModalOpen = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.OnSubmit(ctx, g.name, g.message)
|
g.OnSubmit(g.name, g.message)
|
||||||
g.clear()
|
g.clear()
|
||||||
}),
|
}),
|
||||||
app.If(
|
app.If(
|
||||||
@ -73,7 +72,6 @@ func (g *guestbookForm) clear() {
|
|||||||
g.message = ""
|
g.message = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
type guestbookAlertModal struct {
|
type guestbookAlertModal struct {
|
||||||
app.Compo
|
app.Compo
|
||||||
|
|
||||||
@ -102,4 +100,3 @@ func (g *guestbookAlertModal) Render() app.UI {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"dutchellie.nl/DutchEllie/proper-website-2/entity"
|
"dutchellie.nl/DutchEllie/proper-website-2/entity"
|
||||||
"github.com/maxence-charriere/go-app/v9/pkg/app"
|
"github.com/maxence-charriere/go-app/v9/pkg/app"
|
||||||
@ -27,58 +28,44 @@ type guestbookPanel struct {
|
|||||||
comments []entity.Comment
|
comments []entity.Comment
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *guestbookPanel) OnMount(ctx app.Context) {
|
func newGuestbookPanel() *guestbookPanel {
|
||||||
g.LoadComments(ctx)
|
g := &guestbookPanel{}
|
||||||
}
|
g.LoadComments()
|
||||||
|
return g
|
||||||
func (g *guestbookPanel) OnNav(ctx app.Context) {
|
|
||||||
g.LoadComments(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *guestbookPanel) OnUpdate(ctx app.Context) {
|
|
||||||
g.LoadComments(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *guestbookPanel) Render() app.UI {
|
func (g *guestbookPanel) Render() app.UI {
|
||||||
return app.Div().Body(
|
return app.Range(g.comments).Slice(func(i int) app.UI {
|
||||||
app.Range(g.comments).Slice(func(i int) app.UI {
|
return &guestbookComment{
|
||||||
return &guestbookComment{
|
Comment: g.comments[i],
|
||||||
Comment: g.comments[i],
|
}
|
||||||
}
|
},
|
||||||
},
|
)
|
||||||
),
|
|
||||||
).OnSubmit(func(ctx app.Context, e app.Event) {
|
|
||||||
g.LoadComments(ctx)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *guestbookPanel) LoadComments(ctx app.Context) {
|
func (g *guestbookPanel) LoadComments() {
|
||||||
// TODO: maybe you can put this in a localbrowser storage?
|
// TODO: maybe you can put this in a localbrowser storage?
|
||||||
url := ApiURL
|
url := ApiURL
|
||||||
ctx.Async(func() {
|
res, err := http.Get(url)
|
||||||
res, err := http.Get(url)
|
if err != nil {
|
||||||
if err != nil {
|
app.Log(err)
|
||||||
app.Log(err)
|
return
|
||||||
return
|
}
|
||||||
}
|
defer res.Body.Close()
|
||||||
defer res.Body.Close()
|
jsondata, err := io.ReadAll(res.Body)
|
||||||
jsondata, err := io.ReadAll(res.Body)
|
if err != nil {
|
||||||
if err != nil {
|
app.Log(err)
|
||||||
app.Log(err)
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Dispatch(func(ctx app.Context) {
|
err = json.Unmarshal(jsondata, &g.comments)
|
||||||
err = json.Unmarshal(jsondata, &g.comments)
|
if err != nil {
|
||||||
if err != nil {
|
app.Log(err)
|
||||||
app.Log(err)
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*type guestbookComment struct {
|
type guestbookComment struct {
|
||||||
app.Compo
|
app.Compo
|
||||||
|
|
||||||
Comment entity.Comment
|
Comment entity.Comment
|
||||||
@ -97,4 +84,3 @@ func (c *guestbookComment) Render() app.UI {
|
|||||||
),
|
),
|
||||||
).Class("comment")
|
).Class("comment")
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -11,6 +11,6 @@ func (h *header) Render() app.UI {
|
|||||||
Class("header").
|
Class("header").
|
||||||
Body(
|
Body(
|
||||||
app.Text("Internetica Galactica"),
|
app.Text("Internetica Galactica"),
|
||||||
//&updater{},
|
&updater{},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ func NewHomepage() *Homepage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Homepage) Render() app.UI {
|
func (p *Homepage) Render() app.UI {
|
||||||
|
gbp := newGuestbookPanel()
|
||||||
return newPage().
|
return newPage().
|
||||||
Title("Homepage").
|
Title("Homepage").
|
||||||
LeftBar(
|
LeftBar(
|
||||||
@ -40,12 +41,10 @@ func (p *Homepage) Render() app.UI {
|
|||||||
Class("right").
|
Class("right").
|
||||||
Class("contentblock").
|
Class("contentblock").
|
||||||
UI(
|
UI(
|
||||||
&guestbook{
|
&guestbookForm{
|
||||||
OnSubmit: func(ctx app.Context, name, email, website, message string) {
|
OnSubmit: func(name, message string) {
|
||||||
var comment entity.Comment
|
var comment entity.Comment
|
||||||
comment.Name = name
|
comment.Name = name
|
||||||
comment.Email = email
|
|
||||||
comment.Website = website
|
|
||||||
comment.Message = message
|
comment.Message = message
|
||||||
|
|
||||||
jsondata, err := json.Marshal(comment)
|
jsondata, err := json.Marshal(comment)
|
||||||
@ -55,24 +54,23 @@ func (p *Homepage) Render() app.UI {
|
|||||||
}
|
}
|
||||||
url := ApiURL
|
url := ApiURL
|
||||||
|
|
||||||
ctx.Async(func() {
|
req, err := http.Post(url, "application/json", bytes.NewBuffer(jsondata))
|
||||||
req, err := http.Post(url, "application/json", bytes.NewBuffer(jsondata))
|
if err != nil {
|
||||||
if err != nil {
|
fmt.Printf("err: %v\n", err)
|
||||||
fmt.Printf("err: %v\n", err)
|
return
|
||||||
return
|
}
|
||||||
}
|
if req.StatusCode == 200 {
|
||||||
if req.StatusCode == 200 {
|
p.Update()
|
||||||
p.Update()
|
}
|
||||||
}
|
defer req.Body.Close()
|
||||||
defer req.Body.Close()
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
/*
|
newUIBlock().
|
||||||
newUIBlock().
|
Class("right").
|
||||||
Class("right").
|
Class("contentblock").
|
||||||
Class("contentblock").
|
UI(
|
||||||
UI(),*/
|
gbp.Render(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ func main() {
|
|||||||
"/web/static/adreena.css",
|
"/web/static/adreena.css",
|
||||||
"/web/static/anisha.css",
|
"/web/static/anisha.css",
|
||||||
"/web/static/havakana.css",
|
"/web/static/havakana.css",
|
||||||
"/web/static/form.css",
|
|
||||||
},
|
},
|
||||||
CacheableResources: []string{},
|
CacheableResources: []string{},
|
||||||
}
|
}
|
||||||
|
@ -5,39 +5,48 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="../web/static/style.css">
|
<link rel="stylesheet" href="../web/static/style.css">
|
||||||
<link rel="stylesheet" href="../web/static/form.css">
|
|
||||||
<link rel="stylesheet" href="static/anisha.css?family=anisha">
|
<link rel="stylesheet" href="static/anisha.css?family=anisha">
|
||||||
<link rel="stylesheet" href="static/adreena.css?family=adreena">
|
<link rel="stylesheet" href="static/adreena.css?family=adreena">
|
||||||
<link rel="stylesheet" href="static/havakana.css?family=havakana">
|
<link rel="stylesheet" href="static/havakana.css?family=havakana">
|
||||||
<title>Index</title>
|
<title>Index</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="block right content">
|
<div class="header">
|
||||||
<form action="" class="guestbook-form">
|
Internetica Galactica
|
||||||
<div class="input-groups">
|
<div class="update-box">
|
||||||
<div class="fr">
|
<img src="/web/static/images/hot1.gif" alt="" height="50px" style="" class="pulsing">
|
||||||
<div class="input-group input-group-name">
|
<p class="update-message">
|
||||||
<label for="name">Name:</label>
|
An update is available! Click here to reload!
|
||||||
<input type="text" name="name" class="input">
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group input-group-email">
|
</div>
|
||||||
<label for="email">Email:</label>
|
<div class="main">
|
||||||
<input type="text" name="email" class="input">
|
<div class="navbar">
|
||||||
</div>
|
<ul>
|
||||||
</div>
|
<li><a href="base.html">Home</a></li>
|
||||||
<div class="input-group input-group-website">
|
</ul>
|
||||||
<label for="website">Website:</label>
|
</div>
|
||||||
<input type="text" name="website" class="input">
|
<div class="content">
|
||||||
</div>
|
Dit is eigenlijk 1 van die dingen die steeds kan veranderen
|
||||||
<div class="input-group input-group-message">
|
Deze doet dat
|
||||||
<label for="message">Message:</label>
|
</div>
|
||||||
<textarea name="message" cols="30" rows="5" class="input"></textarea>
|
|
||||||
</div>
|
<p class="content-text">
|
||||||
</div>
|
Welcome to my webspace! Whether you stumbled across this page by accident
|
||||||
<div class="submit-field">
|
or were linked here, you're more than welcome! This is my personal project that I like
|
||||||
<input type="submit" value="Send!">
|
to work on! I was inspired by a couple friends of mine, please do check their webspaces
|
||||||
</div>
|
out as well under "Galaxies" on the left side there!
|
||||||
</form>
|
If you like this page, there is a lot more, so have a look around! You can also leave a
|
||||||
|
nice message for me in the guestbook! There is no registration (unlike the rest of the "modern"
|
||||||
|
internet) so nothing of that sort!
|
||||||
|
That said, this website is my creative outlet and a way to introduce myself, so be kind please!
|
||||||
|
Also its code is entirely open-source and can be found
|
||||||
|
<a href="https://dutchellie.nl/DutchEllie/proper-website-2>here</a> so if you like that sort
|
||||||
|
of stuff, be my guest it's cool!</p>
|
||||||
|
<div class="content">
|
||||||
|
<p class="p-h1">My friends!</p>
|
||||||
|
<p>These are some of the websites of my friends!</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
.input-groups {
|
|
||||||
display:flex;
|
|
||||||
flex-wrap:wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-name {
|
|
||||||
width: 50%;
|
|
||||||
float:left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-email {
|
|
||||||
width: 50%;
|
|
||||||
float:right;
|
|
||||||
padding-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-website {
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-message {
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 16px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-field {
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fr {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
@ -126,11 +126,10 @@ body {
|
|||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
.guestbook-form-div {
|
.gbp {
|
||||||
margin: 0px 0px 10px 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
border: 2px solid;
|
border: 2px solid;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
Loading…
Reference in New Issue
Block a user