Compare commits

...

130 Commits

Author SHA1 Message Date
7e171abfb8
More music lol
All checks were successful
continuous-integration/drone Build is passing
continuous-integration/drone/push Build is passing
2022-06-22 21:58:51 +02:00
95e5a173f1
Messed up desktop
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-06-22 13:49:43 +02:00
d1190c4e88
MOBILE
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-22 13:45:43 +02:00
d66bb5afeb
Fix mobile kinda
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-22 12:39:36 +02:00
dbfdaf38bb
Add hamburger
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-20 13:33:22 +02:00
85d13a2905
New tags good
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-20 11:19:15 +02:00
f5fa2af536
New tags
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-20 11:17:16 +02:00
6a2ec9f099
Mobile scaling
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-20 11:02:53 +02:00
31abd9e138
Okay, I think I know
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-08 20:50:31 +02:00
ff7044ab9b
Finally... Music link fingering 😉
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-06-05 10:12:18 +02:00
acba8179fb
Add update button
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-05-31 10:22:29 +02:00
67358d1616
Merge branch 'main' of dutchellie.nl:DutchEllie/proper-website-2
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-31 09:48:21 +02:00
55e3e13459
Nah nvm
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-27 21:30:49 +02:00
1f59549cc4
Possibly maybe....
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-27 20:51:53 +02:00
e414c98866
Possibly maybe....
Some checks failed
continuous-integration/drone/push Build is failing
2022-05-27 20:50:12 +02:00
029b426757
Made generating static site possible
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-27 20:35:53 +02:00
629b642d86
Go mod tidy 2022-05-23 14:40:48 +02:00
8aaecbab69
Idk what went wrong?
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-05-23 14:40:22 +02:00
821f2c916e
Added very cool songs
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-05-14 22:24:14 +02:00
b765a88f27
Disabled CGO
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-05-14 21:45:04 +02:00
d489dd744c
Update the production pipeline too
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-14 21:22:11 +02:00
b06ec9b955
Forgot the volumes
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-14 21:12:04 +02:00
42eba16ef2
Dependency issue
Some checks failed
continuous-integration/drone/push Build is failing
2022-05-14 21:10:44 +02:00
caf15aadcb
CI update
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-05-14 21:09:48 +02:00
a88651e4b7
Revert APIURL changes, it's impossible
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-14 20:45:28 +02:00
450bf3631c
Oh right, after browser
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-14 18:31:43 +02:00
98a4b28e8b
Come on CI
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-14 18:25:45 +02:00
ba1fae4547
Moved API URL to environment 2022-05-14 18:24:19 +02:00
734d9e4e27 [SKIP CI] Readme.md
Signed-off-by: Quenten <personal@quenten.nl>
2022-05-10 08:43:28 +00:00
e2f4db7b97 Updated Badge [SKIP CI] 2022-05-08 19:29:17 +00:00
be54e41135
Modified API url
All checks were successful
continuous-integration/drone Build is passing
continuous-integration/drone/push Build is passing
2022-05-08 17:11:45 +02:00
77ccef7c68
Added Kyu's Left on Red
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-05-01 17:39:01 +02:00
65f5adc604
Added ERROR to cache
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-04-25 14:29:37 +02:00
14969944a8
Added error by towa
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-25 14:27:50 +02:00
7ce4efb20e
Added a new song
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-20 18:07:52 +02:00
035f3e21d0
Changed domain in intro page
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-04-20 15:25:47 +02:00
24f12a1e37
Changed prod domain 2022-04-20 15:23:54 +02:00
d0e7f90e9f
Moved staging domain
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-20 14:52:20 +02:00
4cd86be5a0
Added frame options
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-20 14:45:04 +02:00
8d0b6a37c1
Fixed values
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-20 14:14:52 +02:00
85685b1da3
Added CSP
Some checks failed
continuous-integration/drone/push Build is failing
2022-04-20 14:03:46 +02:00
111c6fd5de
Removed debug messages
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-19 23:06:27 +02:00
2332cda5e4
Added caching song for expensive S3 storage
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-19 22:51:24 +02:00
182fb13590
Added music player beta
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-19 22:30:47 +02:00
4968a2a8ca
Added sass
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-04-18 22:20:36 +02:00
7e4a5a5302
Added textwithtooltip component
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-18 22:02:51 +02:00
b8e8cc3a60
Added status check
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-18 16:02:41 +02:00
e001227cd4
Updated cache
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-18 13:51:17 +02:00
a92e64b7f2
Added GNU and rewrote page
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-18 13:49:16 +02:00
7389daba09
Hidden blog nav for now
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-27 16:50:48 +02:00
cd8d116fef
Intermediate loading of comments
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-27 16:39:38 +02:00
70c9f027b8
Added efficient comment loading
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-27 14:06:21 +02:00
75dab086ff
Guestbook loading still broken 2022-03-25 14:02:14 +01:00
4eb00e38bc
Blogposts are a thing now
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-25 13:51:25 +01:00
5e688e3870
Change compression level
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-24 15:55:18 +01:00
08514b50c6
Fixed bg
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-24 14:50:36 +01:00
7717210578
Compressing handlergit add .
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-24 14:31:02 +01:00
bb163abb0a
Fixed the API url in makefile [CI SKIP] 2022-03-24 13:36:44 +01:00
d7c8ff80e4
Fixed EOF error
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-24 13:28:28 +01:00
acbdd113d4
Started working on blogposts
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-24 13:25:07 +01:00
6b4c6bb447
Added entity
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-24 13:12:57 +01:00
30d57404f9
eeeeh
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-18 22:45:29 +01:00
6482fcd6f1
Caching
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-16 13:13:14 +01:00
858abd350e
Fixed some layout 2022-03-16 12:49:52 +01:00
44da256e10
Wait noo
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-15 19:38:46 +01:00
715aac28b4
God I hate CSS
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-15 19:37:46 +01:00
5c4b46b65e
Fixed
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-15 17:44:29 +01:00
a32ced531f
This would be really cool
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-15 17:25:42 +01:00
bc38196fd3
Todo 2022-03-15 16:47:49 +01:00
330385a357
Some aligning
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-15 16:44:32 +01:00
cd07bef29e
Rewrote guestbook 2022-03-15 16:42:26 +01:00
73442b212c
Updater
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-15 14:12:15 +01:00
0ca691e3ae
Layout and refactoring 2022-03-15 13:22:59 +01:00
03a09fc452
Moved pages 2022-03-15 13:06:52 +01:00
2e381108d1
More unused code 2022-03-15 12:57:12 +01:00
0b7b478cd3
Removed unused code 2022-03-15 12:56:08 +01:00
81d9e315a0
Major refactor
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-15 12:48:47 +01:00
73cf59ff86
App notes 2022-03-15 09:00:19 +01:00
f82f72ec1c
Added pronouns
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-14 21:40:38 +01:00
73385c9d39
Added Evillious guide website
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-14 21:37:22 +01:00
fb7c79f570
omg this layout is so goood 2022-03-14 21:10:10 +01:00
f46005130e
Fixed css failure 2022-03-14 21:05:03 +01:00
a2fbcf03ca
New frontpage design
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-14 21:04:30 +01:00
00614b6346
Added Kristy
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-14 09:17:20 +01:00
cd5bdead18
Changed friends to galaxies and fixed CSS
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-13 21:34:54 +01:00
af1cd54cd4
Testing and normal API
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-13 20:18:07 +01:00
3ade18b568
I am dumb
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-13 19:37:32 +01:00
d88b75988e
Separate staging and production image
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-13 19:34:20 +01:00
eb5b2c8d23
Fixed dumb mistake
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-13 18:22:47 +01:00
a8f181507d
Fixed Go module
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-13 16:56:18 +01:00
5fe5b75e1d
Changed module name
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-13 16:35:31 +01:00
d54fff9c85
Limited message size on client side
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-13 16:17:27 +01:00
081b58bb16
Added icon and changed name 2022-03-13 15:27:47 +01:00
fbf56fdec8
I am a massive idiot
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-12 21:41:02 +01:00
06d119e1e2
Remove docker build step from promote
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-12 21:37:05 +01:00
263ec7cc6f
Added SHA tags
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-12 21:32:02 +01:00
262aaaf461
Removed emphasis
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-12 21:19:17 +01:00
72db027101
Added production
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-03-12 20:43:21 +01:00
9bda314798
Deploy now depends on docker 2022-03-12 20:28:47 +01:00
a21908aab3
Changed fou link 2022-03-12 20:26:36 +01:00
1c897d727b
Another templating issue
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-12 19:57:43 +01:00
63a063e552
Fixed domain
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-03-12 19:51:40 +01:00
b38052bd32
Removed dry run
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-12 19:45:57 +01:00
a2995ed8da
Alright, another templating issue
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-12 19:40:30 +01:00
860b2e47d0
Templating broken
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-03-12 19:31:01 +01:00
8776c02bb4
Added chart specs
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-03-12 19:27:58 +01:00
291b553638
Moved values
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-03-12 19:26:04 +01:00
a7e111cc96
Let's try this
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-03-12 19:18:04 +01:00
69c48261fe
Fix namespace
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-03-12 19:11:21 +01:00
d719256417
Skip TLS verify
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-03-12 19:10:13 +01:00
a92835309b
Drone fix
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone Build was killed
2022-03-12 18:18:01 +01:00
a15696f1d1
Separated pipelines 2022-03-12 18:15:46 +01:00
fdb401daa2
Fixed secrets
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-12 18:00:12 +01:00
7ce6889321
First test for drone
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-12 17:47:01 +01:00
dbd60036f3
Add cool badge 2022-03-12 16:32:51 +01:00
1968fddfe5
Fix drone
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-12 16:28:45 +01:00
fe76cd80f8
CI/CD
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-12 16:22:41 +01:00
cb3c3c8d30
Last 2022-03-12 15:52:13 +01:00
c10a995b63
Okay this is based 2022-03-07 13:05:33 +01:00
e92d0f2c26
So many things 2022-03-02 13:38:28 +01:00
5245702d00
I am getting so good at this 2022-03-01 19:45:23 +01:00
b60f030880
Laid the groundwork! 2022-03-01 17:07:33 +01:00
7f15b71498
Ohhhh this is great!! 2022-03-01 15:50:53 +01:00
4bc61ddd20
updated gitignore 2022-03-01 14:51:57 +01:00
c9ca9a634e
Updated gitignore 2022-03-01 14:51:45 +01:00
026e7e010c
Hello world from go-app 2022-03-01 14:36:04 +01:00
20bf59c467
Bye random packages 2022-03-01 14:28:48 +01:00
53ff85cb15
Okay nevermind 2022-03-01 14:26:39 +01:00
95fecdb9db
various things 2022-03-01 12:02:05 +01:00
53f6d75fc1
Readme edits 2022-03-01 10:04:28 +01:00
77 changed files with 3360 additions and 1 deletions

159
.drone.yml Normal file
View File

@ -0,0 +1,159 @@
kind: pipeline
type: kubernetes
name: staging
trigger:
event:
- push
# 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}"
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
trigger:
event:
- promote
target:
- production
# PRODUCTION!!!!
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
image: plugins/docker
privileged: true
volumes:
- name: build
path: /drone/src/build
settings:
cache_from:
- "dutchellie/proper-website-2:latest"
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
- name: deploy-production
image: pelotech/drone-helm3
settings:
mode: upgrade
chart: .drone/helm/chart
namespace: drone-production
release: newsite-production
skip_tls_verify: true
values_files:
- .drone/helm/prod-val.yaml
values:
- "image=dutchellie/proper-website-2:latest-${DRONE_COMMIT_SHA:0:8}"
kube_api_server:
from_secret: prod_api_server
kube_token:
from_secret: prod_kube_token
kube_certificate:
from_secret: prod_kube_certificate
kube_service_account: drone-deploy
dry_run: false
depends_on:
- build-publish-image
volumes:
- name: build
temp: {}

View File

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

View File

@ -0,0 +1,39 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.name }}
namespace: {{ .Release.Namespace }}
{{- if .Values.annotations }}
annotations: {{ toYaml .Values.annotations | nindent 4}}
{{- end}}
spec:
selector:
matchLabels:
app: {{ .Values.name }}
release: {{ .Values.release }}
replicas: {{ .Values.replicas }}
template:
metadata:
{{- if .Values.podAnnotations }}
annotations:
{{- range $key, $value := .Values.controller.podAnnotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
labels:
app: {{ .Values.name }}
release: {{ .Values.release }}
spec:
containers:
- name: {{ .Values.containerName }}
image: {{ .Values.image }}
imagePullPolicy: {{ .Values.imagePullPolicy }}
ports:
- name: http
containerPort: 8000
{{- if .Values.containerEnv }}
env: {{ toYaml .Values.containerEnv | nindent 12 }}
{{- end }}

View File

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

View File

@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.service.name }}
namespace: {{ .Release.Namespace }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
selector:
app: {{ .Values.name }}
release: {{ .Values.release }}
ports:
- protocol: TCP
name: http
port: 8000
targetPort: 8000

View File

@ -0,0 +1,28 @@
name: newsite-deployment
annotations: {}
release: latest
replicas: 1
podAnnotations: {}
containerName: newsite
image: dutchellie/proper-website-2:latest
imagePullPolicy: Always
containerEnv: []
service:
name: newsite-service
annotations: {}
ingress:
name: newsite-ingress
annotations: {}
className: nginx
tls: []
# tls:
# - hosts:
# - example.com
# secretName: example-tls
hosts:
- host: example.com
paths:
- path: /
pathType: Prefix

23
.drone/helm/prod-val.yaml Normal file
View File

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

View File

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

0
.drone/helm/values.yaml Normal file
View File

4
.gitignore vendored Normal file
View File

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

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
#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 alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root
RUN mkdir ./web
COPY ./web ./web
COPY ./build/web/app.wasm ./web/
COPY ./build/app ./
EXPOSE 8000
CMD ["./app"]

26
Makefile Normal file
View File

@ -0,0 +1,26 @@
APIURL_prod := https://api.quenten.nl/api
APIURL_staging := https://api.quenten.nl/api/testing
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/
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/
run: build
./app
run-new: build-new
APIURL=${APIURL_staging} ./app
run-prod: build-prod
./app

View File

@ -1,3 +1,34 @@
# proper-website-2 # proper-website-2
Truly proper website [![Build Status](https://drone.dutchellie.nl/api/badges/DutchEllie/proper-website-2/status.svg)](https://drone.dutchellie.nl/DutchEllie/proper-website-2)
A truly proper website this time™
**TODO**:
- 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
- Templating with Go
- Database connection for comments and such
- Markdown article writing
It will be done in the following order:
- First get up a working simple proof of concept
- Then make the comments better
- Database better
- Layout
- Markdown article writing
- Layout

73
comments.go.old Normal file
View File

@ -0,0 +1,73 @@
package main
import (
"context"
"encoding/json"
"io"
"net/http"
"time"
"git.home.dutchellie.nl/DutchEllie/proper-website-2/entity"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
func (a *application) Comment(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
var comment entity.Comment
body, err := io.ReadAll(r.Body)
if err != nil {
a.WriteError(w, http.StatusInternalServerError, err.Error())
return
}
err = json.Unmarshal(body, &comment)
if err != nil {
a.WriteError(w, http.StatusInternalServerError, err.Error())
return
}
comment.PostDate = time.Now()
if comment.Name == "" || comment.Message == "" {
a.WriteError(w, http.StatusBadRequest, "one or more fields empty")
return
}
_, err = a.collection.InsertOne(context.Background(), comment)
if err != nil {
a.WriteError(w, http.StatusInternalServerError, err.Error())
return
}
w.WriteHeader(200)
return
case "GET":
comments := make([]entity.Comment, 0)
filter := bson.D{}
sort := options.Find()
sort.SetSort(bson.D{{"time", -1}})
cur, err := a.collection.Find(context.Background(), filter, sort)
if err != nil {
a.WriteError(w, http.StatusInternalServerError, err.Error())
return
}
err = cur.All(context.Background(), &comments)
if err != nil {
a.WriteError(w, http.StatusInternalServerError, err.Error())
return
}
jsondata, err := json.Marshal(comments)
if err != nil {
a.WriteError(w, http.StatusInternalServerError, err.Error())
return
}
w.WriteHeader(200)
w.Write(jsondata)
return
}
}
func (a *application) WriteError(w http.ResponseWriter, code int, err string) {
w.WriteHeader(code)
w.Write([]byte(err))
}

15
entity/blogpost.go Normal file
View File

@ -0,0 +1,15 @@
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"`
}

12
entity/comment.go Normal file
View File

@ -0,0 +1,12 @@
package entity
import "time"
type Comment struct {
ID string `json:"id,omitempty" bson:"_id,omitempty"`
Name string `json:"name" bson:"name"`
Website string `json:"website,omitempty" bson:"website,omitempty"`
Email string `json:"email,omitempty" bson:"email,omitempty"`
Message string `json:"message" bson:"message"`
PostDate time.Time `json:"time" bson:"time"`
}

14
go.mod Normal file
View File

@ -0,0 +1,14 @@
module dutchellie.nl/DutchEllie/proper-website-2
go 1.17
require (
github.com/gorilla/handlers v1.5.1
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
github.com/google/uuid v1.3.0 // indirect
)

28
go.sum Normal file
View File

@ -0,0 +1,28 @@
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/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=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
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/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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

BIN
src/__debug_bin Executable file

Binary file not shown.

30
src/aboutpage.go Normal file
View File

@ -0,0 +1,30 @@
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"),
)
}

