mirror of https://github.com/usbharu/Hideout.git
feat: WebUIを追加
This commit is contained in:
parent
9c5da79389
commit
6b1b6f8bf2
|
@ -37,3 +37,4 @@ out/
|
||||||
*.db
|
*.db
|
||||||
/src/main/resources/static/
|
/src/main/resources/static/
|
||||||
/node_modules/
|
/node_modules/
|
||||||
|
/src/main/web/generated/
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
||||||
|
"spaces": 2,
|
||||||
|
"generator-cli": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"generators": {
|
||||||
|
"v3.0": {
|
||||||
|
"generatorName": "typescript-fetch",
|
||||||
|
"output": "src/main/web/generated",
|
||||||
|
"glob": "src/main/resources/openapi/api.yaml",
|
||||||
|
"additionalProperties": {
|
||||||
|
"modelPropertyNaming": "camelCase",
|
||||||
|
"supportsES6": true,
|
||||||
|
"withInterfaces": true,
|
||||||
|
"typescriptThreePlus": true,
|
||||||
|
"useSingleRequestParameter": false,
|
||||||
|
"prependFormOrBodyParameters": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
|
@ -2,18 +2,24 @@
|
||||||
"name": "hideout",
|
"name": "hideout",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"solid-js": "^1.7.3"
|
"@solid-primitives/storage": "^1.3.11",
|
||||||
|
"@solidjs/router": "^0.8.2",
|
||||||
|
"@suid/icons-material": "^0.6.3",
|
||||||
|
"@suid/material": "^0.12.3",
|
||||||
|
"solid-js": "^1.7.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@openapitools/openapi-generator-cli": "^2.6.0",
|
||||||
|
"@suid/vite-plugin": "^0.1.3",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vite": "^4.2.1",
|
"vite": "^4.2.1",
|
||||||
"vite-plugin-solid": "^2.7.0",
|
"vite-plugin-solid": "^2.7.0"
|
||||||
"@suid/vite-plugin": "^0.1.3"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"serve": "vite preview"
|
"serve": "vite preview",
|
||||||
|
"gen-api": "openapi-generator-cli generate"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Post"
|
$ref: "#/components/schemas/PostResponse"
|
||||||
401:
|
401:
|
||||||
$ref: "#/components/responses/Unauthorized"
|
$ref: "#/components/responses/Unauthorized"
|
||||||
403:
|
403:
|
||||||
|
@ -37,7 +37,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Post"
|
$ref: "#/components/schemas/PostRequest"
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: 成功
|
description: 成功
|
||||||
|
@ -65,7 +65,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Post"
|
$ref: "#/components/schemas/PostResponse"
|
||||||
401:
|
401:
|
||||||
$ref: "#/components/responses/Unauthorized"
|
$ref: "#/components/responses/Unauthorized"
|
||||||
403:
|
403:
|
||||||
|
@ -90,7 +90,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Post"
|
$ref: "#/components/schemas/PostResponse"
|
||||||
401:
|
401:
|
||||||
$ref: "#/components/responses/Unauthorized"
|
$ref: "#/components/responses/Unauthorized"
|
||||||
403:
|
403:
|
||||||
|
@ -114,7 +114,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Post"
|
$ref: "#/components/schemas/PostResponse"
|
||||||
401:
|
401:
|
||||||
$ref: "#/components/responses/Unauthorized"
|
$ref: "#/components/responses/Unauthorized"
|
||||||
403:
|
403:
|
||||||
|
@ -137,7 +137,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/User"
|
$ref: "#/components/schemas/UserResponse"
|
||||||
|
|
||||||
post:
|
post:
|
||||||
summary: ユーザーを作成する
|
summary: ユーザーを作成する
|
||||||
|
@ -181,7 +181,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/User"
|
$ref: "#/components/schemas/UserResponse"
|
||||||
404:
|
404:
|
||||||
$ref: "#/components/responses/NotFound"
|
$ref: "#/components/responses/NotFound"
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/User"
|
$ref: "#/components/schemas/UserResponse"
|
||||||
post:
|
post:
|
||||||
summary: ユーザーをフォローする
|
summary: ユーザーをフォローする
|
||||||
security:
|
security:
|
||||||
|
@ -228,7 +228,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/User"
|
$ref: "#/components/schemas/UserResponse"
|
||||||
|
|
||||||
components:
|
components:
|
||||||
responses:
|
responses:
|
||||||
|
@ -261,8 +261,22 @@ components:
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
schemas:
|
schemas:
|
||||||
User:
|
Visibility:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- public
|
||||||
|
- unlisted
|
||||||
|
- followers
|
||||||
|
- direct
|
||||||
|
UserResponse:
|
||||||
type: object
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- domain
|
||||||
|
- screenName
|
||||||
|
- description
|
||||||
|
- createdAt
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: number
|
type: number
|
||||||
|
@ -277,14 +291,24 @@ components:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
type: string
|
type: string
|
||||||
|
nullable: true
|
||||||
url:
|
url:
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
readOnly: true
|
||||||
createdAt:
|
createdAt:
|
||||||
type: number
|
type: number
|
||||||
readOnly: true
|
readOnly: true
|
||||||
Post:
|
PostResponse:
|
||||||
type: object
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- userId
|
||||||
|
- text
|
||||||
|
- createdAt
|
||||||
|
- visibility
|
||||||
|
- url
|
||||||
|
- sensitive
|
||||||
|
- apId
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
|
@ -303,12 +327,7 @@ components:
|
||||||
format: int64
|
format: int64
|
||||||
readOnly: true
|
readOnly: true
|
||||||
visibility:
|
visibility:
|
||||||
type: string
|
$ref: "#/components/schemas/Visibility"
|
||||||
enum:
|
|
||||||
- public
|
|
||||||
- unlisted
|
|
||||||
- followers
|
|
||||||
- direct
|
|
||||||
url:
|
url:
|
||||||
type: string
|
type: string
|
||||||
format: uri
|
format: uri
|
||||||
|
@ -328,6 +347,24 @@ components:
|
||||||
format: url
|
format: url
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
|
||||||
|
PostRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
overview:
|
||||||
|
type: string
|
||||||
|
text:
|
||||||
|
type: string
|
||||||
|
visibility:
|
||||||
|
$ref: "#/components/schemas/Visibility"
|
||||||
|
repostId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
replyId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
sensitive:
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
BearerAuth:
|
BearerAuth:
|
||||||
|
|
|
@ -1,58 +1,24 @@
|
||||||
import {Component, createSignal} from "solid-js";
|
import {Component} from "solid-js";
|
||||||
|
import {Route, Router, Routes} from "@solidjs/router";
|
||||||
|
import {TopPage} from "./pages/TopPage";
|
||||||
|
import {createTheme, CssBaseline, ThemeProvider, useMediaQuery} from "@suid/material";
|
||||||
|
|
||||||
export const App: Component = () => {
|
export const App: Component = () => {
|
||||||
|
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
||||||
|
|
||||||
const fn = (form: HTMLButtonElement) => {
|
const theme = createTheme({
|
||||||
console.log(form)
|
palette: {
|
||||||
|
mode: prefersDarkMode() ? 'dark' : 'light',
|
||||||
}
|
}
|
||||||
|
|
||||||
const [username, setUsername] = createSignal("")
|
|
||||||
const [password, setPassword] = createSignal("")
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={function (e: SubmitEvent) {
|
|
||||||
e.preventDefault()
|
|
||||||
fetch("/login", {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({username: username(), password: password()}),
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
}).then(res => res.json())
|
|
||||||
// .then(res => fetch("/auth-check", {
|
|
||||||
// method: "GET",
|
|
||||||
// headers: {
|
|
||||||
// 'Authorization': 'Bearer ' + res.token
|
|
||||||
// }
|
|
||||||
// }))
|
|
||||||
// .then(res => res.json())
|
|
||||||
.then(res => {
|
|
||||||
console.log(res.token);
|
|
||||||
fetch("/refresh-token", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({refreshToken: res.refreshToken}),
|
|
||||||
}).then(res=> res.json()).then(res => console.log(res.token))
|
|
||||||
})
|
})
|
||||||
}
|
return (
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
}>
|
<CssBaseline/>
|
||||||
<input name="username" type="text" placeholder="Username" required
|
<Router>
|
||||||
onChange={(e) => setUsername(e.currentTarget.value)}/>
|
<Routes>
|
||||||
<input name="password" type="password" placeholder="Password" required
|
<Route path="/" component={TopPage}/>
|
||||||
onChange={(e) => setPassword(e.currentTarget.value)}/>
|
</Routes>
|
||||||
<button type="submit">Submit</button>
|
</Router>
|
||||||
</form>
|
</ThemeProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
declare module 'solid-js' {
|
|
||||||
namespace JSX {
|
|
||||||
interface Directives {
|
|
||||||
fn: (form: HTMLFormElement) => void
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import {Avatar as SuidAvatar} from "@suid/material";
|
||||||
|
import {Component, JSXElement} from "solid-js";
|
||||||
|
|
||||||
|
export const Avatar: Component<{ src: string }> = (props): JSXElement => {
|
||||||
|
return (
|
||||||
|
<SuidAvatar src={props.src}/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
import {Component, createSignal, Match, Switch} from "solid-js";
|
||||||
|
import {PostResponse} from "../generated";
|
||||||
|
import {Box, Card, CardActions, CardContent, CardHeader, IconButton, Menu, MenuItem, Typography} from "@suid/material";
|
||||||
|
import {Avatar} from "../atoms/Avatar";
|
||||||
|
import {Favorite, Home, Lock, Mail, MoreVert, Public, Reply, ScreenRotationAlt} from "@suid/icons-material";
|
||||||
|
|
||||||
|
export const Post: Component<{ post: PostResponse }> = (props) => {
|
||||||
|
const [anchorEl, setAnchorEl] = createSignal<null | HTMLElement>(null)
|
||||||
|
const open = () => Boolean(anchorEl());
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader avatar={<Avatar src={""}/>} title={"test user"} subheader={"test@test"}
|
||||||
|
action={<IconButton onclick={(event) => {
|
||||||
|
setAnchorEl(event.currentTarget)
|
||||||
|
}}><MoreVert/><Menu disableScrollLock anchorEl={anchorEl()} open={open()} onClose={handleClose}><MenuItem
|
||||||
|
onclick={handleClose}>aaa</MenuItem></Menu> </IconButton>}/>
|
||||||
|
<CardContent>
|
||||||
|
<Typography>
|
||||||
|
{props.post.text}
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
<CardActions disableSpacing>
|
||||||
|
<IconButton>
|
||||||
|
<Reply/>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton>
|
||||||
|
<ScreenRotationAlt/>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton>
|
||||||
|
<Favorite/>
|
||||||
|
</IconButton>
|
||||||
|
<Box sx={{marginLeft: "auto"}}>
|
||||||
|
<Typography>{new Date(props.post.createdAt).toDateString()}</Typography>
|
||||||
|
</Box>
|
||||||
|
<Switch fallback={<Public/>}>
|
||||||
|
<Match when={props.post.visibility == "public"}>
|
||||||
|
<IconButton>
|
||||||
|
<Public/>
|
||||||
|
</IconButton>
|
||||||
|
</Match>
|
||||||
|
<Match when={props.post.visibility == "direct"}>
|
||||||
|
<IconButton>
|
||||||
|
<Mail/>
|
||||||
|
</IconButton>
|
||||||
|
</Match>
|
||||||
|
<Match when={props.post.visibility == "followers"}>
|
||||||
|
<IconButton>
|
||||||
|
<Lock/>
|
||||||
|
</IconButton>
|
||||||
|
</Match>
|
||||||
|
<Match when={props.post.visibility == "unlisted"}>
|
||||||
|
<IconButton>
|
||||||
|
<Home/>
|
||||||
|
</IconButton>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import {Component} from "solid-js";
|
||||||
|
import {Button, IconButton, Paper, Stack, TextField, Typography} from "@suid/material";
|
||||||
|
import {Avatar} from "../atoms/Avatar";
|
||||||
|
import {AddPhotoAlternate, Poll, Public} from "@suid/icons-material";
|
||||||
|
|
||||||
|
export const PostForm: Component<{ label: string }> = (props) => {
|
||||||
|
return (
|
||||||
|
<Paper>
|
||||||
|
<Stack>
|
||||||
|
<Stack direction={"row"} spacing={2} sx={{padding: 2}}>
|
||||||
|
<Avatar src={""}/>
|
||||||
|
<TextField label={props.label} multiline rows={4} variant={"standard"} fullWidth/>
|
||||||
|
</Stack>
|
||||||
|
<Stack direction={"row"} justifyContent={"space-between"} sx={{padding: 2}}>
|
||||||
|
<Stack direction={"row"} justifyContent={"flex-start"} alignItems={"center"}>
|
||||||
|
<IconButton>
|
||||||
|
<AddPhotoAlternate/>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton>
|
||||||
|
<Poll/>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton>
|
||||||
|
<Public/>
|
||||||
|
</IconButton>
|
||||||
|
</Stack>
|
||||||
|
<Stack direction={"row"} alignItems={"center"} spacing={2}>
|
||||||
|
<Typography>
|
||||||
|
aaa
|
||||||
|
</Typography>
|
||||||
|
<Button variant={"contained"}>
|
||||||
|
投稿する
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import {Component} from "solid-js";
|
||||||
|
import {MainPage} from "../templates/MainPage";
|
||||||
|
import {PostForm} from "../organisms/PostForm";
|
||||||
|
import {Stack} from "@suid/material";
|
||||||
|
import {Post} from "../organisms/Post";
|
||||||
|
import {PostResponse} from "../generated";
|
||||||
|
|
||||||
|
export const TopPage: Component = () => {
|
||||||
|
return (
|
||||||
|
<MainPage>
|
||||||
|
<Stack spacing={1}>
|
||||||
|
<PostForm label={"投稿する"}/>
|
||||||
|
<Post post={{
|
||||||
|
text: "テスト~",
|
||||||
|
sensitive: false,
|
||||||
|
apId: "https://example.com",
|
||||||
|
id: 1234,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
url: "https://example.com",
|
||||||
|
userId: 1234,
|
||||||
|
visibility: "public"
|
||||||
|
} as PostResponse}></Post>
|
||||||
|
<Post post={{
|
||||||
|
text: "テスト 公開範囲",
|
||||||
|
sensitive: false,
|
||||||
|
apId: "https://example.com",
|
||||||
|
id: 1234,
|
||||||
|
createdAt: 1234567,
|
||||||
|
url: "https://example.com",
|
||||||
|
userId: 1234,
|
||||||
|
visibility: "direct"
|
||||||
|
} as PostResponse}></Post>
|
||||||
|
<Post post={{
|
||||||
|
text: "テスト~",
|
||||||
|
sensitive: false,
|
||||||
|
apId: "https://example.com",
|
||||||
|
id: 1234,
|
||||||
|
createdAt: 1234567,
|
||||||
|
url: "https://example.com",
|
||||||
|
userId: 1234,
|
||||||
|
visibility: "unlisted"
|
||||||
|
} as PostResponse}></Post>
|
||||||
|
<Post post={{
|
||||||
|
text: "テスト~",
|
||||||
|
sensitive: false,
|
||||||
|
apId: "https://example.com",
|
||||||
|
id: 1234,
|
||||||
|
createdAt: 1234567,
|
||||||
|
url: "https://example.com",
|
||||||
|
userId: 1234,
|
||||||
|
visibility: "followers"
|
||||||
|
} as PostResponse}></Post>
|
||||||
|
</Stack>
|
||||||
|
</MainPage>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import {ParentComponent} from "solid-js";
|
||||||
|
import {Grid} from "@suid/material";
|
||||||
|
import {Sidebar} from "./Sidebar";
|
||||||
|
|
||||||
|
export const MainPage: ParentComponent = (props) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item xs={0} md={3}>
|
||||||
|
<Sidebar/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} md={5}>
|
||||||
|
{props.children}
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={0} md={3}>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import {Component} from "solid-js";
|
||||||
|
import {Stack} from "@suid/material";
|
||||||
|
|
||||||
|
export const Sidebar: Component = () => {
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue