Compare commits

..

2 Commits

Author SHA1 Message Date
eb920c80fc
Wait don't deploy 2022-03-14 20:33:19 +01:00
1069d10f8e
Pain 2022-03-14 20:32:35 +01:00
69 changed files with 791 additions and 2978 deletions

View File

@ -1,90 +1,6 @@
#kind: pipeline
#type: kubernetes
#name: staging
#
#trigger:
# event:
# - push
# branch:
# - main
#
## STAGING!!!!
#steps:
#- name: build-wasm
# image: golang:1.17.8-alpine
# volumes:
# - name: build-staging
# path: /drone/src/build
# environment:
# APIURL: https://api.quenten.nl/api/testing
# CGO_ENABLED: 0
# commands:
# - mkdir ./build/web
# - GOARCH=wasm GOOS=js go build -o ./build/web/app.wasm -ldflags="-X 'main.ApiURL=$APIURL'" ./src
#- name: build-server
# image: golang:1.17.8-alpine
# volumes:
# - name: build-staging
# path: /drone/src/build
# environment:
# APIURL: https://api.quenten.nl/api/testing
# CGO_ENABLED: 0
# commands:
# - go build -o ./build/app -ldflags="-X 'main.ApiURL=$APIURL'" ./src
#- name: build-publish-image
# image: plugins/docker
# privileged: true
# volumes:
# - name: build-staging
# path: /drone/src/build
# settings:
# cache_from:
# - "dutchellie/proper-website-2:dev"
# username:
# from_secret: docker_username
# password:
# from_secret: docker_password
# dockerfile: Dockerfile
# repo: dutchellie/proper-website-2
# tags:
# - dev-${DRONE_COMMIT_SHA:0:8}
# depends_on:
# - build-wasm
# - build-server
#- name: deploy-staging
# image: pelotech/drone-helm3
# settings:
# mode: upgrade
# chart: .drone/helm/chart
# namespace: drone-staging
# release: newsite-staging
# skip_tls_verify: true
# values_files:
# - .drone/helm/staging-val.yaml
# values:
# - "image=dutchellie/proper-website-2:dev-${DRONE_COMMIT_SHA:0:8}"
# - "baseURL=staging.quenten.nl"
# - "name=newsite-staging"
# kube_api_server:
# from_secret: staging_api_server
# kube_token:
# from_secret: staging_kube_token
# kube_certificate:
# from_secret: staging_kube_certificate
# kube_service_account: drone-deploy
# dry_run: false
# depends_on:
# - build-publish-image
#
#volumes:
# - name: build-staging
# temp: {}
#
#---
kind: pipeline
type: kubernetes
name: production
name: default
trigger:
event:
@ -92,49 +8,71 @@ trigger:
branch:
- main
# PRODUCTION!!!!
# STAGING!!!!!!!!!!!!!
steps:
- name: build-wasm
image: golang:1.17.8-alpine
volumes:
- name: build
path: /drone/src/build
environment:
APIURL: https://api.quenten.nl/api
CGO_ENABLED: 0
commands:
- mkdir ./build/web
- GOARCH=wasm GOOS=js go build -o ./build/web/app.wasm -ldflags="-X 'main.ApiURL=$APIURL'" ./src
- name: build-server
image: golang:1.17.8-alpine
volumes:
- name: build
path: /drone/src/build
environment:
APIURL: https://api.quenten.nl/api
CGO_ENABLED: 0
commands:
- go build -o ./build/app -ldflags="-X 'main.ApiURL=$APIURL'" ./src
- name: build-publish-image
- name: docker
image: plugins/docker
privileged: true
volumes:
- name: build
path: /drone/src/build
settings:
cache_from:
- "dutchellie/proper-website-2:latest"
username:
username:
from_secret: docker_username
password:
from_secret: docker_password
dockerfile: Dockerfile
repo: dutchellie/proper-website-2
tags:
- latest-${DRONE_COMMIT_SHA:0:8}
depends_on:
- build-wasm
- build-server
build_args:
- APIURL=https://api.nicecock.eu/api/testingcomment
tags:
- dev
- ${DRONE_COMMIT_SHA:0:8}
- name: deploy-staging
image: pelotech/drone-helm3
settings:
mode: upgrade
chart: .drone/helm/chart
namespace: drone-staging
release: newsite-staging
skip_tls_verify: true
values_files:
# - .drone/helm/values.yaml
- .drone/helm/staging-val.yaml
values:
- "image=dutchellie/proper-website-2:${DRONE_COMMIT_SHA:0:8}"
kube_api_server:
from_secret: staging_api_server
kube_token:
from_secret: staging_kube_token
kube_certificate:
from_secret: staging_kube_certificate
kube_service_account: drone-deploy
dry_run: false
---
kind: pipeline
type: kubernetes
name: deploy-prod
trigger:
event:
- promote
target:
- production
branch:
- main
steps:
- name: docker
image: plugins/docker
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: dutchellie/proper-website-2
build_args:
- APIURL=https://api.nicecock.eu/api/comment
tags:
- latest
- ${DRONE_COMMIT_SHA:0:8}
- name: deploy-production
image: pelotech/drone-helm3
settings:
@ -144,11 +82,10 @@ steps:
release: newsite-production
skip_tls_verify: true
values_files:
# - .drone/helm/values.yaml
- .drone/helm/prod-val.yaml
values:
- "image=dutchellie/proper-website-2:latest-${DRONE_COMMIT_SHA:0:8}"
- "baseURL=old.quenten.nl"
- "name=newsite-prod"
- "image=dutchellie/proper-website-2:${DRONE_COMMIT_SHA:0:8}"
kube_api_server:
from_secret: prod_api_server
kube_token:
@ -157,131 +94,3 @@ steps:
from_secret: prod_kube_certificate
kube_service_account: drone-deploy
dry_run: false
depends_on:
- build-publish-image
volumes:
- name: build
temp: {}
---
kind: pipeline
type: kubernetes
name: feature-branch
trigger:
event:
- push
branch:
exclude:
- main
# FEATURE DEPLOY
steps:
- name: build-wasm
image: golang:1.17.8-alpine
volumes:
- name: build-feature
path: /drone/src/build
environment:
APIURL: https://api.quenten.nl/api/testing
CGO_ENABLED: 0
commands:
- mkdir ./build/web
- GOARCH=wasm GOOS=js go build -o ./build/web/app.wasm -ldflags="-X 'main.ApiURL=$APIURL'" ./src
- name: build-server
image: golang:1.17.8-alpine
volumes:
- name: build-feature
path: /drone/src/build
environment:
APIURL: https://api.quenten.nl/api/testing
CGO_ENABLED: 0
commands:
- go build -o ./build/app -ldflags="-X 'main.ApiURL=$APIURL'" ./src
- name: build-publish-image
image: plugins/docker
privileged: true
volumes:
- name: build-feature
path: /drone/src/build
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
dockerfile: Dockerfile
repo: dutchellie/proper-website-2
tags:
- feature-${DRONE_BRANCH}-${DRONE_COMMIT_SHA:0:8}
depends_on:
- build-wasm
- build-server
- name: deploy-staging
image: pelotech/drone-helm3
settings:
mode: upgrade
chart: .drone/helm/chart
namespace: drone-staging
release: feature-${DRONE_BRANCH}
skip_tls_verify: true
values_files:
- .drone/helm/staging-val.yaml
values:
- "image=dutchellie/proper-website-2:feature-${DRONE_BRANCH}-${DRONE_COMMIT_SHA:0:8}"
- "baseURL=${DRONE_BRANCH}.quenten.nl"
- "name=${DRONE_BRANCH}-deployment"
kube_api_server:
from_secret: staging_api_server
kube_token:
from_secret: staging_kube_token
kube_certificate:
from_secret: staging_kube_certificate
kube_service_account: drone-deploy
dry_run: false
depends_on:
- build-publish-image
volumes:
- name: build-feature
temp: {}
---
kind: pipeline
type: kubernetes
name: remove-feature-branch
trigger:
event:
- promote
target:
- destroy
branch:
exclude:
- main
steps:
- name: destroy
image: pelotech/drone-helm3
settings:
mode: uninstall
chart: .drone/helm/chart
namespace: drone-staging
release: feature-${DRONE_BRANCH}
skip_tls_verify: true
values_files:
- .drone/helm/staging-val.yaml
values:
- "image=dutchellie/proper-website-2:feature-${DRONE_BRANCH}-${DRONE_COMMIT_SHA:0:8}"
- "baseURL=${DRONE_BRANCH}.quenten.nl"
- "name=${DRONE_BRANCH}-deployment"
kube_api_server:
from_secret: staging_api_server
kube_token:
from_secret: staging_kube_token
kube_certificate:
from_secret: staging_kube_certificate
kube_service_account: drone-deploy
dry_run: false

View File

@ -1,3 +1,3 @@
apiVersion: v2
name: newsite
version: v0.0.2
version: v0.0.1

View File

@ -31,9 +31,5 @@ spec:
ports:
- name: http
containerPort: 8000
{{- if .Values.containerEnv }}
env: {{ toYaml .Values.containerEnv | nindent 12 }}
{{- end }}

View File

@ -1,11 +1,11 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ tpl .Values.ingress.name . }}
name: {{ .Values.ingress.name }}
namespace: {{ .Release.Namespace }}
{{- with .Values.ingress.annotations }}
annotations:
{{- tpl (. | toYaml) $ | nindent 4 }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.ingress.className }}
@ -14,14 +14,14 @@ spec:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ tpl . $ | quote }}
- {{ . | quote }}
{{- end }}
secretName: {{ tpl (.secretName | toYaml) $ }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ tpl .host $ | quote }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
@ -29,7 +29,7 @@ spec:
pathType: {{ .pathType }}
backend:
service:
name: {{ tpl $.Values.service.name $ }}
name: {{ $.Values.service.name }}
port:
number: 8000
{{- end }}

View File

@ -1,7 +1,7 @@
apiVersion: v1
kind: Service
metadata:
name: {{ tpl .Values.service.name . }}
name: {{ .Values.service.name }}
namespace: {{ .Release.Namespace }}
{{- with .Values.service.annotations }}
annotations:

View File

@ -6,7 +6,6 @@ podAnnotations: {}
containerName: newsite
image: dutchellie/proper-website-2:latest
imagePullPolicy: Always
containerEnv: []
service:
name: newsite-service

View File

@ -1,24 +1,17 @@
baseURL: old.quenten.nl
name: newsite-prod
containerEnv:
- name: APIURL
value: https://api.quenten.nl/api
service:
name: "{{ .Values.name }}"
name: newsite-prod
ingress:
name: "{{ .Values.name }}"
name: newsite-prod
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
external-dns.alpha.kubernetes.io/hostname: "{{ .Values.baseURL }}"
nginx.ingress.kubernetes.io/configuration-snippet: |
add_header Content-Security-Policy "frame-ancestors 'self' https://forestofunix.xyz";
proxy_hide_header X-Frame-Options ;
cert-manager.io/cluster-issuer: letsencrypt-prod
external-dns.alpha.kubernetes.io/hostname: "newsite.dutchellie.nl"
tls:
- hosts:
- "{{ .Values.baseURL }}"
secretName: "{{ .Values.name }}-tls"
- newsite.dutchellie.nl
secretName: newsite-tls
hosts:
- host: "{{ .Values.baseURL }}"
- host: newsite.dutchellie.nl
paths:
- path: /
pathType: Prefix

View File