86
src/block.go Normal file
View File

@ -0,0 +1,86 @@
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]
}),
)
}

74
src/bloglinks.go Normal file
View File

@ -0,0 +1,74 @@
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
}
})
})
}

54
src/blogpage.go Normal file
View File

@ -0,0 +1,54 @@
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
}

15
src/emptypage.go Normal file
View File

@ -0,0 +1,15 @@
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()
}

137
src/galaxiespage.go Normal file
View File

@ -0,0 +1,137 @@
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) {
}*/

338
src/guestbook.go Normal file
View File

@ -0,0 +1,338 @@
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
OnSubmit func(
ctx app.Context,
name string,
email string,
website string,
message 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
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)
g.clear()
ctx.NewAction("guestbook-loadcomments")
//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) 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
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")
}

16
src/header.go Normal file
View File

@ -0,0 +1,16 @@
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{},
)
}

76
src/homepage.go Normal file
View File

@ -0,0 +1,76 @@
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 string) {
var comment entity.Comment
comment.Name = name
comment.Email = email
comment.Website = website
comment.Message = message
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()
}
},
},
),
)
}

75
src/html-doc.go Normal file
View File

@ -0,0 +1,75 @@
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(),
)
}

63
src/html.go Normal file
View File

@ -0,0 +1,63 @@
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
)

