Add basic services; Base layout

This commit is contained in:
Aslan 2026-01-01 17:21:56 +01:00
parent be6467cd2c
commit ef526cd2db
30 changed files with 291 additions and 34 deletions

View file

@ -1,13 +1,15 @@
import { Route, Router } from "@solidjs/router";
import type { Component } from "solid-js";
import MainView from "./views/MainView/MainView";
import SettingsView from "./views/SettingsView/SettingsView";
import { MainView } from "./views/MainView";
import { SettingsView } from "./views/SettingsView";
import { LoginView } from "./views/LoginView";
const App: Component = () => {
return (
<Router>
<Route path="/" component={MainView} />
<Route path="/settings" component={SettingsView} />
<Route path="/login" component={LoginView} />
</Router>
);
};

18
src/api/auth/auth.ts Normal file
View file

@ -0,0 +1,18 @@
import { callApi, HTTP } from "../tools";
import {
IFetchLoginRequest,
IFetchLoginResponse,
IFetchRefreshResponse,
} from "./types";
const fetchLoginApi = async (
request: IFetchLoginRequest,
): Promise<IFetchLoginResponse> => {
return await callApi(HTTP.POST, `auth/login`, request);
};
const fetchRefreshApi = async (): Promise<IFetchRefreshResponse> => {
return await callApi(HTTP.GET_REFRESH, `auth/refresh`);
};
export { fetchLoginApi, fetchRefreshApi };

2
src/api/auth/index.ts Normal file
View file

@ -0,0 +1,2 @@
export * from "./auth";
export * from "./types";

21
src/api/auth/types.ts Normal file
View file

@ -0,0 +1,21 @@
interface IFetchLoginRequest {
username: string;
password: string;
}
interface IFetchLoginResponse {
id: string;
ownerId: string;
}
interface IFetchRefreshResponse {
id: string;
ownerId: string;
token: string;
}
export {
type IFetchLoginRequest,
type IFetchLoginResponse,
type IFetchRefreshResponse,
};

View file

@ -1,5 +1,6 @@
{
"schema": "http",
"url": "localhost",
"port": 3012
"port": 3012,
"path": "api/v1"
}

View file

@ -1,31 +1,75 @@
import { state } from "../store/state";
import config from "./config.json";
enum HTTP {
GET_REFRESH,
GET,
POST,
PATCH,
DELETE,
}
async function callApi(type: HTTP, path: string, body?: any) {
async function callApi(type: HTTP, path: string, body?: object) {
let response: Response;
switch (type) {
case HTTP.GET:
case HTTP.GET_REFRESH:
response = await fetch(
`${config.schema}://${config.url}:${config.port}/${path}`,
`${config.schema}://${config.url}:${config.port}/${config.path}/${path}`,
{
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
},
);
break;
case HTTP.GET:
response = await fetch(
`${config.schema}://${config.url}:${config.port}/${config.path}/${path}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${state.auth.session?.token}`,
},
},
);
break;
case HTTP.POST:
response = await fetch(
`${config.schema}://${config.url}:${config.port}/${path}`,
`${config.schema}://${config.url}:${config.port}/${config.path}/${path}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${state.auth.session?.token}`,
},
body: JSON.stringify(body ?? {}),
},
);
break;
case HTTP.PATCH:
response = await fetch(
`${config.schema}://${config.url}:${config.port}/${config.path}/${path}`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${state.auth.session?.token}`,
},
body: JSON.stringify(body ?? {}),
},
);
break;
case HTTP.DELETE:
response = await fetch(
`${config.schema}://${config.url}:${config.port}/${config.path}/${path}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${state.auth.session?.token}`,
},
body: JSON.stringify(body ?? {}),
},

View file

@ -4,4 +4,4 @@ const Channel: Component = () => {
return <div></div>;
};
export default Channel;
export { Channel };

View file

@ -8,4 +8,4 @@ const ChannelBar: Component = () => {
);
};
export default ChannelBar;
export { ChannelBar };

View file

@ -14,4 +14,4 @@ const Community: Component<ICommunityProps> = (props: ICommunityProps) => {
);
};
export default Community;
export { Community };

View file

@ -4,4 +4,4 @@ const Member: Component = () => {
return <div></div>;
};
export default Member;
export { Member };

View file

@ -20,4 +20,4 @@ const Message: Component<IMessageProps> = (props: IMessageProps) => {
);
};
export default Message;
export { Message };

View file