@ -1,24 +1,17 @@
baseURL: staging.quenten.nl
name: newsite-staging
containerEnv:
- name: APIURL
value: https://api.quenten.nl/api/testing
service:
name: "{{ .Values.name }}"
name: newsite-staging
ingress:
name: "{{ .Values.name }}"
name: newsite-staging
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-staging"
external-dns.alpha.kubernetes.io/hostname: "{{ .Values.baseURL }}"
nginx.ingress.kubernetes.io/configuration-snippet: |
add_header Content-Security-Policy "frame-ancestors 'self' https://forestofunix.xyz";
proxy_hide_header X-Frame-Options ;
cert-manager.io/cluster-issuer: letsencrypt-staging
external-dns.alpha.kubernetes.io/hostname: "newsite.staging.dutchellie.nl"
tls:
- hosts:
- "{{ .Values.baseURL }}"
secretName: "{{ .Values.name }}-tls"
- newsite.staging.dutchellie.nl
secretName: newsite-staging-tls
hosts:
- host: "{{ .Values.baseURL }}"
- host: newsite.staging.dutchellie.nl
paths:
- path: /
pathType: Prefix

3
.gitignore vendored
View File

@ -1,5 +1,4 @@
.vscode
app
web/*.wasm
staticsite
node_modules
staticsite

View File

@ -1,17 +1,16 @@
#FROM golang:1.17.8-alpine AS builder
#ARG APIURL
#WORKDIR /project
#ADD . /project/
#RUN go mod tidy
#RUN GOARCH=wasm GOOS=js go build -o web/app.wasm -ldflags="-X 'main.ApiURL=$APIURL'" ./src
#RUN go build -o app -ldflags="-X 'main.ApiURL=$APIURL'" ./src
FROM golang:1.17.8-alpine AS builder
ARG APIURL
WORKDIR /project
ADD . /project/
RUN go mod tidy
RUN GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=$APIURL'" -o web/app.wasm
RUN go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=$APIURL'" -o app
FROM alpine:latest
FROM alpine:latest AS staging
RUN apk --no-cache add ca-certificates
WORKDIR /root
RUN mkdir ./web
COPY ./web ./web
COPY ./build/web/app.wasm ./web/
COPY ./build/app ./
WORKDIR /root/
COPY --from=builder /project/web ./web/
COPY --from=builder /project/app ./
EXPOSE 8000
ENV TESTING true
CMD ["./app"]

View File

@ -1,26 +1,16 @@
APIURL_prod := https://api.quenten.nl/api
APIURL_staging := https://api.quenten.nl/api/testing
APIURL_prod := https://api.nicecock.eu/api/comment
APIURL_staging := https://api.nicecock.eu/api/testingcomment
build:
GOARCH=wasm GOOS=js go build -o web/app.wasm -ldflags="-X 'main.ApiURL=${APIURL_staging}'" ./src
go build -o app -ldflags="-X 'main.ApiURL=${APIURL_staging}'" ./src
cp -r web staticsite/
build-new:
GOARCH=wasm GOOS=js go build -o web/app.wasm ./src
go build -o app ./src
cp -r web staticsite/
GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_staging}'" -o web/app.wasm
go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_staging}'" -o app
build-prod:
GOARCH=wasm GOOS=js go build -o web/app.wasm -ldflags="-X 'main.ApiURL=${APIURL_prod}'" ./src
go build -o app -ldflags="-X 'main.ApiURL=${APIURL_prod}'" ./src
cp -r web staticsite/
GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_prod}'" -o web/app.wasm
go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_prod}'" -o app
run: build
./app
run-new: build-new
APIURL=${APIURL_staging} ./app
run-prod: build-prod
./app

View File

@ -1,23 +1,14 @@
# proper-website-2
[![Build Status](https://drone.dutchellie.nl/api/badges/DutchEllie/proper-website-2/status.svg)](https://drone.dutchellie.nl/DutchEllie/proper-website-2)
[![Build Status](https://drone.nicecock.eu/api/badges/DutchEllie/proper-website-2/status.svg)](https://drone.nicecock.eu/DutchEllie/proper-website-2)
A truly proper website this time™
**TODO**:
- Redo the entire CSS with a framework
- Change domain to quenten.nl and staging.quenten.nl
- Dynamically make domains for other branches
- Make a generic page component, so that you don't have to add the standard panels every time (such as navbar, leftbar, header, etc)
App notes:
- leftbar (under nav) is default always the same.
This is in a standardized "page" component.
it can be changed by specifying it when creating the "page" component, or maybe in a prerender/onnav (or both).
Structure will be:
Generic page component IS IMPLEMENTED BY "specific page" WHERE OnNav/OnPreRender specify its details WHICH IS CREATED with its own "newXPage()" function. See https://github.com/maxence-charriere/go-app/blob/master/docs/src/actions-page.go for example.
- "main content" is an array of app.UI elements.
This can also be further abstracted using "block" components (the ones with the cool color)
- THOSE BLOCK COMPONENTS' CONTENT can be set manually on creation, OR by passing HTML/MarkDown (not decided yet)
This website will be done with this:
- Backend written in Golang

View File

@ -1,62 +0,0 @@
package api
import (
"log"
"net/http"
"net/http/cookiejar"
"github.com/google/uuid"
"github.com/gorilla/mux"
"golang.org/x/net/publicsuffix"
)
type ApiApplication interface {
NewRouter() *mux.Router
Visit(w http.ResponseWriter, r *http.Request)
}
func NewApiApp() (ApiApplication, error) {
cookiejar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
if err != nil {
return nil, err
}
return &apiapp{
cj: cookiejar,
}, nil
}
type apiapp struct {
router mux.Router
cj *cookiejar.Jar
}
func (a *apiapp) NewRouter() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/visit", a.Visit)
return router
}
// Called when someone visits any page of the website
// Calls for the spyware cookie and sets it if it doesn't yet exist
func (a *apiapp) Visit(w http.ResponseWriter, r *http.Request) {
log.Printf("Visit called\n")
c, err := r.Cookie("spyware")
if err != nil && err != http.ErrNoCookie {
log.Printf("Error: %s\n", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
} else if err == http.ErrNoCookie {
// Create cookie and send it
log.Printf("No cookie sent by client, sending cookie to them!\n")
c = &http.Cookie{Name: "spyware", Value: uuid.NewString(), Path: "/", MaxAge: 0}
http.SetCookie(w, c)
}
w.WriteHeader(200)
w.Write([]byte("yeet"))
log.Printf("Someone visited: %s\n", c.Value)
}

47
components/aboutpage.go Normal file
View File

@ -0,0 +1,47 @@
package components
import (
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type AboutPage struct {
app.Compo
}
func NewAboutPage() *AboutPage {
return &AboutPage{}
}
func (a *AboutPage) Render() app.UI {
return app.Div().Body(
&header{},
&navbar{},
&aboutPanel{},
)
}
type aboutPanel struct {
app.Compo
aboutText string
}
func (a *aboutPanel) Render() app.UI {
return app.Div().Body(
app.Img().Src("/web/static/images/rin-1.gif").Styles(map[string]string{"width": "100px", "position": "absolute", "top": "10px", "right": "10px"}),
app.Raw(`<p class="content-text">I am a 21 year old computer science student, living and studying in The Netherlands. I like Docker, Kubernetes and Golang!
<br>
I made this website because I was inspired again by the amazing Neocities pages that I discovered because of my friends.
They also have their own pages (you can find them on the friends tab, do check them out!) and I just had to get a good website of my own!
<br>
I am not that great at web development, especially design, but I love trying it regardless!
<br><br>
To say a bit more about me personally, I love all things computers. From servers to embedded devices! I love the cloud and all that it brings
(except for big megacorps, but alright) and it's my goal to work for a big cloud company!
<br>
Aside from career path ambitions, ボーカロイドはすきです I love vocaloid and other Japanese music and culture!!
I also like Vtubers, especially from Hololive and it's my goal to one day finally understand them in their native language!
<br><br>
There is a lot more to say in words, but who cares about those! Have a look around my creative digital oasis and see what crazy stuff you can find!</p>`),
).Class("content")
}

View File

@ -0,0 +1,23 @@
package components
import (
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type contentView struct {
app.Compo
panels []app.UI
}
func newContentView(panels ...app.UI) *contentView {
return &contentView{panels: panels}
}
func (c *contentView) Render() app.UI {
return app.Div().Body(
app.Range(c.panels).Slice(func(i int) app.UI {
return c.panels[i]
}),
)
}

View File

@ -0,0 +1,81 @@
package components
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type GalaxiesPage struct {
app.Compo
}
func NewGalaxiesPage() *GalaxiesPage {
return &GalaxiesPage{}
}
func (f *GalaxiesPage) Render() app.UI {
return app.Div().Body(
&header{},
&navbar{},
&galaxiesPanel{},
).Class("main")
}
type galaxiesPanel struct {
app.Compo
}
func (b *galaxiesPanel) Render() app.UI {
return app.Div().Body(
app.P().
Text(`Galaxies`).
Class("p-h1"),
app.P().
Class("content-text").
Text(`Here you can find some really really really cool pages that I found on the internet.
Some of these are blogs or even blogposts I found, but the ones on top are special!
They're the websites of friends of mine! Please visit them, because they worked really hard
on their websites as well!`),
app.Div().
Body(
app.P().
Class("p-h2 m-tb10").
Text("My friends!"),
app.Ul().Body(
app.Li().Body(
app.Div().Body(
// TODO: Create a modal popup for each name!!!
app.A().Href("https://forestofunix.xyz").
Class("p-h3 m-t5").
Text("Forest of Unix"),
app.P().
Class("m-t5").
Text(`A website by Sebastiaan. A massive Linux fanboy, runs Gentoo on his
ThinkPad. Absolutely based.`),
),
),
app.Li().Body(
app.Div().Body(
// TODO: Create a modal popup for each name!!!
app.A().Href("https://nymphali.neocities.org").
Class("p-h3 m-t5").
Text("Nymphali"),
app.P().
Class("m-t5").
Text(`The website made by , whoops Nymphali. They have an awesome
minimalist website that's just lovely.`),
),
),
app.Li().Body(
app.Div().Body(
// TODO: Create a modal popup for each name!!!
app.A().Href("https://kristypixel.neocities.org").
Class("p-h3 m-t5").
Text("Kristy"),
app.P().
Class("m-t5").
Text(`Website made by Kristy. Very cute website, I love it! Keep up the
awesome work!`),
),
),
),
),
).Class("content")
}

102
components/guestbookform.go Normal file
View File

@ -0,0 +1,102 @@
package components
import (
"fmt"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type guestbookForm struct {
app.Compo
name string
message string
gbModalOpen bool
OnSubmit func(
name string,
message string,
) // Handler to implement which calls the api
}
func (g *guestbookForm) Render() app.UI {
return app.Div().Body(
app.Form().Body(
app.Input().
Type("text").
Name("name").
Placeholder("Name").
Required(true).
OnChange(g.ValueTo(&g.name)).
Value(g.name),
app.Input().
Type("text").
Name("message").
Placeholder("Message").
Required(true).
OnChange(g.ValueTo(&g.message)).
Value(g.message),
app.Input().
Type("submit").
Name("submit"),
).ID("form").
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(g.name, g.message)
g.clear()
}),
app.If(
g.gbModalOpen,
&guestbookAlertModal{
OnClose: func() {
g.gbModalOpen = false
g.Update()
},
},
),
).Class("content")
}
func (g *guestbookForm) 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"),
),
)
}

View File

@ -0,0 +1,87 @@
package components
import (
"encoding/json"
"io"
"net/http"
"time"
"dutchellie.nl/DutchEllie/proper-website-2/entity"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
/*
What is this supposed to do:
- It should call on the API to give it a certain number of comments, in the range x to y (this has to be implemented in the api)
- When it has called that, it should display those
- Dynamic links are there to navigate the user along the pages
- Comments are shown dynamically
- This panel can be shown or hidden (maybe)
AND VERY IMPORTANT!
- If a user submits a new comment, automatically put it on the page, no reloading
*/
type guestbookPanel struct {
app.Compo
comments []entity.Comment
}
func newGuestbookPanel() *guestbookPanel {
g := &guestbookPanel{}
g.LoadComments()
return g
}
func (g *guestbookPanel) Render() app.UI {
return app.Div().Body(
app.Range(g.comments).Slice(func(i int) app.UI {
return &guestbookComment{
Comment: g.comments[i],
}
}),
).Class("content gbp")
}
func (g *guestbookPanel) LoadComments() {
// TODO: maybe you can put this in a localbrowser storage?
url := ApiURL
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
}
err = json.Unmarshal(jsondata, &g.comments)
if err != nil {
app.Log(err)
return
}
}
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")
}