41
src/http.go Normal file
View File

@ -0,0 +1,41 @@
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
}

103
src/main.go Normal file
View File

@ -0,0 +1,103 @@
package main
import (
"compress/gzip"
"log"
"net/http"
"os"
"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
//}
func main() {
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)
http.Handle("/", compressed)
if os.Getenv("GEN_STATIC_SITE") == "true" {
return
}
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal(err)
}
}

110
src/menu.go Normal file
View File

@ -0,0 +1,110 @@
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)),
)
}

213
src/misc.go Normal file
View File

@ -0,0 +1,213 @@
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
}

37
src/modal.go Normal file
View File

@ -0,0 +1,37 @@
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
// A generic modal to be used on the entire site
type Modal struct {
app.Compo
Title string
Body []app.UI // Body of the modal
OnClose func()
}
func (m *Modal) Render() app.UI {
return app.Div().
Class("generic-modal").
ID("genericModal").
OnClick(func(ctx app.Context, e app.Event) {
m.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")
m.OnClose()
}),
app.Div().
Class("generic-modal-body").
Body(m.Body...),
),
)
}

51
src/music.go Normal file
View File

@ -0,0 +1,51 @@
// 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(),
),
),
)
}

165
src/musicplayer.go Normal file
View File

@ -0,0 +1,165 @@
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)
}),
)
}),
),
)
}

