Tailwind config and basic services

This commit is contained in:
Aslan 2025-12-31 17:15:14 +01:00
parent f1e90c4dd2
commit be6467cd2c
41 changed files with 581 additions and 102 deletions

View file

@ -1,5 +1,5 @@
<!doctype html>
<html lang="en">
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

View file

@ -0,0 +1,10 @@
import { callApi, HTTP } from "../tools";
import { IFetchCommunityRequest, IFetchCommunityResponse } from "./types";
const fetchCommunityApi = async (
request: IFetchCommunityRequest,
): Promise<IFetchCommunityResponse> => {
return await callApi(HTTP.GET, `community/${request.id}`);
};
export { fetchCommunityApi };

View file

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

View file

@ -0,0 +1,11 @@
interface IFetchCommunityRequest {
id: string;
}
interface IFetchCommunityResponse {
id: string;
name: string;
description: string;
}
export { type IFetchCommunityRequest, type IFetchCommunityResponse };

View file

@ -1,12 +0,0 @@
import { callApi, HTTP } from "../tools";
import { IGameTimeResponse } from "./types";
async function fetchTimeApi(): Promise<IGameTimeResponse> {
const data = await callApi(HTTP.GET, "game/time");
return {
time: data,
};
}
export { fetchTimeApi };

View file

@ -1,5 +0,0 @@
interface IGameTimeResponse {
time: number;
}
export { type IGameTimeResponse };

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

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

37
src/api/user/types.ts Normal file
View file

@ -0,0 +1,37 @@
interface IFetchLoggedUserResponse {
id: string;
}
interface IFetchUserRequest {
id: string;
}
interface IFetchUserResponse {
id: string;
username: string;
description: string;
}
interface IFetchUserCommunitiesRequest {
id: string;
}
interface IFetchUserCommunitiesResponse {
id: string;
communities: IFetchUserCommunity[];
}
interface IFetchUserCommunity {
id: string;
name: string;
description: string;
}
export {
type IFetchLoggedUserResponse,
type IFetchUserRequest,
type IFetchUserResponse,
type IFetchUserCommunitiesRequest,
type IFetchUserCommunitiesResponse,
type IFetchUserCommunity,
};

26
src/api/user/user.ts Normal file
View file

@ -0,0 +1,26 @@
import { callApi, HTTP } from "../tools";
import {
IFetchLoggedUserResponse,
IFetchUserRequest,
IFetchUserResponse,
IFetchUserCommunitiesRequest,
IFetchUserCommunitiesResponse,
} from "./types";
const fetchLoggedUserApi = async (): Promise<IFetchLoggedUserResponse> => {
return await callApi(HTTP.GET, `user/logged`);
};
const fetchUserApi = async (
request: IFetchUserRequest,
): Promise<IFetchUserResponse> => {
return await callApi(HTTP.GET, `user/${request.id}`);
};
const fetchUserCommunitiesApi = async (
request: IFetchUserCommunitiesRequest,
): Promise<IFetchUserCommunitiesResponse> => {
return await callApi(HTTP.GET, `user/${request.id}/communities`);
};
export { fetchLoggedUserApi, fetchUserApi, fetchUserCommunitiesApi };

View file

@ -1,7 +1,11 @@
import type { Component } from "solid-js";
const ChannelBar: Component = () => {
return <div class="bg-stone-800 h-16 shadow-bar z-10"></div>;
return (
<div class="absolute w-full top-0 z-10">
<div class="bg-stone-800/25 backdrop-blur-md h-16 w-full shadow-bar p-2"></div>
</div>
);
};
export default ChannelBar;

View file