17
components/header.go Normal file
View File

@ -0,0 +1,17 @@
package components
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type header struct {
app.Compo
}
func (h *header) Render() app.UI {
return app.Div().
Class("border-solid border-4 border-borderpink rounded").
Body(
app.H1().
Class("font-[anisha] bg-cool text-aliceblue text-[80px] text-center").
Text("Internetica Galactica"),
)
}

67
components/homepage.go Normal file
View File

@ -0,0 +1,67 @@
package components
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"dutchellie.nl/DutchEllie/proper-website-2/entity"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
var (
ApiURL string
)
type Homepage struct {
app.Compo
showGuestbook bool
page string
}
func NewHomepage() *Homepage {
return &Homepage{showGuestbook: true, page: "home"}
}
func (p *Homepage) Render() app.UI {
gbp := newGuestbookPanel()
return app.Div().
Body(
&header{},
&navbar{},
&homePanel{
onShowClick: func() {
p.showGuestbook = !p.showGuestbook
},
},
&guestbookForm{
OnSubmit: func(name, message string) {
var comment entity.Comment
comment.Name = name
comment.Message = message
jsondata, err := json.Marshal(comment)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
url := ApiURL
req, err := http.Post(url, "application/json", bytes.NewBuffer(jsondata))
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
if req.StatusCode == 200 {
p.Update()
}
defer req.Body.Close()
},
},
//app.If(p.showGuestbook, gbp),
gbp.Render(),
).Class("mt-1.5 w-[900px] mx-auto ")
}

49
components/homepanel.go Normal file
View File

@ -0,0 +1,49 @@
package components
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type homePanel struct {
app.Compo
onShowClick func()
updateAvailable bool
}
func (p *homePanel) Render() app.UI {
return app.Div().
Class("flex flex-nowrap text-aliceblue").
Body(
app.Div().
Class("flex-auto").
Body(
app.P().Text("Welcome, internet surfer!").Class("p-h1"),
app.P().
Class("").
Text(`Welcome to my website! Whether you came here by accident or were linked to it, I welcome you!
Have a look around my page to see all the stuff I put on it!
I put in a lot of effort, probably the most out of any project I have attempted, so it would be nice
if you left a nice comment down in the guestbook.
There is no signup required (unlike the stupid modern web where that's "essential").
Go crazy, write whatever you want! Just be nice!
Above all, enjoy yourself in my little online webspace!!!`),
app.If(p.updateAvailable,
app.Div().Body(
app.P().
Class("content-text").
Text("An update is available! Reload to update!"),
)),
),
app.Div().Body(
app.P().Text("Please sign my guestbook!").Class("small"),
app.Img().Src("/web/static/images/email3.gif").Style("width", "40px").Style("position", "absolute").Style("bottom", "0px").Style("right", "0px"),
).Style("position", "absolute").Style("bottom", "5px").Style("right", "5px").
OnClick(func(ctx app.Context, e app.Event) {
e.PreventDefault()
p.onShowClick()
}),
).Class("content")
}
func (p *homePanel) OnAppUpdate(ctx app.Context) {
p.updateAvailable = true
}

View File

@ -1,4 +1,4 @@
package main
package components
import "github.com/maxence-charriere/go-app/v9/pkg/app"

25
components/navbar.go Normal file
View File

@ -0,0 +1,25 @@
package components
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type navbar struct {
app.Compo
OnClickButton func(page string)
}
func (n *navbar) Render() app.UI {
return app.Div().Body(
app.Ul().Body(
app.Li().Body(
app.A().Href("/").Text("Home"),
),
app.Li().Body(
app.A().Href("/about").Text("About"),
),
app.Li().Body(
app.A().Href("/galaxies").Text("Galaxies"),
),
),
).Class("border-solid border-4 rounded border-borderpink text-aliceblue bg-cool float-left relative w-[180px] font-[havakana] text-center")
}

27
components/updater.go Normal file
View File

@ -0,0 +1,27 @@
package components
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type updater struct {
app.Compo
updateAvailable bool
}
func (u *updater) onAppUpdate(ctx app.Context) {
u.updateAvailable = ctx.AppUpdateAvailable()
}
func (u *updater) Render() app.UI {
return app.Div().Body(
app.If(u.updateAvailable,
app.Div().Body(
app.P().Text("An update for this website is available! Please click here to reload!"),
).Styles(map[string]string{"position": "absolute", "width": "100px", "bottom": "10px", "right": "10px"}).OnClick(u.onUpdateClick),
),
)
}
func (u *updater) onUpdateClick(ctx app.Context, e app.Event) {
ctx.Reload()
}

1
design-desktop.drawio Normal file
View File

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2022-03-14T17:54:08.495Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36" etag="OBbzwV6CMD5jW8t-1HUz" version="17.1.2" type="browser"><diagram name="Page-1" id="03018318-947c-dd8e-b7a3-06fadd420f32">5VhRb5swEP41PLbCGEjy2CRdq6ntpnVSnx1wwIrB1Jgm7a/fGewQAlVTqUyLlofE/s6c8Xff+ew4eJHtbiQp0nsRU+54brxz8NLxPIRDF3408togkwlugESy2AxqgUf2Rg1onksqFtOyM1AJwRUrumAk8pxGqoMRKcW2O2wteHfWgiS0BzxGhPfRJxar1KDIdVvDLWVJaqaeBsawItEmkaLKzXyOh9f1pzFnxPoy48uUxGJ7AOFrBy+kEKppZbsF5ZpbS1vz3Ld3rPv3ljRXpzwQXSf323T2nbBw++NhdvdTTMmFidUL4ZXh45aSmErzyurV0lQvlGpXroPn25Qp+liQSFu3IAzAUpVx6CFolhuqotR2lBSbPbca6b+5WcwLlYruDiCzkhsqMqrkKwwxVs+qzKjOqnDbhjAIggZLD6KHrOyIkU2yd91SBw3D3ieY9HtMnhuHaNonEeEBEj1/LBKDHokP5IUlRDGR9+iEZaouZ4SzJId2BKSAhvFck8Eg16+MIWNxrB+fS1qyN7KqXelgFILlql5NMHeCpfZVKVE2u1UbgYXgAvwuc5FrL2vG+RH0BXHBbnAZdCLjuf3ITNyBwIwVl/Dsxe3jE8WN/WAkEic9EucEapos9WRQRbyQZJqnfFUWNQkh1/pewWYcJrolYZTIdIHUyv9f0sGbHaeD7/djORtIBzxWOszOLh18NP1wr/fRAIdoNhaJCA2w2Eg+RVbyT5RHMJ81wEStzYKFBe6EpDo94OhY6d+41iKEhSm9AHhtx1s0R8kSjpJUVdpKYlawkkUsT6BHOTOjSoie9qFBVpWZiOvMy4raJ8sjFrO4yrXrSn9xsoL59Whl56b1OTDJiZ6Gs+eKXB6spGgX8mEqn5pvJWhMLwQvg7b3W4Delhfee0oUoKM1r8+mKewINNc7Qke/HYF+hRxtar4eqexAjoMVDk1Gk6PXk+NNRUu1EmJT3ytkdm5JjgeS3HO9AVZH2ylR/4Lxy1axla1+x3WuLobDRe4fYzw80vEQ42g2/auMn99F5Fi3gXdicfJHO/Ci/k3E6nQt6j2/pTN8roQ1XDRnpCsYgKbFrjVabR/sKftC0Hj88lpw+vFvSAHjFoAAdRMnHLjh7K8zhyEPPx9x6Lb/stS2g7+y8PUf</diagram></mxfile>

View File

@ -1,15 +0,0 @@
package entity
import "time"
// Blogpost contains the name, date and such of a blogpost
// The actual post itself is hosted somewhere else in the form of an (html) document
// This is put in the path field
type BlogPost struct {
ID string `json:"id,omitempty" bson:"_id,omitempty"`
Title string `json:"title" bson:"title"`
Author string `json:"author" bson:"author"`
Path string `json:"path" bson:"path"`
PostDate time.Time `json:"time" bson:"time"`
}

View File

@ -9,6 +9,4 @@ type Comment struct {
Email string `json:"email,omitempty" bson:"email,omitempty"`
Message string `json:"message" bson:"message"`
PostDate time.Time `json:"time" bson:"time"`
UUID string `json:"uuid" bson:"uuid"`
}

13
go.mod
View File

@ -2,15 +2,6 @@ module dutchellie.nl/DutchEllie/proper-website-2
go 1.17
require (
github.com/google/uuid v1.3.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/maxence-charriere/go-app/v9 v9.3.3
golang.org/x/net v0.0.0-20220622184535-263ec571b305
)
require github.com/maxence-charriere/go-app/v9 v9.3.3
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect
)
require github.com/google/uuid v1.3.0 // indirect

15
go.sum
View File

@ -1,16 +1,9 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gomarkdown/markdown v0.0.0-20210408062403-ad838ccf8cdd/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/maxence-charriere/go-app/v9 v9.3.3 h1:vo+1oohWMfTQ0S3eg9JOjuFEy1n3bVNgeDi4eHNfMzA=
github.com/maxence-charriere/go-app/v9 v9.3.3/go.mod h1:zo0n1kh4OMKn7P+MrTUUi7QwUMU2HOfHsZ293TITtxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -20,16 +13,10 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20220622184535-263ec571b305 h1:dAgbJ2SP4jD6XYfMNLVj0BF21jo2PjChrtGaAvF5M3I=
golang.org/x/net v0.0.0-20220622184535-263ec571b305/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=

104
main.go Normal file
View File

@ -0,0 +1,104 @@
package main
import (
"log"
"net/http"
"dutchellie.nl/DutchEllie/proper-website-2/components"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
//type application struct {
// client *mongo.Client
// database *mongo.Database
// collection *mongo.Collection
//}
const (
apiurl = "https://quenten.nl:8007/"
)
func main() {
homepage := components.NewHomepage()
aboutpage := components.NewAboutPage()
galaxiespage := components.NewGalaxiesPage()
app.Route("/", homepage)
app.Route("/about", aboutpage)
app.Route("/galaxies", galaxiespage)
// This is executed on the client side only.
// It handles client side stuff
// It exits immediately on the server side
app.RunWhenOnBrowser()
icon := &app.Icon{
Default: "/web/static/images/icon-small.png",
Large: "/web/static/images/icon.png",
}
handler := &app.Handler{
Name: "Internetica Galactica",
Icon: *icon,
BackgroundColor: "#362730",
ThemeColor: "#362730",
LoadingLabel: "Internetica Galactica",
Title: "Internetica Galactica",
Description: "A 1990's style PWA!",
Author: "Quenten",
Keywords: []string{
"Based website",
"Cool website",
"PWA",
"Programming",
"Go", "Golang",
"Webassembly", "WASM",
"DutchEllie", "Quenten",
},
Styles: []string{
//"/web/static/style.css",
"/web/static/newstyle.css",
"/web/static/adreena.css",
"/web/static/anisha.css",
"/web/static/havakana.css",
},
Scripts: []string{
"https://cdn.tailwindcss.com",
"/web/static/custom.js",
},
CacheableResources: []string{},
}
app.GenerateStaticWebsite("./staticsite", handler)
/*
uri := "mongodb+srv://guestbook-database:5WUDzpvBKBBiiMCy@cluster0.wtt64.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(uri))
if err != nil {
fmt.Println(err)
return
}
defer func() {
if err = client.Disconnect(context.TODO()); err != nil {
panic(err)
}
}()
// Ping the primary
if err := client.Ping(context.TODO(), readpref.Primary()); err != nil {
panic(err)
}
db := client.Database("guestbook")
coll := db.Collection("comments")
apiapp := &application{
client: client,
database: db,
collection: coll,
}
*/
http.Handle("/", handler)
//http.HandleFunc("/api/comment", apiapp.Comment)
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal(err)
}
}