32
src/navbar.go Normal file
View File

@ -0,0 +1,32 @@
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()
}

101
src/page.go Normal file
View File

@ -0,0 +1,101 @@
package main
import "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)
}
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
}

32
src/undertale.go Normal file
View File

@ -0,0 +1,32 @@
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"),
)
}

49
test-website/base.html Normal file
View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<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">
<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>
</body>
</html>
<!-- After you're done, find a way to credit the graphics creators!
https://dokodemo.neocities.org/materials/index.html BACKGROUND
https://dokodemo.neocities.org/
https://neo-neighborhoods.neocities.org/SiliconValley/6603/ COOL SHIT
https://fontcity.neocities.org/ FOR THE FONT-->

34
test-website/friends.html Normal file
View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<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="static/style.css">
<link rel="stylesheet" href="static/anisha.css?family=anisha">
<title>Index</title>
</head>
<body>
<div class="header">
Internetica Galactica
</div>
<div class="main">
<div class="navbar">
<ul>
<li><a href="base.html">Home</a></li>
<li><a href="friends.html">Friends</a></li>
</ul>
</div>
<div class="content">
Dit is eigenlijk 1 van die dingen die steeds kan veranderen
Deze doet dat
</div>
</div>
</body>
</html>
<!-- After you're done, find a way to credit the graphics creators!
https://dokodemo.neocities.org/materials/index.html BACKGROUND
https://dokodemo.neocities.org/
https://neo-neighborhoods.neocities.org/SiliconValley/6603/ COOL SHIT
https://fontcity.neocities.org/ FOR THE FONT-->

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,73 @@
html {
background-image: url(images/background_star.gif);
}
body {
width: 900px;
position: relative;
margin-left: auto;
margin-right: auto;
color:aliceblue;
font-family: havakana;
font-size: 1.1em;
}
.header {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
background-color: rgb(54, 39, 48);
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:rgb(252, 230, 255)
}
.content {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
background-color: rgb(54, 39, 48);
margin-bottom: 5px;
position: relative;
float:right;
width: 614px;
padding: 10px;
}
.content p {
width: 80%;
margin-left: 10px;
margin-top: 5px;
margin-bottom: 5px;
}
.p-h1 {
font-family: anisha;
font-size: 3em;
}
.p-h2 {
font-family: adreena;
font-size: 1.5em;
}