@ -1,7 +1,17 @@
import type { Component } from "solid-js";
import { ICommunityProps } from "./types";
const Community: Component = () => {
return <div></div>;
const Community: Component<ICommunityProps> = (props: ICommunityProps) => {
return (
<div
class="avatar cursor-pointer"
onClick={() => props.onCommunityClick(props.id)}
>
<div class="w-full rounded-full">
<img src={props.avatar} />
</div>
</div>
);
};
export default Community;

View file

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

View file

@ -0,0 +1,8 @@
interface ICommunityProps {
id: string;
name: string;
avatar: string;
onCommunityClick: (id: string) => void;
}
export { type ICommunityProps };

View file

@ -9,7 +9,7 @@ const Message: Component<IMessageProps> = (props: IMessageProps) => {
onClick={() => props.onProfileClick(props.userId)}
>
<div class="w-10 rounded-full">
<img src={props.profileImage} />
<img src={props.avatar} />
</div>
</div>
<div>

View file

@ -3,7 +3,7 @@ interface IMessageProps {
message: string;
userId: string;
username: string;
profileImage: string;
avatar: string;
onProfileClick: (userId: string) => void;
}

View file

@ -2,18 +2,14 @@ import type { Component } from "solid-js";
const MessageBar: Component = () => {
return (
<div class="bg-stone-800 h-16 shadow-bar z-10 p-2">
<div class="join w-full h-full">
<div class="w-full h-full">
<label class="input validator join-item w-full h-full">
<input
type="text"
placeholder="Write a text message..."
required
/>
</label>
</div>
<button class="btn btn-neutral join-item h-full">Send</button>
<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">
Send
</button>
</div>
</div>
);

View file

@ -0,0 +1,16 @@
import { fetchCommunityApi } from "../../api/community";
import { CommunityActionTypes } from "../../store/community";
import { dispatch } from "../../store/state";
const fetchCommunity = async (id: string) => {
const data = await fetchCommunityApi({
id: id,
});
dispatch({
type: CommunityActionTypes.FETCH_COMMUNITY_FINISH,
payload: data,
});
};
export { fetchCommunity };

View file

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

View file

@ -1,13 +0,0 @@
import { fetchTimeApi } from "../../api/game/game";
import { dispatch } from "../../store/state";
async function fetchTime() {
const data = await fetchTimeApi();
dispatch({
type: "FETCH_TIME_FINISH",
payload: data.time,
});
}
export { fetchTime };

View file

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

48
src/services/user/user.ts Normal file
View file

@ -0,0 +1,48 @@
import {
fetchLoggedUserApi,
fetchUserApi,
fetchUserCommunitiesApi,
} from "../../api/user";
import { UserActionTypes } from "../../store/user";
import { CommunityActionTypes } from "../../store/community";
import { dispatch } from "../../store/state";
const fetchLoggedUser = async () => {
const data = await fetchLoggedUserApi();
dispatch({
type: UserActionTypes.FETCH_LOGGED_USER_ID_FINISH,
payload: data,
});
};
const fetchUser = async (id: string) => {
const data = await fetchUserApi({
id: id,
});
dispatch({
type: UserActionTypes.FETCH_USER_FINISH,
payload: data,
});
};
const fetchUserCommunities = async (id: string) => {
const data = await fetchUserCommunitiesApi({
id: id,
});
dispatch({
type: UserActionTypes.FETCH_USER_COMMUNITIES_FINISH,
payload: data,
});
data.communities.forEach((community) => {
dispatch({
type: CommunityActionTypes.SET_COMMUNITY,
payload: community,
});
});
};
export { fetchLoggedUser, fetchUser, fetchUserCommunities };

View file

@ -1,7 +1,8 @@
import { GameAction, GameActionTypes } from "./game";
import { UserActionTypes, UserAction } from "./user";
import { CommunityActionTypes, CommunityAction } from "./community";
type ActionTypes = GameActionTypes;
type ActionTypes = UserActionTypes | CommunityActionTypes;
type Action = GameAction;
type Action = UserAction | CommunityAction;
export { type Action, type ActionTypes };

View file

@ -0,0 +1,21 @@
import { IFetchCommunityResponse } from "../../api/community";
import { IFetchUserCommunity } from "../../api/user";
enum CommunityActionTypes {
FETCH_COMMUNITY_START = "FETCH_COMMUNITY_START",
FETCH_COMMUNITY_FINISH = "FETCH_COMMUNITY_FINISH",
SET_COMMUNITY = "SET_COMMUNITY",
}
type CommunityAction =
| { type: CommunityActionTypes.FETCH_COMMUNITY_START; payload: string }
| {
type: CommunityActionTypes.FETCH_COMMUNITY_FINISH;
payload: IFetchCommunityResponse;
}
| {
type: CommunityActionTypes.SET_COMMUNITY;
payload: IFetchUserCommunity;
};
export { CommunityActionTypes, type CommunityAction };

View file

@ -0,0 +1,34 @@
import { fetchCommunity } from "../../services/community";
import { CommunityActionTypes, CommunityAction } from "./actions";
import { ICommunityState } from "./types";
function communityReducer(
state: ICommunityState,
action: CommunityAction,
): ICommunityState {
switch (action.type) {
case CommunityActionTypes.FETCH_COMMUNITY_START:
fetchCommunity(action.payload);
return state;
case CommunityActionTypes.FETCH_COMMUNITY_FINISH:
return {
...state,
communities: {
...state.communities,
[action.payload.id]: action.payload,
},
};
case CommunityActionTypes.SET_COMMUNITY:
return {
...state,
communities: {
...state.communities,
[action.payload.id]: action.payload,
},
};
default:
return state;
}
}
export { communityReducer };

View file

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

View file

@ -0,0 +1,11 @@
interface ICommunityState {
communities: Record<string, ICommunity>;
}
interface ICommunity {
id: string;
name?: string;
description?: string;
}
export { type ICommunityState, type ICommunity };

View file

@ -1,10 +0,0 @@
type FETCH_TIME_START = "FETCH_TIME_START";
type FETCH_TIME_FINISH = "FETCH_TIME_FINISH";
type GameActionTypes = FETCH_TIME_START | FETCH_TIME_FINISH;
type GameAction =
| { type: FETCH_TIME_START }
| { type: FETCH_TIME_FINISH; payload: number };
export { type GameActionTypes, type GameAction };

View file

@ -1,17 +0,0 @@
import { fetchTime } from "../../services/game/game";
import { GameAction } from "./actions";
import { IGameState } from "./types";
function gameReducer(state: IGameState, action: GameAction): IGameState {
switch (action.type) {
case "FETCH_TIME_START":
fetchTime();
return { ...state };
case "FETCH_TIME_FINISH":
return { ...state, time: action.payload };
default:
return state;
}
}
export { gameReducer };

View file

@ -1,5 +0,0 @@
interface IGameState {
time: number;
}
export { type IGameState };

View file

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

View file

@ -4,8 +4,12 @@ import { Action } from "./actions";
import { reducer } from "./reducers";
const [state, setState] = createStore<IState>({
game: {
time: 0,
user: {
loggedUserId: undefined,
users: {},
},
community: {
communities: {},
},
});

View file

@ -1,7 +1,9 @@
import { IGameState } from "./game";
import { IUserState } from "./user";
import { ICommunityState } from "./community";
interface IState {
game: IGameState;
user: IUserState;
community: ICommunityState;
}
export { type IState };

30
src/store/user/actions.ts Normal file
View file

@ -0,0 +1,30 @@
import {
IFetchLoggedUserResponse,
IFetchUserResponse,
IFetchUserCommunitiesResponse,
} from "../../api/user";
enum UserActionTypes {
FETCH_LOGGED_USER_ID_START = "FETCH_LOGGED_USER_ID_START",
FETCH_LOGGED_USER_ID_FINISH = "FETCH_LOGGED_USER_ID_FINISH",
FETCH_USER_START = "FETCH_USER_START",
FETCH_USER_FINISH = "FETCH_USER_FINISH",
FETCH_USER_COMMUNITIES_START = "FETCH_USER_COMMUNITIES_START",
FETCH_USER_COMMUNITIES_FINISH = "FETCH_USER_COMMUNITIES_FINISH",
}
type UserAction =
| { type: UserActionTypes.FETCH_LOGGED_USER_ID_START }
| {
type: UserActionTypes.FETCH_LOGGED_USER_ID_FINISH;
payload: IFetchLoggedUserResponse;
}
| { type: UserActionTypes.FETCH_USER_START; payload: string }
| { type: UserActionTypes.FETCH_USER_FINISH; payload: IFetchUserResponse }
| { type: UserActionTypes.FETCH_USER_COMMUNITIES_START; payload: string }
| {
type: UserActionTypes.FETCH_USER_COMMUNITIES_FINISH;
payload: IFetchUserCommunitiesResponse;
};
export { UserActionTypes, type UserAction };

View file

@ -1,3 +1,3 @@
export * from "./game";
export * from "./user";
export * from "./actions";
export * from "./types";

13
src/store/user/types.ts Normal file
View file

@ -0,0 +1,13 @@
interface IUserState {
loggedUserId?: string;
users: Record<string, IUser>;
}
interface IUser {
id: string;
username?: string;
description?: string;
communities?: string[];
}
export { type IUserState, type IUser };

51
src/store/user/user.ts Normal file
View file

@ -0,0 +1,51 @@
import {
fetchLoggedUser,
fetchUser,
fetchUserCommunities,
} from "../../services/user";
import { UserActionTypes, UserAction } from "./actions";
import { IUserState } from "./types";
function userReducer(state: IUserState, action: UserAction): IUserState {
switch (action.type) {
case UserActionTypes.FETCH_LOGGED_USER_ID_START:
fetchLoggedUser();
return state;
case UserActionTypes.FETCH_LOGGED_USER_ID_FINISH:
return {
...state,
loggedUserId: action.payload.id,
};
case UserActionTypes.FETCH_USER_START:
fetchUser(action.payload);
return state;
case UserActionTypes.FETCH_USER_FINISH:
return {
...state,
users: {
...state.users,
[action.payload.id]: action.payload,
},
};
case UserActionTypes.FETCH_USER_COMMUNITIES_START:
fetchUserCommunities(action.payload);
return state;
case UserActionTypes.FETCH_USER_COMMUNITIES_FINISH:
return {
...state,
users: {
...state.users,
[action.payload.id]: {
id: action.payload.id,
communities: action.payload.communities.map(
(community) => community.id,
),
},
},
};
default:
return state;
}
}
export { userReducer };

View file

@ -1,7 +1,13 @@
import type { Component } from "solid-js";
import CommunityView from "../CommunityView/CommunityView";
const ChannelView: Component = () => {
return <div class="bg-stone-900 w-64 shadow-panel z-20"></div>;
return (
<div class="bg-stone-900 w-96 shadow-panel z-20">
<CommunityView />
<div></div>
</div>
);
};
export default ChannelView;

View file

@ -1,4 +1,4 @@
import type { Component } from "solid-js";
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";
@ -10,40 +10,197 @@ const ChatView: Component = () => {
message: "Hello this is a test message",
userId: "12121",
username: "Aslan",
image: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432433",
message: "Hi hi",
userId: "12122",
username: "TestUser",
image: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
image: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
messageId: "432434",
message: "Nooooo",
userId: "12121",
username: "Aslan",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
];
let scrollRef: HTMLUListElement | undefined;
onMount(() => {
if (scrollRef) {
scrollRef.scrollTop = scrollRef.scrollHeight;
}
});
const onProfileClick = (userId: string) => {
console.log(userId);
};
return (
<div class="bg-stone-800 flex-1 z-0">
<div class="flex flex-col h-full">
<div class="bg-stone-800 flex-1 z-0 relative">
<div class="h-full">
<ChannelBar />
<ul class="flex-1 h-full list flex flex-col justify-end p-2">
<ul
ref={scrollRef}
class="h-full list flex flex-col p-2 pt-16 pb-24 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-500 scrollbar-track-gray-800"
>
{testMessages.map((msg) => (
<Message
messageId={msg.messageId}
message={msg.message}
userId={msg.userId}
username={msg.username}
profileImage={msg.image}
avatar={msg.avatar}
onProfileClick={onProfileClick}
/>
))}

View file

@ -1,7 +1,38 @@
import type { Component } from "solid-js";
import Community from "../../components/Community/Community";
const CommunityView: Component = () => {
return <div class="bg-stone-950 w-20 shadow-panel z-30"></div>;
const communitiesTest = [
{
id: "21",
communityName: "Community 1",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
{
id: "22",
communityName: "Gamer's Lair",
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
},
];
const onCommunityClick = (id: string) => {
console.log(id);
};
return (
<div class="p-3 h-full">
<div class="bg-stone-950 w-20 h-full rounded-full shadow-panel z-30 flex flex-col p-3 gap-3">
{communitiesTest.map((community) => (
<Community
id={community.id}
name={community.communityName}
avatar={community.avatar}
onCommunityClick={onCommunityClick}
/>
))}
</div>
</div>
);
};
export default CommunityView;

View file

@ -1,5 +1,4 @@
import type { Component } from "solid-js";
import CommunityView from "../CommunityView/CommunityView";
import ChannelView from "../ChannelView/ChannelView";
import ChatView from "../ChatView/ChatView";
import MemberView from "../MemberView/MemberView";
@ -7,7 +6,6 @@ import MemberView from "../MemberView/MemberView";
const MainView: Component = () => {
return (
<div class="flex flex-row h-screen">
<CommunityView />
<ChannelView />
<ChatView />
<MemberView />

4
tailwind.config.js Normal file
View file

@ -0,0 +1,4 @@
module.exports = {
plugins: [require("daisyui"), require("tailwind-scrollbar")],
daisyui: { themes: ["dark"] },
};