View File

@ -1,30 +0,0 @@
package main
import (
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type AboutPage struct {
app.Compo
}
func NewAboutPage() *AboutPage {
return &AboutPage{}
}
func (a *AboutPage) Render() app.UI {
return newPage().
Title("About me").
LeftBar(
newHTMLBlock().
Class("left").
Class("leftbarblock").
Src("/web/blocks/snippets/bannerpanel.html"),
).
Main(
newHTMLBlock().
Class("right").
Class("contentblock").
Src("/web/blocks/pages/about.html"),
)
}

View File

@ -1,86 +0,0 @@
package main
import (
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type htmlBlock struct {
app.Compo
Iclass string
Isrc string // HTML document source
Iid string
// TODO: implement invisibility for other background functions
}
func newHTMLBlock() *htmlBlock {
return &htmlBlock{}
}
func (b *htmlBlock) ID(v string) *htmlBlock {
b.Iid = v
return b
}
func (b *htmlBlock) Class(v string) *htmlBlock {
b.Iclass = app.AppendClass(b.Iclass, v)
return b
}
func (b *htmlBlock) Src(v string) *htmlBlock {
b.Isrc = v
return b
}
func (b *htmlBlock) Render() app.UI {
return app.Div().
Class("block").
Class(b.Iclass).
Body(
newRemoteHTMLDoc().
Src(b.Isrc),
)
}
// ==================
// UI element block
// ==================
type uiBlock struct {
app.Compo
Iclass string
Iui []app.UI
Iid string
}
func newUIBlock() *uiBlock {
return &uiBlock{}
}
func (b *uiBlock) ID(v string) *uiBlock {
b.Iid = v
return b
}
func (b *uiBlock) Class(v string) *uiBlock {
b.Iclass = app.AppendClass(b.Iclass, v)
return b
}
func (b *uiBlock) UI(v ...app.UI) *uiBlock {
b.Iui = app.FilterUIElems(v...)
return b
}
func (b *uiBlock) Render() app.UI {
return app.Div().
Class("block").
Class(b.Iclass).
Body(
app.Range(b.Iui).Slice(func(i int) app.UI {
return b.Iui[i]
}),
)
}

View File

@ -1,74 +0,0 @@
package main
import (
"encoding/json"
"io"
"net/http"
"dutchellie.nl/DutchEllie/proper-website-2/entity"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type blogLinks struct {
app.Compo
blogposts []entity.BlogPost
}
func (b *blogLinks) OnMount(ctx app.Context) {
b.LoadPosts(ctx)
}
func (b *blogLinks) Render() app.UI {
return newUIBlock().
Class("left").
Class("leftbarblock").
Class("blogpost-bar").
UI(
app.P().
Class("p-h3").
Style("margin-left", "10px").
Style("margin-top", "10px").
Style("text-decoration", "underline").
Text("Posts"),
app.Range(b.blogposts).Slice(func(i int) app.UI {
return app.P().
Class("blogpost-titles").
Style("cursor", "pointer").
Text(b.blogposts[i].Title).
OnClick(func(ctx app.Context, e app.Event) {
//e.PreventDefault()
// Calling the update-blogpost action defined in the blogpage.go file.
// There it's updated
ctx.NewAction("update-blogpost", app.T("blogpost", b.blogposts[i].Path))
})
}),
)
}
func (b *blogLinks) LoadPosts(ctx app.Context) {
// TODO: maybe you can put this in a localbrowser storage?
url := ApiURL + "/blogpost"
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, &b.blogposts)
if err != nil {
app.Log(err)
return
}
})
})
}

View File