176
ui/menu.go Normal file
View File

@ -0,0 +1,176 @@
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

@ -0,0 +1,6 @@
<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

@ -0,0 +1,22 @@
<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

@ -0,0 +1,65 @@
<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

@ -0,0 +1,28 @@
<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

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

View File

@ -0,0 +1,8 @@
<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>

View File

1
web/static/adreena.css Normal file

File diff suppressed because one or more lines are too long

1
web/static/anisha.css Normal file

File diff suppressed because one or more lines are too long

46
web/static/form.css Normal file
View File

@ -0,0 +1,46 @@
.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;
}

1
web/static/havakana.css Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
web/static/images/hot1.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
web/static/images/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
web/static/images/rin-1.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 KiB

BIN
web/static/images/rin-2.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

1
web/static/nikoleta.css Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,14 @@
<p>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>

429
web/static/style.css Normal file
View File

@ -0,0 +1,429 @@
html {
/* overflow-y: scroll; */
margin: 0;
height: 99%;
}
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;
margin-right: auto;
color:aliceblue;
font-family: havakana;
font-size: 1.1em;
box-sizing: border-box;
}
.navbar {
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;
}
.content {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
background-color: rgb(54, 39, 48);
margin-bottom: 5px;
position: relative;
float:right;
width: 614px;
padding: 10px;
}
.no-border {
border: none;
}
.content-text {
max-width: 75%;
width: auto;
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%;
margin-left: 10px;
margin-top: 5px;
margin-bottom: 5px;
}*/
.guestbook-form-div {
margin: 0px 0px 10px 0px;
}
.comment {
border: 2px solid;
border-radius: 6px;
border-color: aliceblue;
padding: 0px 0px 5px 0px;
background-color: darkseagreen;
}
.comment-header {
margin-top: 0px;
margin-left: 0px;
margin-right: 0px;
background-color: aquamarine;
width: 100%;
color: black;
display: flex;
flex-direction: row;
}
div .name {
text-align: left;
overflow-wrap: break-word;
}
div .date {
font-size: 0.8em;
margin-right: 10px;
text-align: right;
color: black;
}
div.comment-header p {
width: 80%;
margin-left: 10px;
margin-top: 5px;
margin-bottom: 5px;
}
div.comment-message p{
width: 80%;
margin-left: 10px;
margin-top: 5px;
margin-bottom: 5px;
}
.comment-message {
color:white;
overflow-wrap: break-word;
}
.gb-modal {
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}
.gb-modal-content {
background-color: #fefefe;
color: black;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
.friend-frame {
width: 290px;
height: 200px;
overflow: hidden;
list-style: none;
text-decoration: none;
-ms-zoom: 0.75;
-moz-transform: scale(0.75);
-moz-transform-origin: 0 0;
-o-transform: scale(0.75);
-o-transform-origin: 0 0;
-webkit-transform: scale(0.75);
-webkit-transform-origin: 0 0;
}
.p-h1 {
font-family: anisha;
font-size: 3em;
margin-top:5px;
margin-bottom:5px;
margin-left: 10px;
}
.p-h2 {
font-size: 1.5em;
margin-left: 10px;
}
.p-h3 {
font-size: 1.2em;
}
.small {
font-size: 0.8em;
width: 80%;
margin-left: 10px;
margin-top: 5px;
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;
}
/*Margin top-bottom 10px*/
.m-tb10 {
margin-top: 10px;
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;
}
}