diff --git a/Makefile b/Makefile index 3d9374b..0601754 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ build-frontend: - cd frontend && pnpm build + pnpm --dir frontend build build: - go build -o bin/tiny-todo main.go + make build-frontend && go build -o bin/tiny-todo main.go run: make build-frontend && go run main.go serve \ No newline at end of file diff --git a/cmd/serve.go b/cmd/serve.go index e16a512..b06ee66 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -1,9 +1,8 @@ package cmd import ( - "fmt" - "github.com/lukecarr/tiny-todo/internal/server" + "github.com/moducate/x/osx" "github.com/spf13/cobra" ) @@ -13,7 +12,7 @@ func MakeServeCmd() *cobra.Command { Short: "Serves tiny-todo's server", Run: func(cmd *cobra.Command, args []string) { srv := server.New("") - srv.Listen(fmt.Sprintf(":3000")) + srv.Listen(osx.Getenv("TINY_TODO_ADDR", ":3000")) }, } } diff --git a/frontend/hooks/local-storage.ts b/frontend/hooks/local-storage.ts new file mode 100644 index 0000000..03385f0 --- /dev/null +++ b/frontend/hooks/local-storage.ts @@ -0,0 +1,43 @@ +import { useState } from 'preact/hooks' + +export function useLocalStorage(key: string, initialValue: T): [T, (value: T) => void] { + // State to store our value + // Pass initial state function to useState so logic is only executed once + const [storedValue, setStoredValue] = useState(() => { + if (typeof window === "undefined") { + return initialValue + } + + try { + // Get from local storage by key + const item = window.localStorage.getItem(key) + // Parse stored json or if none return initialValue + return item ? JSON.parse(item) : initialValue + } catch (error) { + // If error also return initialValue + console.log(error) + return initialValue + } + }) + + // Return a wrapped version of useState's setter function that ... + // ... persists the new value to localStorage. + const setValue = (value: T) => { + try { + // Allow value to be a function so we have same API as useState + const valueToStore = + value instanceof Function ? value(storedValue) : value + // Save state + setStoredValue(valueToStore) + // Save to local storage + if (typeof window !== "undefined") { + window.localStorage.setItem(key, JSON.stringify(valueToStore)) + } + } catch (error) { + // A more advanced implementation would handle the error case + console.log(error) + } + }; + + return [storedValue, setValue] +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 4ae5b1d..ccc0e88 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "typescript": "4.6.3", "vite": "2.9.1", "vite-plugin-windicss": "1.8.3", + "vite-tsconfig-paths": "3.4.1", "windicss": "3.5.1" }, "volta": { diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index fa2bd66..615e666 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -8,6 +8,7 @@ specifiers: typescript: 4.6.3 vite: 2.9.1 vite-plugin-windicss: 1.8.3 + vite-tsconfig-paths: 3.4.1 windicss: 3.5.1 dependencies: @@ -20,6 +21,7 @@ devDependencies: typescript: 4.6.3 vite: 2.9.1 vite-plugin-windicss: 1.8.3_vite@2.9.1 + vite-tsconfig-paths: 3.4.1_vite@2.9.1 windicss: 3.5.1 packages: @@ -273,6 +275,10 @@ packages: to-fast-properties: 2.0.0 dev: true + /@cush/relative/1.0.0: + resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==} + dev: true + /@jridgewell/resolve-uri/3.0.5: resolution: {integrity: sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==} engines: {node: '>=6.0.0'} @@ -371,6 +377,10 @@ packages: picomatch: 2.3.1 dev: true + /@types/json5/0.0.29: + resolution: {integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4=} + dev: true + /@windicss/config/1.8.3: resolution: {integrity: sha512-1fvfZhRD7WfV/Xh6uIAYKIdbQWrwEgSdkFlHiLPzMDS44KjwNZILDzLAz9Y2W5H2K4MLGgGMnzGS89ECyjc0Ww==} dependencies: @@ -742,11 +752,19 @@ packages: is-glob: 4.0.3 dev: true + /glob-regex/0.3.2: + resolution: {integrity: sha512-m5blUd3/OqDTWwzBBtWBPrGlAzatRywHameHeekAZyZrskYouOGdNB8T/q6JucucvJXtOuyHIn0/Yia7iDasDw==} + dev: true + /globals/11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} dev: true + /globrex/0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: true + /has-flag/3.0.0: resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=} engines: {node: '>=4'} @@ -797,6 +815,13 @@ packages: hasBin: true dev: true + /json5/1.0.1: + resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} + hasBin: true + dependencies: + minimist: 1.2.6 + dev: true + /json5/2.2.1: resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} engines: {node: '>=6'} @@ -826,6 +851,10 @@ packages: picomatch: 2.3.1 dev: true + /minimist/1.2.6: + resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} + dev: true + /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true @@ -882,6 +911,15 @@ packages: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true + /recrawl-sync/2.2.2: + resolution: {integrity: sha512-E2sI4F25Fu2nrfV+KsnC7/qfk/spQIYXlonfQoS4rwxeNK5BjxnLPbWiRXHVXPwYBOTWtPX5765kTm/zJiL+LQ==} + dependencies: + '@cush/relative': 1.0.0 + glob-regex: 0.3.2 + slash: 3.0.0 + tslib: 1.14.1 + dev: true + /resolve/1.22.0: resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==} hasBin: true @@ -919,6 +957,11 @@ packages: hasBin: true dev: true + /slash/3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + /source-map-js/1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -933,6 +976,11 @@ packages: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} dev: true + /strip-bom/3.0.0: + resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=} + engines: {node: '>=4'} + dev: true + /supports-color/5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -957,6 +1005,19 @@ packages: is-number: 7.0.0 dev: true + /tsconfig-paths/3.14.1: + resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.1 + minimist: 1.2.6 + strip-bom: 3.0.0 + dev: true + + /tslib/1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + /typescript/4.6.3: resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==} engines: {node: '>=4.2.0'} @@ -977,6 +1038,20 @@ packages: - supports-color dev: true + /vite-tsconfig-paths/3.4.1_vite@2.9.1: + resolution: {integrity: sha512-SgK3/pnTuJ3i+gMSAWLR6VCPSw26bnxawrmXGvCDjJgk8MAQgmbCrFrAzfwbwZBXSqSuvWEuX04Wt73qJKx8fQ==} + peerDependencies: + vite: '>2.0.0-0' + dependencies: + debug: 4.3.4 + globrex: 0.1.2 + recrawl-sync: 2.2.2 + tsconfig-paths: 3.14.1 + vite: 2.9.1 + transitivePeerDependencies: + - supports-color + dev: true + /vite/2.9.1: resolution: {integrity: sha512-vSlsSdOYGcYEJfkQ/NeLXgnRv5zZfpAsdztkIrs7AZHV8RCMZQkwjo4DS5BnrYTqoWqLoUe1Cah4aVO4oNNqCQ==} engines: {node: '>=12.2.0'} diff --git a/frontend/src/app.tsx b/frontend/src/app.tsx index b261c36..cf5add9 100644 --- a/frontend/src/app.tsx +++ b/frontend/src/app.tsx @@ -1,5 +1,5 @@ -import Header from './components/Header' -import Router from './components/Router' +import Header from 'src/components/Header' +import Router from 'src/components/Router' export function App() { return ( diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 58893e1..99baf4c 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -1,10 +1,11 @@ import type { FunctionalComponent } from 'preact' +import Welcome from './Welcome' const Header: FunctionalComponent = () => { return

tiny-todo

-
A simple todo app that's: tiny 🐜, lightweight 🔦🏋️‍♀️, and performant ⚡. Built using Go & Preact!
-