@ -1,13 +1,36 @@
import type { Component } from "solid-js";
import { dispatch } from "../../store/state";
import { UserActionTypes } from "../../store/user";
import { AuthActionTypes } from "../../store/auth";
const MessageBar: Component = () => {
const onRefresh = () => {
dispatch({
type: AuthActionTypes.FETCH_REFRESH_START,
});
};
const onSend = () => {
dispatch({
type: UserActionTypes.FETCH_LOGGED_USER_ID_START,
});
};
return (
<div class="absolute w-full bottom-0 p-4 z-10">
<div class="bg-stone-800/25 backdrop-blur-lg h-16 shadow-bar p-2 flex flex-row gap-2 rounded-full">
<label class="bg-stone-800/50 backdrop-blur-lg input w-full h-full rounded-full focus:border-none outline-none">
<input type="text" placeholder="Send a message..." />
</label>
<button class="bg-black/50 backdrop-blur-lg btn btn-neutral h-full rounded-full">
<button
class="bg-black/50 backdrop-blur-lg btn btn-neutral h-full rounded-full"
onClick={onRefresh}
>
Refresh test
</button>
<button
class="bg-black/50 backdrop-blur-lg btn btn-neutral h-full rounded-full"
onClick={onSend}
>
Send
</button>
</div>
@ -15,4 +38,4 @@ const MessageBar: Component = () => {
);
};
export default MessageBar;
export { MessageBar };

26
src/services/auth/auth.ts Normal file
View file

@ -0,0 +1,26 @@
import { fetchLoginApi, fetchRefreshApi } from "../../api/auth";
import { AuthActionTypes } from "../../store/auth";
import { dispatch } from "../../store/state";
const fetchLogin = async (username: string, password: string) => {
const data = await fetchLoginApi({
username: username,
password: password,
});
dispatch({
type: AuthActionTypes.FETCH_LOGIN_FINISH,
payload: data,
});
};
const fetchRefresh = async () => {
const data = await fetchRefreshApi();
dispatch({
type: AuthActionTypes.FETCH_REFRESH_FINISH,
payload: data,
});
};
export { fetchLogin, fetchRefresh };

View file

@ -0,0 +1 @@
export * from "./auth";

View file

@ -1,8 +1,9 @@
import { AuthActionTypes, AuthAction } from "./auth";
import { UserActionTypes, UserAction } from "./user";
import { CommunityActionTypes, CommunityAction } from "./community";
type ActionTypes = UserActionTypes | CommunityActionTypes;
type ActionTypes = AuthActionTypes | UserActionTypes | CommunityActionTypes;
type Action = UserAction | CommunityAction;
type Action = AuthAction | UserAction | CommunityAction;
export { type Action, type ActionTypes };

26
src/store/auth/actions.ts Normal file
View file

@ -0,0 +1,26 @@
import {
IFetchLoginRequest,
IFetchLoginResponse,
IFetchRefreshResponse,
} from "../../api/auth";
enum AuthActionTypes {
FETCH_LOGIN_START = "FETCH_LOGIN_START",
FETCH_LOGIN_FINISH = "FETCH_LOGIN_FINISH",
FETCH_REFRESH_START = "FETCH_REFRESH_START",
FETCH_REFRESH_FINISH = "FETCH_REFRESH_FINISH",
}
type AuthAction =
| { type: AuthActionTypes.FETCH_LOGIN_START; payload: IFetchLoginRequest }
| {
type: AuthActionTypes.FETCH_LOGIN_FINISH;
payload: IFetchLoginResponse;
}
| { type: AuthActionTypes.FETCH_REFRESH_START }
| {
type: AuthActionTypes.FETCH_REFRESH_FINISH;
payload: IFetchRefreshResponse;
};
export { AuthActionTypes, type AuthAction };

28
src/store/auth/auth.ts Normal file
View file

@ -0,0 +1,28 @@
import { fetchLogin, fetchRefresh } from "../../services/auth";
import { AuthActionTypes, AuthAction } from "./actions";
import { IAuthState } from "./types";
function authReducer(state: IAuthState, action: AuthAction): IAuthState {
switch (action.type) {
case AuthActionTypes.FETCH_LOGIN_START:
fetchLogin(action.payload.username, action.payload.password);
return state;
case AuthActionTypes.FETCH_LOGIN_FINISH:
return {
...state,
session: action.payload,
};
case AuthActionTypes.FETCH_REFRESH_START:
fetchRefresh();
return state;
case AuthActionTypes.FETCH_REFRESH_FINISH:
return {
...state,
session: action.payload,
};
default:
return state;
}
}
export { authReducer };

3
src/store/auth/index.ts Normal file
View file

@ -0,0 +1,3 @@
export * from "./auth";
export * from "./actions";
export * from "./types";

11
src/store/auth/types.ts Normal file
View file

@ -0,0 +1,11 @@
interface IAuthState {
session?: ISession;
}
interface ISession {
id?: string;
ownerId?: string;
token?: string;
}
export { type IAuthState, type ISession };

View file

@ -1,10 +1,12 @@
import { IState } from "./types";
import { Action } from "./actions";
import { AuthAction, authReducer } from "./auth";
import { UserAction, userReducer } from "./user";
import { CommunityAction, communityReducer } from "./community";
function reducer(state: IState, action: Action): IState {
return {
auth: authReducer(state.auth, action as AuthAction),
user: userReducer(state.user, action as UserAction),
community: communityReducer(state.community, action as CommunityAction),
};

View file

@ -4,6 +4,9 @@ import { Action } from "./actions";
import { reducer } from "./reducers";
const [state, setState] = createStore<IState>({
auth: {
session: undefined,
},
user: {
loggedUserId: undefined,
users: {},

View file

@ -1,7 +1,9 @@
import { IAuthState } from "./auth";
import { IUserState } from "./user";
import { ICommunityState } from "./community";
interface IState {
auth: IAuthState;
user: IUserState;
community: ICommunityState;
}

View file

@ -1,5 +1,5 @@
import type { Component } from "solid-js";
import CommunityView from "../CommunityView/CommunityView";
import { CommunityView } from "../CommunityView";
const ChannelView: Component = () => {
return (
@ -10,4 +10,4 @@ const ChannelView: Component = () => {
);
};
export default ChannelView;
export { ChannelView };

View file

@ -1,7 +1,7 @@
import { onMount, type Component } from "solid-js";
import ChannelBar from "../../components/ChannelBar/ChannelBar";
import MessageBar from "../../components/MessageBar/MessageBar";
import Message from "../../components/Message/Message";
import { ChannelBar } from "../../components/ChannelBar";
import { MessageBar } from "../../components/MessageBar";
import { Message } from "../../components/Message";
const ChatView: Component = () => {
const testMessages = [
@ -211,4 +211,4 @@ const ChatView: Component = () => {
);
};
export default ChatView;
export { ChatView };

View file

@ -1,5 +1,5 @@
import type { Component } from "solid-js";
import Community from "../../components/Community/Community";
import { Community } from "../../components/Community";
const CommunityView: Component = () => {
const communitiesTest = [
@ -35,4 +35,4 @@ const CommunityView: Component = () => {
);
};
export default CommunityView;
export { CommunityView };

View file

@ -1,7 +1,50 @@
import type { Component } from "solid-js";
import { createSignal, type Component } from "solid-js";
import { dispatch } from "../../store/state";
import { AuthActionTypes } from "../../store/auth";
const LoginView: Component = () => {
return <div></div>;
const [getUsername, setUsername] = createSignal("");
const [getPassword, setPassword] = createSignal("");
const onLogin = () => {
dispatch({
type: AuthActionTypes.FETCH_LOGIN_START,
payload: {
username: getUsername(),
password: getPassword(),
},
});
};
export default LoginView;
return (
<div>
<fieldset class="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4 mx-auto mt-32 lg:mt-64">
<legend class="fieldset-legend">Login</legend>
<label class="label">Username</label>
<input
type="email"
class="input"
placeholder="Username"
value={getUsername()}
onInput={(e) => setUsername(e.target.value)}
/>
<label class="label">Password</label>
<input
type="password"
class="input"
placeholder="Password"
value={getPassword()}
onInput={(e) => setPassword(e.target.value)}
/>
<button class="btn btn-neutral mt-4" onClick={onLogin}>
Login
</button>
</fieldset>
</div>
);
};
export { LoginView };

View file

@ -1,7 +1,7 @@
import type { Component } from "solid-js";
import ChannelView from "../ChannelView/ChannelView";
import ChatView from "../ChatView/ChatView";
import MemberView from "../MemberView/MemberView";
import { ChannelView } from "../ChannelView";
import { ChatView } from "../ChatView";
import { MemberView } from "../MemberView";
const MainView: Component = () => {
return (
@ -13,4 +13,4 @@ const MainView: Component = () => {
);
};
export default MainView;
export { MainView };

View file

@ -4,4 +4,4 @@ const MemberView: Component = () => {
return <div class="bg-stone-900 w-64 shadow-panel z-20"></div>;
};
export default MemberView;
export { MemberView };

View file

@ -4,4 +4,4 @@ const RegisterView: Component = () => {
return <div></div>;
};
export default RegisterView;
export { RegisterView };

View file

@ -4,4 +4,4 @@ const SettingsView: Component = () => {
return <div></div>;
};
export default SettingsView;
export { SettingsView };