From e46408c9b68013a995b5e27a9dc871e83463c316 Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Wed, 23 Nov 2022 00:04:37 +0800 Subject: [PATCH 1/2] feat: simple dogs API example with ReactJS + SWR This adds support for fetching images by breed. The user can select a breed from a dropdown and the app will fetch a list of images for that breed. The user can also fetch a random image. Refer to https://wails.io/docs/tutorials/dogsapi/ --- app.go | 82 ++++++++++++++++++++++- frontend/index.html | 1 + frontend/package.json | 5 +- frontend/package.json.md5 | 2 +- frontend/src/App.css | 67 +++--------------- frontend/src/App.tsx | 108 +++++++++++++++++++++++------- frontend/src/style.css | 23 +++---- frontend/wailsjs/go/main/App.d.ts | 6 +- frontend/wailsjs/go/main/App.js | 12 +++- frontend/yarn.lock | 12 ++++ main.go | 2 +- 11 files changed, 215 insertions(+), 105 deletions(-) diff --git a/app.go b/app.go index af53038a..30173c46 100644 --- a/app.go +++ b/app.go @@ -2,9 +2,29 @@ package main import ( "context" + "encoding/json" "fmt" + "io/ioutil" + "log" + "net/http" + "sort" ) +type RandomImage struct { + Message string + Status string +} + +type AllBreeds struct { + Message map[string]map[string][]string + Status string +} + +type ImagesByBreed struct { + Message []string + Status string +} + // App struct type App struct { ctx context.Context @@ -21,7 +41,63 @@ func (a *App) startup(ctx context.Context) { a.ctx = ctx } -// Greet returns a greeting for the given name -func (a *App) Greet(name string) string { - return fmt.Sprintf("Hello %s, It's show time!", name) +func (a *App) GetRandomImageUrl() string { + response, err := http.Get("https://dog.ceo/api/breeds/image/random") + if err != nil { + log.Fatal(err) + } + + responseData, err := ioutil.ReadAll(response.Body) + if err != nil { + log.Fatal(err) + } + + var data RandomImage + json.Unmarshal(responseData, &data) + + return data.Message +} + +func (a *App) GetBreedList() []string { + var breeds []string + + response, err := http.Get("https://dog.ceo/api/breeds/list/all") + if err != nil { + log.Fatal(err) + } + + responseData, err := ioutil.ReadAll(response.Body) + if err != nil { + log.Fatal(err) + } + + var data AllBreeds + json.Unmarshal(responseData, &data) + + for k := range data.Message { + breeds = append(breeds, k) + } + + sort.Strings(breeds) + + return breeds +} + +func (a *App) GetImageUrlsByBreed(breed string) []string { + + url := fmt.Sprintf("%s%s%s%s", "https://dog.ceo/api/", "breed/", breed, "/images") + response, err := http.Get(url) + if err != nil { + log.Fatal(err) + } + + responseData, err := ioutil.ReadAll(response.Body) + if err != nil { + log.Fatal(err) + } + + var data ImagesByBreed + json.Unmarshal(responseData, &data) + + return data.Message } diff --git a/frontend/index.html b/frontend/index.html index a7c0e60a..f0ebfcd1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,6 +4,7 @@ wails-react-demo +
diff --git a/frontend/package.json b/frontend/package.json index f66ca4aa..8eda4fe5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,8 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "swr":"2.0.0-rc.0" }, "devDependencies": { "@types/react": "^18.0.25", @@ -19,4 +20,4 @@ "typescript": "^4.9.3", "vite": "^3.2.4" } -} \ No newline at end of file +} diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index 056fa1f1..bc23d28a 100755 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -e8868246d0965fec42f8916e85224d6b \ No newline at end of file +3ea1113871f7d8ed6506615c919ed32f \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css index f949d9c1..4c05dbd2 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,59 +1,14 @@ -#app { - height: 100vh; - text-align: center; +#App { + width: 100vw; + height: 100vh; + text-align: center; + justify-content: center; + margin: 0 auto; } -#logo { - display: block; - width: 50%; - height: 50%; - margin: auto; - padding: 10% 0 0; - background-position: center; - background-repeat: no-repeat; - background-size: 100% 100%; - background-origin: content-box; +p { + justify-content: center; + text-align: center; + align-items: center; + display: flex; } - -.result { - height: 20px; - line-height: 20px; - margin: 1.5rem auto; -} - -.input-box .btn { - width: 60px; - height: 30px; - line-height: 30px; - border-radius: 3px; - border: none; - margin: 0 0 0 20px; - padding: 0 8px; - cursor: pointer; -} - -.input-box .btn:hover { - background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); - color: #333333; -} - -.input-box .input { - border: none; - border-radius: 3px; - outline: none; - height: 30px; - line-height: 30px; - padding: 0 10px; - background-color: rgba(240, 240, 240, 1); - -webkit-font-smoothing: antialiased; -} - -.input-box .input:hover { - border: none; - background-color: rgba(255, 255, 255, 1); -} - -.input-box .input:focus { - border: none; - background-color: rgba(255, 255, 255, 1); -} \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a6e56f9f..5b8a327a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,28 +1,88 @@ -import {useState} from 'react'; -import logo from './assets/images/logo-universal.png'; -import './App.css'; -import {Greet} from "../wailsjs/go/main/App"; +import useSWR from "swr"; + +import "./App.css"; +import { + GetBreedList, + GetImageUrlsByBreed, + GetRandomImageUrl, +} from "../wailsjs/go/main/App"; +import { useState } from "react"; + +function BreedList({ name }: { name: string }) { + const { isLoading, data: images = [], error } = useSWR( + name, + GetImageUrlsByBreed, + ); + + if (isLoading) return

Loading

; + + if (error) return

Failed to fetch

; + + return ( +
    + {images.map((img) => ( +
  1. + dog +
  2. + ))} +
+ ); +} + +function RandomDog({ showedAt }: { showedAt: number }) { + const { isLoading, data: img, error } = useSWR( + `random-${showedAt}`, + GetRandomImageUrl, + ); + + if (isLoading) return

Loading

; + + if (error) return

Failed to fetch

; + + return dog; +} function App() { - const [resultText, setResultText] = useState("Please enter your name below 👇"); - const [name, setName] = useState(''); - const updateName = (e: any) => setName(e.target.value); - const updateResultText = (result: string) => setResultText(result); - - function greet() { - Greet(name).then(updateResultText); - } - - return ( -
- -
{resultText}
-
- - -
-
- ) + const { isLoading, data: breeds = [], error } = useSWR( + "getBreedList", + GetBreedList, + ); + const [seletedBreed, setSeletedBreed] = useState< + { type: "random" | "list"; value?: string } + >(); + + if (isLoading) return

Loading

; + + if (error) return

Failed to fetch

; + + return ( +
+

Dog API

+ + +

+ Click on down arrow to select a breed + + +

+ + {seletedBreed?.value && } + + {seletedBreed?.type === "random" && } +
+ ); } -export default App +export default App; diff --git a/frontend/src/style.css b/frontend/src/style.css index 3940d6c6..6e4c2b6f 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -1,26 +1,19 @@ html { - background-color: rgba(27, 38, 54, 1); - text-align: center; - color: white; + text-align: center; } body { - margin: 0; - color: white; - font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + margin: 0; + color: white; + font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; } @font-face { - font-family: "Nunito"; - font-style: normal; - font-weight: 400; - src: local(""), + font-family: "Nunito"; + font-style: normal; + font-weight: 400; + src: local(""), url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2"); } - -#app { - height: 100vh; - text-align: center; -} diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts index 02a3bb98..999675dc 100755 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -1,4 +1,8 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -export function Greet(arg1:string):Promise; +export function GetBreedList():Promise>; + +export function GetImageUrlsByBreed(arg1:string):Promise>; + +export function GetRandomImageUrl():Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index c71ae77c..816d2cbc 100755 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -2,6 +2,14 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -export function Greet(arg1) { - return window['go']['main']['App']['Greet'](arg1); +export function GetBreedList() { + return window['go']['main']['App']['GetBreedList'](); +} + +export function GetImageUrlsByBreed(arg1) { + return window['go']['main']['App']['GetImageUrlsByBreed'](arg1); +} + +export function GetRandomImageUrl() { + return window['go']['main']['App']['GetRandomImageUrl'](); } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 4e0db2de..23200e4a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -704,6 +704,13 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +swr@2.0.0-rc.0: + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.0.0-rc.0.tgz#4ed95e483f3b87e1f188a74e9b2c7655694d9c85" + integrity sha512-QOp+4Cqnb/uuLKeuRDh7aT+ws6wSWWKPqfyIpBXK8DM3IugOYeLO5v+390I0p1MIfRd0CQlAIJZBEgmHaTfDuA== + dependencies: + use-sync-external-store "^1.2.0" + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -722,6 +729,11 @@ update-browserslist-db@^1.0.9: escalade "^3.1.1" picocolors "^1.0.0" +use-sync-external-store@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + vite@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/vite/-/vite-3.2.4.tgz#d8c7892dd4268064e04fffbe7d866207dd24166e" diff --git a/main.go b/main.go index ac15b552..8d75e7df 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,7 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "Wails React Demo", + Title: "Git Batch Changes", Width: 1024, Height: 768, AssetServer: &assetserver.Options{ From 007368bf948b88363d9032d2f51486965cd7242c Mon Sep 17 00:00:00 2001 From: Huynh Duc Dung Date: Sun, 4 Dec 2022 03:44:31 +0800 Subject: [PATCH 2/2] chore: fix typo --- frontend/src/App.tsx | 52 ++++++++++++++++++++++++-------------------- main.go | 2 +- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5b8a327a..2ffcdcb5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,18 +1,18 @@ -import useSWR from "swr"; - -import "./App.css"; import { GetBreedList, GetImageUrlsByBreed, GetRandomImageUrl, } from "../wailsjs/go/main/App"; +import "./App.css"; import { useState } from "react"; +import useSWR from "swr"; function BreedList({ name }: { name: string }) { - const { isLoading, data: images = [], error } = useSWR( - name, - GetImageUrlsByBreed, - ); + const { + isLoading, + data: images = [], + error, + } = useSWR(name, GetImageUrlsByBreed); if (isLoading) return

Loading

; @@ -30,10 +30,11 @@ function BreedList({ name }: { name: string }) { } function RandomDog({ showedAt }: { showedAt: number }) { - const { isLoading, data: img, error } = useSWR( - `random-${showedAt}`, - GetRandomImageUrl, - ); + const { + isLoading, + data: img, + error, + } = useSWR(`random-${showedAt}`, GetRandomImageUrl); if (isLoading) return

Loading

; @@ -43,13 +44,15 @@ function RandomDog({ showedAt }: { showedAt: number }) { } function App() { - const { isLoading, data: breeds = [], error } = useSWR( - "getBreedList", - GetBreedList, - ); - const [seletedBreed, setSeletedBreed] = useState< - { type: "random" | "list"; value?: string } - >(); + const { + isLoading, + data: breeds = [], + error, + } = useSWR("getBreedList", GetBreedList); + const [selectedBreed, setSelectedBreed] = useState<{ + type: "random" | "list"; + value?: string; + }>(); if (isLoading) return

Loading

; @@ -59,15 +62,18 @@ function App() {

Dog API

-

Click on down arrow to select a breed -

- {seletedBreed?.value && } + {selectedBreed?.value && } - {seletedBreed?.type === "random" && } + {selectedBreed?.type === "random" && }
); } diff --git a/main.go b/main.go index 8d75e7df..60b4746c 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,7 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "Git Batch Changes", + Title: "Dogs Api Demo", Width: 1024, Height: 768, AssetServer: &assetserver.Options{