@ -1,54 +0,0 @@
package main
import (
"fmt"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type BlogPage struct {
app.Compo
display bool
currentPostPath string
}
// TODO: write the backend API for this
// TODO: Find a proper way of rendering blog posts in markdown
// Backend ideas: In the DB, create an entry for each post and a link to where the html file is located!
// That way, I don't have to parse and render markdown!!
// Layout, the leftbar contains the blogpost links and the mainbar contains the post itself!
// Function: After pressing a link for a blog post, that blog post ID gets put in the state instead of the URL
func NewBlogPage() *BlogPage {
return &BlogPage{}
}
func (b *BlogPage) OnMount(ctx app.Context) {
ctx.Handle("update-blogpost", b.onUpdateBlogPost)
b.display = false
}
func (b *BlogPage) Render() app.UI {
return newPage().
Title("Blog").
LeftBar(
&blogLinks{},
).
Main(
app.If(b.display,
newHTMLBlock().
Class("right").
Class("contentblock").
Src(b.currentPostPath),
),
)
}
func (b *BlogPage) onUpdateBlogPost(ctx app.Context, a app.Action) {
fmt.Printf("Called the update-blogpost ActionHandler\n")
blogpost := a.Tags.Get("blogpost")
b.currentPostPath = blogpost
b.display = true
}

View File

@ -1,15 +0,0 @@
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type EmptyPage struct {
app.Compo
}
func NewEmptyPage() *EmptyPage {
return &EmptyPage{}
}
func (e *EmptyPage) Render() app.UI {
return app.Head().Body()
}

View File

@ -1,137 +0,0 @@
package main
import (
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type GalaxiesPage struct {
app.Compo
gnuOver bool
mousex, mousey int
}
func NewGalaxiesPage() *GalaxiesPage {
return &GalaxiesPage{}
}
func (f *GalaxiesPage) Render() app.UI {
return newPage().
Title("Galaxies").
LeftBar(
newHTMLBlock().
Class("left").
Class("leftbarblock").
Src("/web/blocks/snippets/bannerpanel.html"),
).
Main(
/*
newHTMLBlock().
Class("right").
Class("contentblock").
Src("/web/blocks/pages/galaxies.html"),
*/
newUIBlock().
Class("right").
Class("contentblock").
UI(
app.Div().
Body(
app.P().
Class("p-h1").
Text("Galaxies"),
app.P().
Class("content-text").
Text(`Here you can find some really really really cool pages that I found on the internet.
Some of these are blogs or even blogposts I found, but the ones on top are special!
They're the websites of friends of mine! Please visit them, because they worked really hard
on their websites as well!`),
app.Div().
Body(
app.P().
Class("p-h2").
Class("mt-20").
Class("mb-10").
Class("bold").
Text("My friends!"),
app.Ul().
Body(
app.Li().
Body(
newLinkWithStatus().
Link("https://forestofunix.xyz").
LinkText("Forest of Unix").
Text("A website made by Sebastiaan. A massive Linux fanboy, runs Gentoo on his ThinkPad. Absolutely based. His website is written in Lisp, that's why it's often offline. That was the inspiration for the online/offline status text."),
),
app.Li().
Body(
newLinkWithStatus().
Link("https://nymphali.neocities.org").
LinkText("Nymphali").
Text("The website made by ■■■■■■, whoops Nymphali. They have an awesome minimalist website that's just lovely."),
),
app.Li().
Body(
newLinkWithStatus().
Link("https://kristypixel.neocities.org").
LinkText("Kristypixel").
Text("Website made by Kristy. Very cute website, I love it! Keep up the awesome work!"),
),
app.Li().
Body(
newLinkWithStatus().
Link("https://leftonred.neocities.org").
LinkText("Left on Red").
Text("Kyu made this website, he's a friend of mine as well! Still very new, but their dark mode design is very cool!"),
),
),
),
app.Div().
Body(
app.P().
Class("p-h2").
Class("mt-20").
Class("mb-10").
Class("bold").
Text("Neat webspaces"),
app.P().
Class("m-t5").
Style("margin-left", "10px").
Text("Just very neat websites I found and causes I support. Not necessarily by people I know. I just wanted to share them here!"),
app.Ul().
Body(
app.Li().
Body(
newLinkWithStatus().
Link("https://evillious.ylimegirl.com").
LinkText("Evillious Chronicles fan guide").
Text("A VERY cool website made by Ylimegirl! They wrote a whole website dedicated to Evillious Chronicles, which is a super good Japanese light novel and vocaloid series!! Definitely look it up!"),
),
app.Li().
Body(
newLinkWithStatus().
Link("https://www.gnu.org").
LinkBody(
newTextWithTooltip().
Text("The GNU Project").
Tooltip(
app.Img().
Src("/web/static/images/gnu-head-sm.png").
Width(129).
Height(122).
Alt("GNU"),
),
).
Text("The official website of the GNU project. They advocate for free/libre software. This is not to be confused with 'open source' software. I highly recommend you read about them and their efforts."),
),
),
),
),
),
)
}
/*
func (f *GalaxiesPage) onMouseOverGnu(ctx app.Context, e app.Event) {
}*/

View File

@ -1,387 +0,0 @@
package main
import (
"bytes"
"crypto/sha256"
"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
lastHash [32]byte
gbModalOpen bool
gbErrorModalOpen bool
errorText string
OnSubmit func(
ctx app.Context,
name string,
email string,
website string,
message string,
uuid string,
) // Handler to implement which calls the api
}
// TODO: The comments are loaded like 2 or 3 times every time the page is loaded...
func (g *guestbook) OnMount(ctx app.Context) {
ctx.Handle("guestbook-loadcomments", g.onHandleLoadComments)
ctx.NewAction("guestbook-loadcomments")
}
/*
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
fmt.Println("Send clicked")
e.PreventDefault()
if g.name == "" || g.message == "" {
fmt.Printf("Error: one or more field(s) are empty. For now unhandled\n")
g.gbErrorModalOpen = true
g.errorText = "One or more field(s) are empty. Fix that shit man."
return
}
fmt.Println("Nothing too empty")
if len(g.name) > 40 || len(g.message) > 360 {
fmt.Printf("Error: Your message is too long fucker\n")
g.gbModalOpen = true
return
}
fmt.Println("Got through validity")
var uuid string = ""
// c := client.Jar.Cookies(app.Window().URL())
// fmt.Println("Getting cookies")
// for _, c2 := range c {
// if c2.Name == "spyware" {
// uuid = c2.Value
// fmt.Println("Found spyware cookie")
// }
// }
// // Check if uuid is set, if it's not, then clearly the cookie does not exist
// if uuid == "" {
// uuid = "undetermined"
// g.errorText = "Failed sending message, couldn't insert UUID"
// g.gbErrorModalOpen = true
// return
// }
g.OnSubmit(ctx, g.name, g.email, g.website, g.message, uuid)
g.clear()
ctx.NewAction("guestbook-loadcomments")
//g.LoadComments(ctx)
}),
app.If(
g.gbModalOpen,
NewGuestbookAlertModal().
OnClose(func() {
g.gbModalOpen = false
g.Update()
}).
Text("Your name must be <= 40 and your message must be <= 360 characters"),
),
app.If(
g.gbErrorModalOpen,
NewGuestbookAlertModal().
OnClose(func() {
g.gbErrorModalOpen = false
g.Update()
}).
Text(fmt.Sprintf("Error placing comment: %s", g.errorText)),
),
app.Div().Body(
app.Range(g.comments).Slice(func(i int) app.UI {
return &guestbookComment{
Comment: g.comments[i],
}
},
),
),
)
}
func (g *guestbook) SmartLoadComments(ctx app.Context) {
{
// Get the comments quickly to at least render something
tmpjsondata := make([]byte, 0)
err := ctx.LocalStorage().Get("comments", &tmpjsondata)
if err != nil {
app.Log(err)
return
}
ctx.Dispatch(func(ctx app.Context) {
err = json.Unmarshal(tmpjsondata, &g.comments)
if err != nil {
app.Log(err)
return
}
})
g.Update()
}
var lasthash []byte
err := ctx.LocalStorage().Get("lasthash", &lasthash)
if err != nil {
app.Log(err)
return
}
if lasthash == nil {
fmt.Printf("Program thinks lasthash is empty\n")
g.LoadComments(ctx)
return
}
url := ApiURL + "/commenthash"
ctx.Async(func() {
res, err := http.Get(url)
if err != nil {
app.Log(err)
return
}
defer res.Body.Close()
hash, err := io.ReadAll(res.Body)
if err != nil {
app.Log(err)
return
}
//fmt.Printf("hash: %v\n", hash)
//fmt.Printf("lasthash: %v\n", lasthash)
// If the hash is different, aka there was an update in the comments
if !bytes.Equal(hash, lasthash) {
fmt.Printf("Hash calculation is different\n")
g.LoadComments(ctx)
return
}
// if the hash is not different, then there is no need to load new comments
jsondata := make([]byte, 0)
err = ctx.LocalStorage().Get("comments", &jsondata)
if err != nil {
app.Log(err)
return
}
//fmt.Printf("jsondata: %v\n", jsondata)
ctx.Dispatch(func(ctx app.Context) {
err = json.Unmarshal(jsondata, &g.comments)
if err != nil {
app.Log(err)
return
}
})
return
})
}
func (g *guestbook) LoadComments(ctx app.Context) {
// TODO: maybe you can put this in a localbrowser storage?
//fmt.Printf("Called LoadComments()\n")
url := ApiURL + "/comment"
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
}
})
ctx.LocalStorage().Set("comments", jsondata)
// Calculating the hash
//fmt.Printf("Calculating the hash from LoadComments\n")
hash := sha256.Sum256(jsondata)
//fmt.Printf("hash fresh from calculation: %v\n", hash)
//g.lastHash = hash
err = ctx.LocalStorage().Set("lasthash", []byte(fmt.Sprintf("%x\n", hash)))
if err != nil {
app.Log(err)
return
}
})
}
func (g *guestbook) clear() {
g.name = ""
g.message = ""
g.website = ""
g.email = ""
}
func (g *guestbook) onHandleLoadComments(ctx app.Context, a app.Action) {
g.SmartLoadComments(ctx)
g.Update()
}
type guestbookAlertModal struct {
app.Compo
PreviousAttempts int
IOnClose func() // For when we close the modal
IText string
}
func NewGuestbookAlertModal() *guestbookAlertModal {
return &guestbookAlertModal{}
}
func (g *guestbookAlertModal) OnClose(v func()) *guestbookAlertModal {
g.IOnClose = v
return g
}
func (g *guestbookAlertModal) Text(v string) *guestbookAlertModal {
g.IText = v
return g
}
func (g *guestbookAlertModal) Render() app.UI {
return app.Div().
Class("gb-modal").
ID("gbModal").
OnClick(func(ctx app.Context, e app.Event) {
g.IOnClose()
}).
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.IOnClose()
}),
app.P().Text(g.IText),
),
)
}
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")
}

View File

@ -1,16 +0,0 @@
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type header struct {
app.Compo
}
func (h *header) Render() app.UI {
return app.Div().
Class("header").
Body(
app.Text("Internetica Galactica"),
//&updater{},
)
}

View File

@ -1,77 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"dutchellie.nl/DutchEllie/proper-website-2/entity"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
var (
ApiURL string
)
type Homepage struct {
app.Compo
}
func NewHomepage() *Homepage {
return &Homepage{}
}
func (p *Homepage) Render() app.UI {
return newPage().
Title("Homepage").
LeftBar(
newHTMLBlock().
Class("left").
Class("leftbarblock").
Src("/web/blocks/snippets/bannerpanel.html"),
).
Main(
newHTMLBlock().
Class("right").
Class("contentblock").
Src("/web/blocks/pages/intro.html"),
newUIBlock().
Class("right").
Class("contentblock").
UI(
&guestbook{
OnSubmit: func(ctx app.Context, name, email, website, message, uuid string) {
var comment entity.Comment
comment.Name = name
comment.Email = email
comment.Website = website
comment.Message = message
comment.UUID = uuid
jsondata, err := json.Marshal(comment)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
url := ApiURL + "/comment"
// This is not Async'ed, because otherwise you run into a race
// condition where you reload the comments before the server had time
// to process the request!
{
req, err := http.Post(url, "application/json", bytes.NewBuffer(jsondata))
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
if req.StatusCode == 200 {
p.Update()
}
defer req.Body.Close()
}
},
},
),
)
}

View File

@ -1,75 +0,0 @@
package main
import (
"fmt"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type htmlDoc struct {
app.Compo
Ihtml string
}
func newHTMLDoc() *htmlDoc {
return &htmlDoc{}
}
func (h *htmlDoc) HTML(v string) *htmlDoc {
h.Ihtml = fmt.Sprintf("<div>%s</div>", v)
return h
}
func (h *htmlDoc) Render() app.UI {
return app.Raw(h.Ihtml)
}
type remoteHTMLDoc struct {
app.Compo
Isrc string
html htmlContent
}
func newRemoteHTMLDoc() *remoteHTMLDoc {
return &remoteHTMLDoc{}
}
func (h *remoteHTMLDoc) Src(v string) *remoteHTMLDoc {
h.Isrc = v
return h
}
func (h *remoteHTMLDoc) OnMount(ctx app.Context) {
h.load(ctx)
}
func (h *remoteHTMLDoc) OnNav(ctx app.Context) {
h.load(ctx)
}
func (h *remoteHTMLDoc) load(ctx app.Context) {
src := h.Isrc
ctx.ObserveState(htmlState(src)).
While(func() bool {
return src == h.Isrc
}).
OnChange(func() {
}).
Value(&h.html)
ctx.NewAction(getHTML, app.T("path", h.Isrc))
}
func (h *remoteHTMLDoc) Render() app.UI {
return app.Div().
Body(
app.If(h.html.Status == loaded,
newHTMLDoc().
HTML(h.html.Data),
).Else(),
)
}

View File

@ -1,63 +0,0 @@
package main
import (
"errors"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
const (
getHTML = "/html/get"
)
func handleGetHTML(ctx app.Context, a app.Action) {
path := a.Tags.Get("path")
if path == "" {
app.Log(errors.New("getting html failed"))
return
}
state := htmlState(path)
var ht htmlContent
ctx.GetState(state, &ht)
switch ht.Status {
case loading, loaded:
return
}
ht.Status = loading
ht.Error = nil
ctx.SetState(state, ht)
res, err := get(ctx, path)
if err != nil {
ht.Status = loadingErr
ht.Error = err
ctx.SetState(state, ht)
return
}
ht.Status = loaded
ht.Data = string(res)
ctx.SetState(state, ht)
}
func htmlState(src string) string {
return src
}
type htmlContent struct {
Status status
Error error
Data string
}
type status int
const (
neverLoaded status = iota
loading
loadingErr
loaded
)

View File

@ -1,41 +0,0 @@
package main
import (
"fmt"
"io"
"net/http"
"strings"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
func get(ctx app.Context, path string) ([]byte, error) {
url := path
if !strings.HasPrefix(url, "http") {
u := ctx.Page().URL()
u.Path = path
url = u.String()
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
fmt.Printf("Error at getting html page\n")
return nil, err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
// Which means either client or server error
if res.StatusCode >= 400 {
return nil, err
}
b, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
return b, nil
}

View File

@ -1,129 +0,0 @@
package main
import (
"compress/gzip"
"log"
"net/http"
"net/http/cookiejar"
"os"
"dutchellie.nl/DutchEllie/proper-website-2/api"
"github.com/gorilla/handlers"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
//type application struct {
// client *mongo.Client
// database *mongo.Database
// collection *mongo.Collection
//}
var jar http.CookieJar
var client http.Client
func main() {
// Create cookiejar
var err error
jar, err = cookiejar.New(nil)
if err != nil {
log.Fatalf("Error creating cookiejar: %s\n", err.Error())
}
client = http.Client{
Jar: jar,
}
homepage := NewHomepage()
aboutpage := NewAboutPage()
galaxiespage := NewGalaxiesPage()
undertalePage := NewUndertalePage()
musicPage := NewMusicPage()
app.Route("/", homepage)
app.Route("/about", aboutpage)
app.Route("/galaxies", galaxiespage)
app.Route("/undertale", undertalePage)
app.Route("/blog", NewBlogPage())
app.Route("/music", musicPage)
app.Handle(getHTML, handleGetHTML)
// This is executed on the client side only.
// It handles client side stuff
// It exits immediately on the server side
app.RunWhenOnBrowser()
icon := &app.Icon{
Default: "/web/static/images/icon-small.png",
Large: "/web/static/images/icon.png",
}
handler := &app.Handler{
Name: "Internetica Galactica",
Icon: *icon,
BackgroundColor: "#362730",
ThemeColor: "#362730",
LoadingLabel: "Internetica Galactica",
Title: "Internetica Galactica",
Description: "A 1990's style PWA!",
Author: "Quenten",
Keywords: []string{
"Based website",
"Cool website",
"PWA",
"Programming",
"Go", "Golang",
"Webassembly", "WASM",
"DutchEllie", "Quenten",
},
Styles: []string{
"/web/static/style.css",
"/web/static/adreena.css",
"/web/static/anisha.css",
"/web/static/havakana.css",
"/web/static/form.css",
},
CacheableResources: []string{
// Images
"/web/static/images/email3.gif",
"/web/static/images/rin-len1.webp",
"/web/static/images/background_star.gif",
"/web/static/images/kanata-1.gif",
"/web/static/images/rin-1.gif",
"/web/static/images/rin-2.gif",
"/web/static/images/gnu-head-sm.png",
// Pages
"/web/blocks/pages/about.html",
"/web/blocks/pages/intro.html",
"/web/blocks/snippets/bannerpanel.html",
// Music
"https://music-website.s3.nl-ams.scw.cloud/Tokusya-Seizon%20Wonder-la-der%21%21.mp3",
"https://music-website.s3.nl-ams.scw.cloud/kegarenaki-barajuuji.mp3",
"https://music-website.s3.nl-ams.scw.cloud/error-towa.mp3",
"https://music-website.s3.nl-ams.scw.cloud/diamond-city-lights-lazulight.opus",
"https://music-website.s3.nl-ams.scw.cloud/tsunami-finana.opus",
"https://music-website.s3.nl-ams.scw.cloud/%E7%A5%9E%E3%81%A3%E3%81%BD%E3%81%84%E3%81%AA.m4a",
"https://music-website.s3.nl-ams.scw.cloud/Servant%20of%20Evil%20with%20English%20Sub%20-%20%E6%82%AA%E3%83%8E%E5%8F%AC%E4%BD%BF%20-%20Kagamine%20Len%20-%20HQ.m4a",
"https://music-website.s3.nl-ams.scw.cloud/%E3%83%94%E3%83%8E%E3%82%AD%E3%82%AA%E3%83%94%E3%83%BC%20-%20%E3%81%8D%E3%81%BF%E3%82%82%E6%82%AA%E3%81%84%E4%BA%BA%E3%81%A7%E3%82%88%E3%81%8B%E3%81%A3%E3%81%9F%20feat.%20%E5%88%9D%E9%9F%B3%E3%83%9F%E3%82%AF%20_%20I%27m%20glad%20you%27re%20evil%20too.m4a",
},
}
app.GenerateStaticWebsite("./staticsite", handler)
compressed := handlers.CompressHandlerLevel(handler, gzip.BestSpeed)
// Create spyware module
spywareapi, err := api.NewApiApp()
if err != nil {
log.Fatal(err)
}
http.Handle("/", compressed)
http.HandleFunc("/api/visit", spywareapi.Visit)
// router.HandleFunc("/api/visit", spywareapi.Visit)
if os.Getenv("GEN_STATIC_SITE") == "true" {
return
}
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal(err)
}
}

View File

@ -1,110 +0,0 @@
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type menu struct {
app.Compo
Iclass string
updateAvailable bool
IpaneWidth int
OnClickButton func(page string)
}
func newMenu() *menu {
return &menu{}
}
func (m *menu) PaneWidth(px int) *menu {
if px > 0 {
m.IpaneWidth = px
}
return m
}
func (m *menu) Class(v string) *menu {
m.Iclass = app.AppendClass(m.Iclass, v)
return m
}
func (m *menu) OnAppUpdate(ctx app.Context) {
m.updateAvailable = ctx.AppUpdateAvailable()
}
func (m *menu) Render() app.UI {
return app.Div().
Class("block").
// Class("leftbarblock-nop").
Class("navbar").
Body(
app.Ul().Body(
newMenuLink().
Link("/").
Text("Home"),
newMenuLink().
Link("/about").
Text("About"),
newMenuLink().
Link("/galaxies").
Text("Galaxies"),
newMenuLink().
Link("/music").
Text("Music"),
// Disabled for now since there are none anyway
app.Li().
Body(
app.A().Href("/blog").Text("Blog"),
).Style("display", "none"),
),
app.If(m.updateAvailable,
app.Div().Body(
app.Img().
Src("/web/static/images/hot1.gif").
Class("update-img"),
app.Span().
Text("Update available! Click here to update!").
Class("update-text"),
).
Class("update-div").
OnClick(m.onUpdateClick),
),
)
}
func (m *menu) onUpdateClick(ctx app.Context, e app.Event) {
ctx.Reload()
}
type menuLink struct {
app.Compo
IText string
ILink string
}
func newMenuLink() *menuLink {
return &menuLink{}
}
func (m *menuLink) Text(v string) *menuLink {
m.IText = v
return m
}
func (m *menuLink) Link(v string) *menuLink {
m.ILink = v
return m
}
func (m *menuLink) Render() app.UI {
return app.A().
Class("menuitem-link").
Href(m.ILink).
Body(app.Div().
Class("menuitem").
Body(app.Span().
Class("menuitem-text").
Text(m.IText)),
)
}

View File

@ -1,213 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
//#################################
//## ###
//## Link with status component ###
//## ###
//#################################
type linkWithStatus struct {
app.Compo
Ilink string
IText string
ILinkText string
ILinkBody app.UI
status bool
}
func newLinkWithStatus() *linkWithStatus {
return &linkWithStatus{}
}
func (f *linkWithStatus) Link(s string) *linkWithStatus {
f.Ilink = s
return f
}
func (f *linkWithStatus) LinkBody(v app.UI) *linkWithStatus {
f.ILinkBody = v
return f
}
func (f *linkWithStatus) LinkText(s string) *linkWithStatus {
return f.LinkBody(app.Text(s))
}
func (f *linkWithStatus) Text(s string) *linkWithStatus {
f.IText = s
return f
}
func (f *linkWithStatus) checkStatus(ctx app.Context) {
ctx.Async(func() {
data := struct {
Url string `json:"url"`
}{
Url: f.Ilink,
}
jsondata, err := json.Marshal(data)
if err != nil {
app.Log(err)
return
}
req, err := http.NewRequest("POST", ApiURL+"/checkonline", bytes.NewBuffer(jsondata))
if err != nil {
app.Log(err)
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
app.Log(err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
app.Log(err)
return
}
jsonresp := struct {
Status bool `json:"status"`
}{}
err = json.Unmarshal(body, &jsonresp)
if err != nil {
app.Log(err)
return
}
ctx.Dispatch(func(ctx app.Context) {
f.status = jsonresp.Status
})
})
}
func (f *linkWithStatus) OnNav(ctx app.Context) {
f.checkStatus(ctx)
}
func (f *linkWithStatus) Render() app.UI {
return app.Div().
Body(
app.Div().
Style("display", "flex").
Style("gap", "20px").
Body(
app.A().
Href(f.Ilink).
Class("p-h3").
Class("m-t5").
Body(f.ILinkBody),
app.If(f.status,
app.P().
Style("color", "green").
Style("width", "fit-content").
Style("margin", "5px 0px 0px 0px").
Text("Online"),
).Else(
app.P().
Style("color", "red").
Style("width", "fit-content").
Style("margin", "5px 0px 0px 0px").
Text("Offline"),
),
),
app.P().
Class("m-t5").
Text(f.IText),
)
}
//#################################
//## ###
//## Text with tooltip ###
//## ###
//#################################
type textWithTooltip struct {
app.Compo
ITooltip app.UI
IClass string
ITextClass string
IText string
activated bool
mousex, mousey int
}
func newTextWithTooltip() *textWithTooltip {
return &textWithTooltip{}
}
func (f *textWithTooltip) Class(v string) *textWithTooltip {
f.IClass = app.AppendClass(f.IClass, v)
return f
}
func (f *textWithTooltip) TextClass(v string) *textWithTooltip {
f.ITextClass = app.AppendClass(f.ITextClass, v)
return f
}
func (f *textWithTooltip) Text(v string) *textWithTooltip {
f.IText = v
return f
}
func (f *textWithTooltip) Tooltip(v app.UI) *textWithTooltip {
f.ITooltip = v
return f
}
func (f *textWithTooltip) Render() app.UI {
return app.Div().
Class(f.IClass).
Body(
app.Span().
Class(f.ITextClass).
Text(f.IText).
OnMouseOver(func(ctx app.Context, e app.Event) {
f.activated = true
}).
OnMouseMove(func(ctx app.Context, e app.Event) {
f.mousex, f.mousey = app.Window().CursorPosition()
}).
OnMouseOut(func(ctx app.Context, e app.Event) {
f.activated = false
}),
app.If(f.activated,
app.Div().
Style("position", "fixed").
Style("overflow", "hidden").
Style("top", fmt.Sprintf("%dpx", f.mousey+20)).
Style("left", fmt.Sprintf("%dpx", f.mousex+20)).
Body(
f.ITooltip,
),
),
)
}
//#################################
//## ###
//## Tooltip ###
//## ###
//#################################
type toolTip struct {
app.Compo
}

View File

@ -1,51 +0,0 @@
// Idea for this page:
// - Make a navbar on the top for different genres and switch the pages content when clicked
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type MusicPage struct {
app.Compo
}
func NewMusicPage() *MusicPage {
return &MusicPage{}
}
func (f *MusicPage) Render() app.UI {
return newPage().
Title("Music!").
LeftBar(
newHTMLBlock().
Class("left").
Class("leftbarblock").
Src("/web/blocks/snippets/bannerpanel.html"),
).
Main(
// Genre navbar above this
newUIBlock().
Class("right").
Class("contentblock").
UI(
app.Div().
Body(
app.P().
Class("m-t5").
Text(`I am quite picky with my music most of the time. I rarely enjoy an entire album of an artist and most artists for me have only a couple amazing songs.
My tastes in music are almost exclusively Japanese songs. Vocaloid is how I began and nowadays I listen to all sorts of Japanese music.
Here are some of the songs, artists and albums I like the most.`),
app.P().
Class("p-h3").
Style("color", "red").
Text("Warning! Player feature still in beta. Stuff can break and design is most certainly not final at all!"),
app.P().
Text("Just click one of the songs to play it."),
app.P().
Class("p-h2").
Text("Songs"),
newMusicPlayer(),
),
),
)
}

View File

@ -1,165 +0,0 @@
package main
import (
"fmt"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
const (
songRepoURL string = "" // URL where the music files are stored. Made a bit like the ApiURL
)
type song struct {
ITitle string
IID string
IURL string
}
func newSong() *song {
return &song{}
}
func (f *song) Title(v string) *song {
f.ITitle = v
return f
}
func (f *song) URL(v string) *song {
f.IURL = v
return f
}
func (f *song) ID(v string) *song {
f.IID = v
return f
}
type musicPlayer struct {
app.Compo
songs map[string](*song)
currentlySelectedSong string
}
func newMusicPlayer() *musicPlayer {
return &musicPlayer{
songs: make(map[string]*song),
}
}
// On... handlers
func (f *musicPlayer) OnMount(ctx app.Context) {
ctx.Handle("switchSong", f.handleSwitchSong)
// Statically create all the music.
// I am not making a database for this shit lmao
// Also do not forget to at least set the currentlySelectedSong to the first one added, or at least make it point somewhere
// f.songs["ievan-polka"] = newSong().
// Title("Ievan Polka - Hatsune Miku").
// URL("https://files.catbox.moe/lh229f.mp3").
// ID("ievan-polka")
f.songs["god-ish"] = newSong().
Title("God-ish (神っぽいな) feat. Hatsune Miku - PinocchioP").
URL("https://music-website.s3.nl-ams.scw.cloud/%E7%A5%9E%E3%81%A3%E3%81%BD%E3%81%84%E3%81%AA.m4a").
ID("god-ish")
f.songs["servant-of-evil"] = newSong().
Title("Servant of Evil (悪ノ召使) feat. Kagamine Rin - mothy / AkunoP").
URL("https://music-website.s3.nl-ams.scw.cloud/Servant%20of%20Evil%20with%20English%20Sub%20-%20%E6%82%AA%E3%83%8E%E5%8F%AC%E4%BD%BF%20-%20Kagamine%20Len%20-%20HQ.m4a").
ID("servant-of-evil")
f.songs["im-glad-youre-evil-too"] = newSong().
Title("I'm glad you're evil too (feat. Hatsune Miku) - PinocchioP").
URL("https://music-website.s3.nl-ams.scw.cloud/%E3%83%94%E3%83%8E%E3%82%AD%E3%82%AA%E3%83%94%E3%83%BC%20-%20%E3%81%8D%E3%81%BF%E3%82%82%E6%82%AA%E3%81%84%E4%BA%BA%E3%81%A7%E3%82%88%E3%81%8B%E3%81%A3%E3%81%9F%20feat.%20%E5%88%9D%E9%9F%B3%E3%83%9F%E3%82%AF%20_%20I%27m%20glad%20you%27re%20evil%20too.m4a").
ID("im-glad-youre-evil-too")
f.songs["tokusya-seizon"] = newSong().
Title("Tokusya-Seizon Wonder-la-der!! - Amane Kanata").
URL("https://music-website.s3.nl-ams.scw.cloud/Tokusya-Seizon%20Wonder-la-der%21%21.mp3").
ID("tokusya-seizon")
f.songs["kegarenaki-barajuuji"] = newSong().
Title("Kegarenaki Barajuuji - Ariabl'eyeS").
URL("https://music-website.s3.nl-ams.scw.cloud/kegarenaki-barajuuji.mp3").
ID("kegarenaki-barajuuji")
f.songs["error-towa"] = newSong().
Title("-ERROR (Cover) - Tokoyami Towa").
URL("https://music-website.s3.nl-ams.scw.cloud/error-towa.mp3").
ID("error-towa")
f.songs["diamond-city-lights"] = newSong().
Title("Diamond City Lights - LazuLight").
URL("https://music-website.s3.nl-ams.scw.cloud/diamond-city-lights-lazulight.opus").
ID("diamond-city-lights")
f.songs["tsunami-finana"] = newSong().
Title("TSUNAMI - Finana Ryugu").
URL("https://music-website.s3.nl-ams.scw.cloud/tsunami-finana.opus").
ID("tsunami-finana")
}
// Action handlers
// Call with a value called "title" to switch to the right song
func (f *musicPlayer) handleSwitchSong(ctx app.Context, a app.Action) {
title, ok := a.Value.(string)
if !ok {
app.Log("Error calling handleSwitchSong function. Title value was not found")
return
}
v, ok := f.songs[title]
if !ok {
app.Log("Error getting song. Song with title does not exist")
return
}
f.currentlySelectedSong = v.IID
f.Update()
}
func (f *musicPlayer) Render() app.UI {
// Don't forget to handle the possibility of no songs having been added and the currentlySelectedSong to be empty
if f.currentlySelectedSong == "" {
return app.Div().
Body(
app.Range(f.songs).Map(func(s string) app.UI {
return app.Div().
Style("border", "solid 1px red").
Style("width", "fit-content").
Body(
app.Span().
Style("text-decoration", "underline").
Style("width", "fit-content").
Class("finger-hover").
Text(f.songs[s].ITitle).
OnClick(func(ctx app.Context, e app.Event) {
ctx.NewActionWithValue("switchSong", f.songs[s].IID)
}),
)
}),
)
}
return app.Div().Body(
app.P().
Class("p-h3").
Text(fmt.Sprintf("Currently playing: %s", f.songs[f.currentlySelectedSong].ITitle)),
app.Audio().
Src(f.songs[f.currentlySelectedSong].IURL).
Controls(true).
AutoPlay(true),
// Lots of buttons of songs
app.Div().
Body(
app.Range(f.songs).Map(func(s string) app.UI {
return app.Div().
Style("border", "solid 1px red").
Style("width", "fit-content").
Body(
app.Span().
Style("text-decoration", "underline").
Style("width", "fit-content").
Class("finger-hover").
Text(f.songs[s].ITitle).
OnClick(func(ctx app.Context, e app.Event) {
ctx.NewActionWithValue("switchSong", f.songs[s].IID)
}),
)
}),
),
)
}

View File

@ -1,32 +0,0 @@
package main
import (
"dutchellie.nl/DutchEllie/proper-website-2/ui"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type navbar struct {
app.Compo
updateAvailable bool
OnClickButton func(page string)
}
func (n *navbar) OnAppUpdate(ctx app.Context) {
n.updateAvailable = ctx.AppUpdateAvailable()
}
func (n *navbar) Render() app.UI {
return ui.Menu().
PaneWidth(250).
Menu(
newMenu(),
).
HamburgerMenu(
newMenu(),
)
}
func (n *navbar) onUpdateClick(ctx app.Context, e app.Event) {
ctx.Reload()
}

View File

@ -1,127 +0,0 @@
package main
import (
"fmt"
"net/url"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
// Page is a generic page. By default it has a header, navbar and a default leftbar
type page struct {
app.Compo
Ititle string
/*Description
Blah blah
etc*/
IbackgroundClass string
Ibackground []app.UI
IleftBar []app.UI
Imain []app.UI
hideRightContent bool
// TODO: Possibly add "updateavailable" here, so it shows up on every page
}
func newPage() *page {
return &page{
hideRightContent: false,
}
}
func (p *page) Background(v ...app.UI) *page {
p.Ibackground = app.FilterUIElems(v...)
return p
}
func (p *page) BackgroundClass(t string) *page {
p.IbackgroundClass = app.AppendClass(p.IbackgroundClass, t)
return p
}
func (p *page) Title(t string) *page {
p.Ititle = t
return p
}
func (p *page) LeftBar(v ...app.UI) *page {
p.IleftBar = app.FilterUIElems(v...)
return p
}
func (p *page) Main(v ...app.UI) *page {
p.Imain = app.FilterUIElems(v...)
return p
}
func (p *page) OnMount(ctx app.Context) {
ctx.Handle("right-hide", p.hideRight)
ctx.Handle("right-show", p.showRight)
// Send a visit request to the spyware API to track people
ctx.Async(func() {
resp, err := client.Get("/api/visit")
if err != nil {
app.Logf("Error while creating vist request %s\n", err.Error())
}
defer resp.Body.Close()
cook, err := resp.Request.Cookie("spyware")
if err != nil {
app.Logf("Error reading cookie from request: %s\n", err)
}
fmt.Printf("cook: %v\n", cook)
ctx.Dispatch(func(ctx app.Context) {
fmt.Printf("jar: %v\n", jar)
c := client.Jar.Cookies(&url.URL{Host: app.Window().URL().Hostname()})
fmt.Printf("c: %v\n", c)
})
})
}
func (p *page) Render() app.UI {
if p.IbackgroundClass == "" {
p.IbackgroundClass = app.AppendClass(p.IbackgroundClass, "background")
}
return app.Div().
Class("main").
Body(
// Header and navbar
&header{},
app.Div().
Class("left").
Body(
&navbar{},
app.Range(p.IleftBar).Slice(func(i int) app.UI {
return p.IleftBar[i]
}),
),
app.Div().
Style("display", visible(p.hideRightContent)).
Class("right").
ID("right").
Body(
app.Range(p.Imain).Slice(func(i int) app.UI {
return p.Imain[i]
}),
),
)
}
func visible(v bool) string {
if v {
return "none"
}
return "block"
}
func (p *page) hideRight(ctx app.Context, a app.Action) {
p.hideRightContent = true
}
func (p *page) showRight(ctx app.Context, a app.Action) {
p.hideRightContent = false
}

View File

@ -1,32 +0,0 @@
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type UndertalePage struct {
app.Compo
}
// TODO: Autoplay Megalovania
func NewUndertalePage() *UndertalePage {
return &UndertalePage{}
}
func (u *UndertalePage) Render() app.UI {
return newPage().
Title("Undertale").
BackgroundClass("undertale-bg").
Background().
LeftBar(
newHTMLBlock().
Class("left").
Class("leftbarblock a").
Src("/web/blocks/snippets/bannerpanel.html"),
).
Main(
newHTMLBlock().
Class("right").
Class("contentblock").
Src("/web/blocks/pages/undertale.html"),
)
}

View File

@ -4,41 +4,20 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<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/form.css">
<link rel="stylesheet" href="static/anisha.css?family=anisha">
<link rel="stylesheet" href="static/adreena.css?family=adreena">
<link rel="stylesheet" href="static/havakana.css?family=havakana">
<link rel="stylesheet" href="static/adreena.css">
<link rel="stylesheet" href="static/anisha.css">
<script src="https://cdn.tailwindcss.com"></script>
<title>Index</title>
</head>
<body>
<div class="block right content">
<form action="" class="guestbook-form">
<div class="input-groups">
<div class="fr">
<div class="input-group input-group-name">
<label for="name">Name:</label>
<input type="text" name="name" class="input">
</div>
<div class="input-group input-group-email">
<label for="email">Email:</label>
<input type="text" name="email" class="input">
</div>
</div>
<div class="input-group input-group-website">
<label for="website">Website:</label>
<input type="text" name="website" class="input">
</div>
<div class="input-group input-group-message">
<label for="message">Message:</label>
<textarea name="message" cols="30" rows="5" class="input"></textarea>
</div>
</div>
<div class="submit-field">
<input type="submit" value="Send!">
</div>
</form>
<div class="w-[900px] ">
test
</div>
<div class="">
</div>
</body>
</html>

View File

@ -1,176 +0,0 @@
package ui
import (
"strconv"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type IMenu interface {
app.UI
ID(v string) IMenu
Class(v string) IMenu
PaneWidth(px int) IMenu
HamburgerButton(v app.UI) IMenu
HamburgerMenu(v ...app.UI) IMenu
Menu(v ...app.UI) IMenu
}
func Menu() IMenu {
return &menu{}
}
type menu struct {
app.Compo
Iid string
Iclass string
Ipanewidth int
IhamburgerButton app.UI
IhamburgerMenu []app.UI
Imenu []app.UI
hideMenu bool
showHamburgerMenu bool
width int
}
func (m *menu) ID(v string) IMenu {
m.Iid = v
return m
}
func (m *menu) Class(v string) IMenu {
m.Iclass = app.AppendClass(m.Iclass, v)
return m
}
func (m *menu) PaneWidth(px int) IMenu {
if px > 0 {
m.Ipanewidth = px
}
return m
}
func (m *menu) HamburgerButton(v app.UI) IMenu {
b := app.FilterUIElems(v)
if len(b) != 0 {
m.IhamburgerButton = b[0]
}
return m
}
func (m *menu) HamburgerMenu(v ...app.UI) IMenu {
m.IhamburgerMenu = app.FilterUIElems(v...)
return m
}
func (m *menu) Menu(v ...app.UI) IMenu {
m.Imenu = app.FilterUIElems(v...)
return m
}
func (m *menu) OnPreRender(ctx app.Context) {
m.refresh(ctx)
}
func (m *menu) OnMount(ctx app.Context) {
m.refresh(ctx)
}
func (m *menu) OnResize(ctx app.Context) {
m.refresh(ctx)
}
func (m *menu) OnUpdate(ctx app.Context) {
m.refresh(ctx)
}
func (m *menu) Render() app.UI {
visible := func(v bool) string {
if v {
return "block"
}
return "none"
}
return app.Div().
ID(m.Iid).
Class(m.Iclass).
Style("height", "100%").
Body(
app.Div().
//Style("display", "flex").
Style("display", visible(!m.hideMenu)).
Style("width", "100%").
Style("height", "100%").
Style("overflow", "hidden").
Body(
app.Div().
Style("position", "relative").
Style("display", visible(!m.hideMenu)).
Style("flex-shrink", "0").
Style("flex-basis", pxToString(m.Ipanewidth)).
Style("overflow", "hidden").
Body(m.Imenu...),
),
app.Div().
Style("display", visible(m.hideMenu && len(m.IhamburgerMenu) != 0)).
Style("position", "absolute").
Style("top", "0").
Style("left", "0").
Style("cursor", "pointer").
OnClick(m.onHamburgerButtonClick).
Body(
app.If(m.IhamburgerButton == nil,
app.Div().
Class("goapp-shell-hamburger-button-default").
Text("☰"),
),
),
app.Div().
Style("display", visible(m.hideMenu && m.showHamburgerMenu)).
Style("position", "relative").
// Style("top", "0").
// Style("left", "0").
// Style("right", "0").
Style("width", "100%").
Style("height", "100%").
Style("overflow", "hidden").
OnClick(m.hideHamburgerMenu).
Body(m.IhamburgerMenu...),
)
}
func (m *menu) refresh(ctx app.Context) {
w, _ := app.Window().Size()
hideMenu := true
if w >= 914 {
hideMenu = false
}
if hideMenu != m.hideMenu ||
w != m.width {
m.hideMenu = hideMenu
m.width = w
ctx.Defer(func(app.Context) {
m.ResizeContent()
})
}
}
func pxToString(px int) string {
return strconv.Itoa(px) + "px"
}
func (m *menu) onHamburgerButtonClick(ctx app.Context, e app.Event) {
m.showHamburgerMenu = true
ctx.NewAction("right-hide")
}
func (m *menu) hideHamburgerMenu(ctx app.Context, e app.Event) {
m.showHamburgerMenu = false
ctx.NewAction("right-show")
}

View File

@ -1,6 +0,0 @@
<p class="p-h1">Test blogpost</p>
<p>This is some text under the title</p>
<p class="p-h2">Header 2</p>
<p class="p-h3">Header 3</p>
<p>Regular text here</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Minus, omnis nesciunt beatae dolorem laudantium quidem? Eos fugiat doloribus, ipsam, suscipit repudiandae culpa laboriosam accusamus alias atque esse rerum, tenetur illum.</p>

View File

@ -1,22 +0,0 @@
<img src="/web/static/images/rin-1.gif" style="width:100px;position:absolute;top:10px;right:10px;">
<p class="content-text">I am a 21 year old computer science student (they/them, he/him, she/her), living and studying in
The Netherlands. I like Docker, Kubernetes and Golang!
<br>
I made this website because I was inspired again by the amazing Neocities pages that I discovered because of my
friends.
They also have their own pages (you can find them on the Galaxies page, do check them out!) and I just had to get a
good website of my own!
<br>
I am not that great at web development, especially design, but I love trying it regardless!
<br><br>
To say a bit more about me personally, I love all things computers. From servers to embedded devices! I love the
cloud and all that it brings
(except for big megacorps, but alright) and it's my goal to work for a big cloud company!
<br>
Aside from career path ambitions,ボーカロイドはすきです! I love vocaloid and other Japanese music and culture!!
I also like Vtubers, especially from Hololive and it's my goal to one day finally understand them in their native
language!
<br><br>
There is a lot more to say in words, but who cares about those! Have a look around my creative digital oasis and see
what crazy stuff you can find!
</p>

View File

@ -1,65 +0,0 @@
<p class="p-h1">
Galaxies
</p>
<p class="content-text">
Here you can find some really really really cool pages that I found on the internet.
Some of these are blogs or even blogposts I found, but the ones on top are special!
They're the websites of friends of mine! Please visit them, because they worked really hard
on their websites as well!
</p>
<div>
<p class="p-h2 mt-20 mb-10 bold">My friends!</p>
<ul>
<li>
<div><a href="https://forestofunix.xyz" class="p-h3 m-t5">Forest of Unix</a>
<p class="m-t5">A website by Sebastiaan. A massive Linux fanboy, runs Gentoo on his
ThinkPad. Absolutely based.</p>
</div>
</li>
<li>
<div><a href="https://nymphali.neocities.org" class="p-h3 m-t5">Nymphali</a>
<p class="m-t5">The website made by ■■■■■■, whoops Nymphali. They have an awesome
minimalist website that's just lovely.</p>
</div>
</li>
<li>
<div><a class="p-h3 m-t5" href="https://kristypixel.neocities.org">Kristy</a>
<p class="m-t5">Website made by Kristy. Very cute website, I love it! Keep up the
awesome work!</p>
</div>
</li>
</ul>
</div>
<div>
<p class="p-h2 mt-20 mb-10 bold">Neat webspaces</p>
<p class="m-t5" style="margin-left:10px;">Just very neat websites I found. Not necessarily by people I know.
I just thought it would be nice to share them here!</p>
<ul>
<li>
<div><a href="https://evillious.ylimegirl.com/" class="p-h3 m-t5">Evillious Chronicles fan guide</a>
<p class="m-t5">A VERY cool website made by Ylimegirl! They wrote a whole
website dedicated to Evillious Chronicles, which is a super
good Japanese light novel and vocaloid series!! Definitely look it up!</p>
</div>
</li>
<li>
<div>
<a href="https://www.gnu.org/" class="p-h3 m-t5">The GNU Project</a>
<div style="display:flex; gap:5px;">
<div style="flex:70%">
<p class="m-t5">
The official website of the GNU project.
They advocate for free/libre software.
This is not to be confused with 'open source' software.
I highly recommend you read about them and their efforts.
</p>
</div>
<div style="flex:30%">
<img src="/web/static/images/gnu-head-sm.png" alt="GNU" width="129" height="122" >
</div>
</div>
</div>
</li>
</ul>
</div>

View File

@ -1,28 +0,0 @@
<p class="p-h1">Welcome, internet surfer!</p>
<div style="position:absolute; top:10px; right:5px;">
<p class="small">Please sign my guestbook</p>
<img src="/web/static/images/email3.gif" alt="" style="width:40px; position:absolute; bottom:0px; right:0px;">
</div>
<img src="/web/static/images/rin-len1.webp" alt="" height="230" style="float:right; margin-bottom: 10px;">
<p class="content-text">
Welcome to my webspace! Whether you stumbled across this page by accident
or were linked here, you're more than welcome! This is my personal project that I like
to work on! I was inspired by a couple friends of mine, please do check their webspaces
out as well under "Galaxies" on the left side there!
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">on my personal Gitea instance</a> so if you like that
sort
of stuff, be my guest it's cool!
</p>
<br>
<p class="content-text">
<img src="/web/static/images/rin-2.gif" alt="Kagamine Rin drawing" style="float:left; width:100px; margin-right: 20px;">
There is a lot of stuff I want to add to this website! In fact, there is also a "staging" website, which might
contain
new features! It can be found at <a href="https://staging.quenten.nl">staging.quenten.nl</a>.
Don't worry about the invalid SSL certificate, that's normal!
</p>

View File

@ -1 +0,0 @@
<p>Test</p>

View File

@ -1,8 +0,0 @@
<div style="display: flex; gap:5px; padding:5px" >
<div style="flex:50%;">
<img src="/web/static/images/kanata-1.gif" alt="Amane Kanata excited!!" style="width:100%" >
</div>
<div style="flex:50%;">
<img src="/web/static/images/kanata-1.gif" alt="Amane Kanata excited!!" style="width:100%" >
</div>
</div>

11
web/static/custom.js Normal file
View File

@ -0,0 +1,11 @@
tailwind.config = {
theme: {
extend: {
colors: {
'cool': '#362730',
'borderpink': '#fce6ff',
'aliceblue': '#f0f8ff',
}
}
}
}

View File

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

Binary file not shown.

Before

(image error) Size: 1.6 KiB

Binary file not shown.

Before

(image error) Size: 384 B

Binary file not shown.

Before

(image error) Size: 1.4 MiB

Binary file not shown.

Before

(image error) Size: 1.1 MiB

Binary file not shown.

Before

(image error) Size: 192 KiB

Binary file not shown.

Before

(image error) Size: 45 KiB

4
web/static/newstyle.css Normal file
View File

@ -0,0 +1,4 @@
html {
background-image: url(images/background_star.gif);
overflow-y: scroll;
}

View File

@ -1,64 +1,9 @@
html {
/* overflow-y: scroll; */
margin: 0;
height: 99%;
background-image: url(images/background_star.gif);
overflow-y: scroll;
}
body {
margin: 0;
height: 99%;
background-image: url(images/background_star.gif);
}
.background {
background-image: url(images/background_star.gif);
width: 100%;
height: 100vh;
position: absolute;
}
.undertale-bg {
background-image: url(images/ut-bg.webp);
width: 100%;
position: sticky;
}
.header {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
margin-bottom: 5px;
background-color: rgb(54, 39, 48);
font-size: 5em;
font-family: anisha;
text-align: center;
height: 100%;
}
.update-div {
font-size: 0.8rem;
display: flex;
}
.update-div:hover {
cursor: pointer;
}
.finger-hover:hover {
cursor: pointer;
}
.update-text {
align-self: center;
flex: 70%;
}
.update-img {
flex: 30%;
}
.main {
margin-top: 5px;
width: 900px;
position: relative;
margin-left: auto;
@ -66,64 +11,37 @@ body {
color:aliceblue;
font-family: havakana;
font-size: 1.1em;
box-sizing: border-box;
}
.header {
border: 3px solid;
border-radius: 4px;
border-color: #fce6ff;
background-color: #362730;
font-size: 5em;
font-family: anisha;
text-align: center;
}
.main {
margin-top: 5px;
}
.navbar {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
background-color: rgb(54, 39, 48);
position: relative;
float:left;
width: 250px;
text-decoration: none;
list-style: none;
}
.navbar a, a:link, a:visited{
text-decoration: none;
color:#fce6ff
}
.left {
float:left;
max-width: 256px;
}
.right {
float:right;
max-width: 614px;
}
.leftbar {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
background-color: rgb(54, 39, 48);
position: relative;
float:left;
width: 250px;
padding: 5px 0px;
}
.block {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
background-color: rgb(54, 39, 48);
margin-bottom: 5px;
position: relative;
}
.leftbarblock-nop {
float:left;
width: 250px;
}
.leftbarblock {
float:left;
width: 250px;
padding: 5px 0px;
}
.contentblock {
float:right;
width: 614px;
padding: 10px;
color:rgb(252, 230, 255)
}
.content {
@ -138,28 +56,13 @@ body {
padding: 10px;
}
.no-border {
border: none;
}
.content-text {
max-width: 75%;
width: auto;
max-width: 80%;
margin-left: 10px;
margin-top: 5px;
margin-bottom: 5px;
}
.blogpost-bar {
}
.blogpost-titles {
margin-left: 10px;
text-decoration: none;
color: rgb(252, 230, 255);
}
/*
.content p {
max-width: 80%;
@ -168,11 +71,10 @@ body {
margin-bottom: 5px;
}*/
.guestbook-form-div {
margin: 0px 0px 10px 0px;
.gbp {
}
.comment {
border: 2px solid;
border-radius: 6px;
@ -298,33 +200,6 @@ div.comment-message p{
margin-bottom: 5px;
}
.update-message {
font-family: havakana;
margin-top: 0px;
}
.pulsing {
animation-name: pulsing;
animation-duration: 0.4s;
animation-timing-function: ease-out;
animation-direction: alternate;
animation-iteration-count: infinite;
animation-play-state: running;
}
@keyframes pulsing {
0% {
transform: scale(.7);
}
50% {
}
100% {
transform: scale(1.5);
}
}
/*Margin top 5px*/
.m-t5 {
margin-top: 5px;
@ -336,94 +211,6 @@ div.comment-message p{
margin-bottom: 10px;
}
.mt-20 {
margin-top: 20px;
}
.mb-10 {
margin-bottom: 10px;
}
.bold {
font-weight: 700;
}
.fit {
width: fit-content;
}
.invisible {
visibility: hidden;
}
@media only screen and (max-width: 914px) {
.header {
font-size: 10vw;
height: fit-content;
flex: 0 1 auto;
}
.main {
display: flex;
flex-flow: column;
padding-left: 5px;
padding-right: 5px;
width: 100%;
height: 100%;
}
.block {
box-sizing: border-box;
}
.right {
width: 100%;
max-width: none;
z-index: 0;
}
.leftbarblock {
display: none;
}
.left {
z-index: 1;
max-width: unset;
width: 100%;
flex: 1 1 auto;
}
.navbar {
height: 100%;
display: flexbox;
flex-direction: column;
}
.navbar ul {
padding-inline-end: 40px;
}
.menuitem {
padding: 3.5mm 10px;
margin-bottom: 10px;
min-width: 65px;
flex: 1 1 auto;
border: 0px none;
border-radius: 6px;
background-color: #4974a5;
text-align: center;
vertical-align: middle;
}
.menuitem-text {
}
.menuitem-link {
list-style-type: none;
}
.menuitem-link a {
text-align: center;
}
}
}