Invite and Session API/Services; Home view; Sidebar Items; Modals

This commit is contained in:
Aslan 2026-01-10 19:24:10 -05:00
parent 280158470a
commit e36587b99d
80 changed files with 1343 additions and 71 deletions

View file

@ -31,7 +31,7 @@ const updateChannelApi = async (
const removeChannelApi = async ( const removeChannelApi = async (
request: IRemoveChannelRequest, request: IRemoveChannelRequest,
): Promise<IRemoveChannelResponse> => { ): Promise<IRemoveChannelResponse> => {
return await callApi(HTTP.DELETE, `channel/${request.id}`, request); return await callApi(HTTP.DELETE, `channel/${request.id}`);
}; };
export { export {

View file

@ -22,6 +22,7 @@ interface ICreateChannelResponse extends IFetchChannel {}
interface IUpdateChannelRequest { interface IUpdateChannelRequest {
id: string; id: string;
name?: string; name?: string;
description?: string;
} }
interface IUpdateChannelResponse extends IFetchChannel {} interface IUpdateChannelResponse extends IFetchChannel {}

View file

@ -2,12 +2,20 @@ import { callApi, HTTP } from "../tools";
import { import {
IFetchCommunityRequest, IFetchCommunityRequest,
IFetchCommunityResponse, IFetchCommunityResponse,
ICreateCommunityRequest,
ICreateCommunityResponse,
IUpdateCommunityRequest,
IUpdateCommunityResponse,
IRemoveCommunityRequest,
IRemoveCommunityResponse,
IFetchCommunityChannelsRequest, IFetchCommunityChannelsRequest,
IFetchCommunityChannelsResponse, IFetchCommunityChannelsResponse,
IFetchCommunityRolesRequest, IFetchCommunityRolesRequest,
IFetchCommunityRolesResponse, IFetchCommunityRolesResponse,
IFetchCommunityMembersRequest, IFetchCommunityMembersRequest,
IFetchCommunityMembersResponse, IFetchCommunityMembersResponse,
IFetchCommunityInvitesRequest,
IFetchCommunityInvitesResponse,
} from "./types"; } from "./types";
const fetchCommunityApi = async ( const fetchCommunityApi = async (
@ -16,6 +24,24 @@ const fetchCommunityApi = async (
return await callApi(HTTP.GET, `community/${request.id}`); return await callApi(HTTP.GET, `community/${request.id}`);
}; };
const createCommunityApi = async (
request: ICreateCommunityRequest,
): Promise<ICreateCommunityResponse> => {
return await callApi(HTTP.POST, `community`, request);
};
const updateCommunityApi = async (
request: IUpdateCommunityRequest,
): Promise<IUpdateCommunityResponse> => {
return await callApi(HTTP.PATCH, `community/${request.id}`, request);
};
const removeCommunityApi = async (
request: IRemoveCommunityRequest,
): Promise<IRemoveCommunityResponse> => {
return await callApi(HTTP.DELETE, `community/${request.id}`);
};
const fetchCommunityChannelsApi = async ( const fetchCommunityChannelsApi = async (
request: IFetchCommunityChannelsRequest, request: IFetchCommunityChannelsRequest,
): Promise<IFetchCommunityChannelsResponse> => { ): Promise<IFetchCommunityChannelsResponse> => {
@ -34,9 +60,19 @@ const fetchCommunityMembersApi = async (
return await callApi(HTTP.GET, `community/${request.id}/members`); return await callApi(HTTP.GET, `community/${request.id}/members`);
}; };
const fetchCommunityInvitesApi = async (
request: IFetchCommunityInvitesRequest,
): Promise<IFetchCommunityInvitesResponse> => {
return await callApi(HTTP.GET, `community/${request.id}/invites`);
};
export { export {
fetchCommunityApi, fetchCommunityApi,
createCommunityApi,
updateCommunityApi,
removeCommunityApi,
fetchCommunityChannelsApi, fetchCommunityChannelsApi,
fetchCommunityRolesApi, fetchCommunityRolesApi,
fetchCommunityMembersApi, fetchCommunityMembersApi,
fetchCommunityInvitesApi,
}; };

View file

@ -12,6 +12,28 @@ interface IFetchCommunityRequest {
interface IFetchCommunityResponse extends IFetchCommunity {} interface IFetchCommunityResponse extends IFetchCommunity {}
interface ICreateCommunityRequest {
name: string;
}
interface ICreateCommunityResponse extends IFetchCommunity {}
interface IUpdateCommunityRequest {
id: string;
name?: string;
description?: string;
}
interface IUpdateCommunityResponse extends IFetchCommunity {}
interface IRemoveCommunityRequest {
id: string;
}
interface IRemoveCommunityResponse {
id: string;
}
interface IFetchCommunityChannelsRequest { interface IFetchCommunityChannelsRequest {
id: string; id: string;
} }
@ -54,10 +76,29 @@ interface IFetchCommunityMember {
username: string; username: string;
} }
interface IFetchCommunityInvitesRequest {
id: string;
}
interface IFetchCommunityInvitesResponse {
id: string;
invites: IFetchCommunityInvite[];
}
interface IFetchCommunityInvite {
id: string;
}
export { export {
type IFetchCommunity, type IFetchCommunity,
type IFetchCommunityRequest, type IFetchCommunityRequest,
type IFetchCommunityResponse, type IFetchCommunityResponse,
type ICreateCommunityRequest,
type ICreateCommunityResponse,
type IUpdateCommunityRequest,
type IUpdateCommunityResponse,
type IRemoveCommunityRequest,
type IRemoveCommunityResponse,
type IFetchCommunityChannelsRequest, type IFetchCommunityChannelsRequest,
type IFetchCommunityChannelsResponse, type IFetchCommunityChannelsResponse,
type IFetchCommunityChannel, type IFetchCommunityChannel,
@ -67,4 +108,7 @@ export {
type IFetchCommunityMembersRequest, type IFetchCommunityMembersRequest,
type IFetchCommunityMembersResponse, type IFetchCommunityMembersResponse,
type IFetchCommunityMember, type IFetchCommunityMember,
type IFetchCommunityInvitesRequest,
type IFetchCommunityInvitesResponse,
type IFetchCommunityInvite,
}; };

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

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

29
src/api/invite/invite.ts Normal file
View file

@ -0,0 +1,29 @@
import { callApi, HTTP } from "../tools";
import {
IFetchInviteRequest,
IFetchInviteResponse,
IRemoveInviteRequest,
IRemoveInviteResponse,
IAcceptInviteRequest,
IAcceptInviteResponse,
} from "./types";
const fetchInviteApi = async (
request: IFetchInviteRequest,
): Promise<IFetchInviteResponse> => {
return await callApi(HTTP.GET, `invite/${request.id}`);
};
const removeInviteApi = async (
request: IRemoveInviteRequest,
): Promise<IRemoveInviteResponse> => {
return await callApi(HTTP.DELETE, `invite/${request.id}`);
};
const acceptInviteApi = async (
request: IAcceptInviteRequest,
): Promise<IAcceptInviteResponse> => {
return await callApi(HTTP.GET, `invite/${request.id}/accept`);
};
export { fetchInviteApi, removeInviteApi, acceptInviteApi };

48
src/api/invite/types.ts Normal file
View file

@ -0,0 +1,48 @@
interface IFetchInvite {
id: string;
communityId: string;
valid: boolean;
unlimitedInvites: boolean;
hasExpiration: boolean;
totalInvites: number;
remainingInvites: number;
creationDate: number;
expirationDate: number;
}
interface IFetchInviteRequest {
id: string;
}
interface IFetchInviteResponse extends IFetchInvite {}
interface IRemoveInviteRequest {
id: string;
}
interface IRemoveInviteResponse {
id: string;
userId: string;
}
interface IAcceptInviteRequest {
id: string;
}
interface IAcceptInviteResponse {
id: string;
userId: string;
userName: string;
communityId: string;
communityName: string;
}
export {
type IFetchInvite,
type IFetchInviteRequest,
type IFetchInviteResponse,
type IRemoveInviteRequest,
type IRemoveInviteResponse,
type IAcceptInviteRequest,
type IAcceptInviteResponse,
};

View file

@ -31,7 +31,7 @@ const updateRoleApi = async (
const removeRoleApi = async ( const removeRoleApi = async (
request: IRemoveRoleRequest, request: IRemoveRoleRequest,
): Promise<IRemoveRoleResponse> => { ): Promise<IRemoveRoleResponse> => {
return await callApi(HTTP.DELETE, `role/${request.id}`, request); return await callApi(HTTP.DELETE, `role/${request.id}`);
}; };
export { fetchRoleApi, createRoleApi, updateRoleApi, removeRoleApi }; export { fetchRoleApi, createRoleApi, updateRoleApi, removeRoleApi };

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

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

View file

@ -0,0 +1,21 @@
import { callApi, HTTP } from "../tools";
import {
IFetchSessionRequest,
IFetchSessionResponse,
IRemoveSessionRequest,
IRemoveSessionResponse,
} from "./types";
const fetchSessionApi = async (
request: IFetchSessionRequest,
): Promise<IFetchSessionResponse> => {
return await callApi(HTTP.GET, `session/${request.id}`);
};
const removeSessionApi = async (
request: IRemoveSessionRequest,
): Promise<IRemoveSessionResponse> => {
return await callApi(HTTP.DELETE, `session/${request.id}`);
};
export { fetchSessionApi, removeSessionApi };

28
src/api/session/types.ts Normal file
View file

@ -0,0 +1,28 @@
interface IFetchSession {
id: string;
userId: string;
creationDate: number;
}
interface IFetchSessionRequest {
id: string;
}
interface IFetchSessionResponse extends IFetchSession {}
interface IRemoveSessionRequest {
id: string;
}
interface IRemoveSessionResponse {
id: string;
userId: string;
}
export {
type IFetchSession,
type IFetchSessionRequest,
type IFetchSessionResponse,
type IRemoveSessionRequest,
type IRemoveSessionResponse,
};

View file

@ -18,6 +18,20 @@ interface IFetchUserRequest {
interface IFetchUserResponse extends IFetchUser {} interface IFetchUserResponse extends IFetchUser {}
interface IFetchUserSessionsRequest {
id: string;
}
interface IFetchUserSessionsResponse {
id: string;
sessions: IFetchUserSession[];
}
interface IFetchUserSession {
id: string;
userId: string;
}
interface IFetchUserCommunitiesRequest { interface IFetchUserCommunitiesRequest {
id: string; id: string;
} }
@ -30,7 +44,6 @@ interface IFetchUserCommunitiesResponse {
interface IFetchUserCommunity { interface IFetchUserCommunity {
id: string; id: string;
name: string; name: string;
description: string;
} }
export { export {
@ -38,6 +51,9 @@ export {
type IFetchLoggedUserResponse, type IFetchLoggedUserResponse,
type IFetchUserRequest, type IFetchUserRequest,
type IFetchUserResponse, type IFetchUserResponse,
type IFetchUserSessionsRequest,
type IFetchUserSessionsResponse,
type IFetchUserSession,
type IFetchUserCommunitiesRequest, type IFetchUserCommunitiesRequest,
type IFetchUserCommunitiesResponse, type IFetchUserCommunitiesResponse,
type IFetchUserCommunity, type IFetchUserCommunity,

View file

@ -3,6 +3,8 @@ import {
IFetchLoggedUserResponse, IFetchLoggedUserResponse,
IFetchUserRequest, IFetchUserRequest,
IFetchUserResponse, IFetchUserResponse,
IFetchUserSessionsRequest,
IFetchUserSessionsResponse,
IFetchUserCommunitiesRequest, IFetchUserCommunitiesRequest,
IFetchUserCommunitiesResponse, IFetchUserCommunitiesResponse,
} from "./types"; } from "./types";
@ -17,10 +19,21 @@ const fetchUserApi = async (
return await callApi(HTTP.GET, `user/${request.id}`); return await callApi(HTTP.GET, `user/${request.id}`);
}; };
const fetchUserSessionsApi = async (
request: IFetchUserSessionsRequest,
): Promise<IFetchUserSessionsResponse> => {
return await callApi(HTTP.GET, `user/${request.id}/sessions`);
};
const fetchUserCommunitiesApi = async ( const fetchUserCommunitiesApi = async (
request: IFetchUserCommunitiesRequest, request: IFetchUserCommunitiesRequest,
): Promise<IFetchUserCommunitiesResponse> => { ): Promise<IFetchUserCommunitiesResponse> => {
return await callApi(HTTP.GET, `user/${request.id}/communities`); return await callApi(HTTP.GET, `user/${request.id}/communities`);
}; };
export { fetchLoggedUserApi, fetchUserApi, fetchUserCommunitiesApi }; export {
fetchLoggedUserApi,
fetchUserApi,
fetchUserSessionsApi,
fetchUserCommunitiesApi,
};

View file

@ -5,7 +5,7 @@ const Channel: Component<IChannelProps> = (props: IChannelProps) => {
return ( return (
<li <li
class={`flex flex-row gap-2 items-center p-1 cursor-pointer rounded-lg ${props.active ? "bg-stone-700 hover:bg-stone-700" : "hover:bg-stone-800"}`} class={`flex flex-row gap-2 items-center p-1 cursor-pointer rounded-lg ${props.active ? "bg-stone-700 hover:bg-stone-700" : "hover:bg-stone-800"}`}
onClick={() => props.onChannelClick(props.id)} onClick={() => props.onChannelClick?.(props.id)}
> >
<div class="font-bold text-xl">&nbsp#</div> <div class="font-bold text-xl">&nbsp#</div>
<div class="font-bold">{props.name}</div> <div class="font-bold">{props.name}</div>

View file

@ -2,7 +2,7 @@ interface IChannelProps {
id: string; id: string;
name: string; name: string;
active: boolean; active: boolean;
onChannelClick: (id: string) => void; onChannelClick?: (id: string) => void;
} }
export { type IChannelProps }; export { type IChannelProps };

View file

@ -5,10 +5,10 @@ const Community: Component<ICommunityProps> = (props: ICommunityProps) => {
return ( return (
<div <div
class="avatar cursor-pointer" class="avatar cursor-pointer"
onClick={() => props.onCommunityClick(props.id)} onClick={() => props.onCommunityClick?.(props.id)}
> >
<div <div
class={`w-full transition-[border-radius] duration-300 outline-stone-300 hover:outline-2 ${props.active ? "rounded-lg outline-2" : "rounded-4xl"}`} class={`w-full transition-[border-radius] duration-300 outline-stone-300 ${props.active ? "rounded-lg outline-3 hover:outline-3" : "rounded-4xl hover:outline-2"}`}
> >
<img src={props.avatar} /> <img src={props.avatar} />
</div> </div>

View file

@ -3,7 +3,7 @@ interface ICommunityProps {
name: string; name: string;
avatar: string; avatar: string;
active: boolean; active: boolean;
onCommunityClick: (id: string) => void; onCommunityClick?: (id: string) => void;
} }
export { type ICommunityProps }; export { type ICommunityProps };

View file

@ -0,0 +1,101 @@
import { createSignal, type Component } from "solid-js";
import { ICommunityModalProps } from "./types";
import { dispatch } from "../../store/state";
import { CommunityActionTypes } from "../../store/community";
import { InviteActionTypes } from "../../store/invite";
const CommunityModal: Component<ICommunityModalProps> = (props) => {
const [getCommunityName, setCommunityName] = createSignal<string>("");
const [getInviteId, setInviteId] = createSignal<string>("");
const onCreateCommunity = () => {
dispatch({
type: CommunityActionTypes.CREATE_COMMUNITY_START,
payload: {
name: getCommunityName(),
},
});
setCommunityName("");
props.onClose?.();
};
const onJoinCommunity = () => {
dispatch({
type: InviteActionTypes.ACCEPT_INVITE_START,
payload: getInviteId(),
});
setInviteId("");
props.onClose?.();
};
const createCommunityHtml = () => (
<>
<h3 class="text-lg font-bold text-center mb-6">
Create a new Community
</h3>
<div class="bg-stone-800 h-16 p-2 flex flex-row gap-2 rounded-2xl">
<label class="bg-stone-800 input w-full h-full rounded-xl focus:border-none outline-none">
<input
type="text"
placeholder="Enter name of the new community"
value={getCommunityName()}
onInput={(e) => setCommunityName(e.currentTarget.value)}
/>
</label>
<button
class="bg-stone-950 btn btn-neutral h-full rounded-xl"
onClick={onCreateCommunity}
>
Create
</button>
</div>
</>
);
const joinCommunityHtml = () => (
<>
<h3 class="text-lg font-bold text-center mb-6">
Join an existing Community
</h3>
<div class="bg-stone-800 h-16 p-2 flex flex-row gap-2 rounded-2xl">
<label class="bg-stone-800 input w-full h-full rounded-xl focus:border-none outline-none">
<input
type="text"
placeholder="Enter invite ID"
value={getInviteId()}
onInput={(e) => setInviteId(e.currentTarget.value)}
/>
</label>
<button
class="bg-stone-950 btn btn-neutral h-full rounded-xl"
onClick={onJoinCommunity}
>
Join
</button>
</div>
</>
);
return (
<div>
<dialog ref={props.dialogRef} class="modal bg-[#00000050]">
<div class="modal-box bg-stone-950 rounded-3xl">
{createCommunityHtml()}
<div class="divider my-8"></div>
{joinCommunityHtml()}
</div>
<form
onClick={props.onClose}
method="dialog"
class="modal-backdrop"
></form>
</dialog>
</div>
);
};
export default CommunityModal;

View file

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

View file

@ -0,0 +1,6 @@
interface ICommunityModalProps {
dialogRef?: (element: HTMLDialogElement) => void;
onClose?: () => void;
}
export { type ICommunityModalProps };

View file

@ -0,0 +1,22 @@
import type { Component } from "solid-js";
import { IHomeCardProps } from "./types";
import { Dynamic } from "solid-js/web";
const HomeCard: Component<IHomeCardProps> = (props: IHomeCardProps) => {
return (
<a class="w-60 cursor-pointer" onClick={props.onClick}>
<div class="card border-2 bg-stone-800 border-stone-500 hover:border-stone-100 w-60 h-60">
<div class="flex flex-col h-full gap-1 m-6">
<div class="w-20">
<Dynamic component={props.icon} />
</div>
<div class="grow"></div>
<h2 class="card-title">{props.title}</h2>
<p>{props.description}</p>
</div>
</div>
</a>
);
};
export { HomeCard };

View file

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

View file

@ -0,0 +1,11 @@
import { JSXElement } from "solid-js";
import { IconParameters } from "../icons";
interface IHomeCardProps {
title: string;
description: string;
onClick?: () => void;
icon: (props: IconParameters) => JSXElement;
}
export { type IHomeCardProps };

View file

@ -5,7 +5,7 @@ const Member: Component<IMemberProps> = (props: IMemberProps) => {
return ( return (
<li <li
class={`flex flex-row gap-4 items-center p-1 cursor-pointer rounded-lg ${props.active ? "bg-stone-700 hover:bg-stone-700" : "hover:bg-stone-800"}`} class={`flex flex-row gap-4 items-center p-1 cursor-pointer rounded-lg ${props.active ? "bg-stone-700 hover:bg-stone-700" : "hover:bg-stone-800"}`}
onClick={() => props.onMemberClick(props.id)} onClick={() => props.onMemberClick?.(props.id)}
> >
<div class="avatar"> <div class="avatar">
<div class="w-9 rounded-full"> <div class="w-9 rounded-full">

View file

@ -3,7 +3,7 @@ interface IMemberProps {
username: string; username: string;
avatar: string; avatar: string;
active: boolean; active: boolean;
onMemberClick: (id: string) => void; onMemberClick?: (id: string) => void;
} }
export { type IMemberProps }; export { type IMemberProps };

View file

@ -6,7 +6,7 @@ const Message: Component<IMessageProps> = (props: IMessageProps) => {
<li class="list-row p-3 hover:bg-stone-700"> <li class="list-row p-3 hover:bg-stone-700">
<div <div
class="avatar cursor-pointer" class="avatar cursor-pointer"
onClick={() => props.onProfileClick(props.userId)} onClick={() => props.onProfileClick?.(props.userId)}
> >
<div class="w-10 rounded-full"> <div class="w-10 rounded-full">
<img src={props.avatar} /> <img src={props.avatar} />

View file

@ -4,7 +4,7 @@ interface IMessageProps {
userId: string; userId: string;
username: string; username: string;
avatar: string; avatar: string;
onProfileClick: (userId: string) => void; onProfileClick?: (userId: string) => void;
} }
export { type IMessageProps }; export { type IMessageProps };

View file

@ -1,5 +1,4 @@
import type { Component } from "solid-js"; import type { Component } from "solid-js";
import { state } from "../../store/state";
const MessageBar: Component = () => { const MessageBar: Component = () => {
return ( return (
@ -8,7 +7,7 @@ const MessageBar: Component = () => {
<label class="bg-stone-800/50 backdrop-blur-lg input w-full h-full rounded-full focus:border-none outline-none"> <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..." /> <input type="text" placeholder="Send a message..." />
</label> </label>
<button class="bg-black/50 backdrop-blur-lg btn btn-neutral h-full rounded-full"> <button class="bg-stone-950/50 backdrop-blur-lg btn btn-neutral h-full rounded-full">
Send Send
</button> </button>
</div> </div>

View file

@ -0,0 +1,22 @@
import type { Component } from "solid-js";
import { ISettingsModalProps } from "./types";
const SettingsModal: Component<ISettingsModalProps> = (props) => {
return (
<div>
<dialog ref={props.dialogRef} class="modal bg-[#00000050]">
<div class="modal-box bg-stone-950 rounded-3xl">
<h3 class="text-lg font-bold text-center">Settings</h3>
<p class="py-4 text-center">Not implemented yet</p>
</div>
<form
onClick={props.onClose}
method="dialog"
class="modal-backdrop"
></form>
</dialog>
</div>
);
};
export default SettingsModal;

View file

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

View file

@ -0,0 +1,6 @@
interface ISettingsModalProps {
dialogRef?: (element: HTMLDialogElement) => void;
onClose?: () => void;
}
export { type ISettingsModalProps };

View file

@ -0,0 +1,18 @@
import type { Component } from "solid-js";
import { ISidebarItemProps } from "./types";
import { Dynamic } from "solid-js/web";
const SidebarItem: Component<ISidebarItemProps> = (
props: ISidebarItemProps,
) => {
return (
<div
class={`bg-stone-800 w-full p-2 cursor-pointer transition-[border-radius] duration-300 outline-stone-300 ${props.active ? "rounded-lg outline-3 hover:outline-3" : "rounded-4xl hover:outline-2"}`}
onClick={() => props.onClick?.()}
>
<Dynamic component={props.icon} />
</div>
);
};
export { SidebarItem };

View file

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

View file

@ -0,0 +1,10 @@
import { JSXElement } from "solid-js";
import { IconParameters } from "../icons";
interface ISidebarItemProps {
icon: (props: IconParameters) => JSXElement;
active: boolean;
onClick?: () => void;
}
export { type ISidebarItemProps };

View file

@ -0,0 +1,28 @@
import type { Component } from "solid-js";
import { IconParameters, defaultFillIconParameters as defaults } from "./types";
const HomeIcon: Component<IconParameters> = ({
width,
height,
fill = defaults.fill,
stroke = defaults.stroke,
strokeWidth = defaults.strokeWidth,
}: IconParameters) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={height}
fill={fill}
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke={stroke}
>
<path d="M11.47 3.841a.75.75 0 0 1 1.06 0l8.69 8.69a.75.75 0 1 0 1.06-1.061l-8.689-8.69a2.25 2.25 0 0 0-3.182 0l-8.69 8.69a.75.75 0 1 0 1.061 1.06l8.69-8.689Z" />
<path d="m12 5.432 8.159 8.159c.03.03.06.058.091.086v6.198c0 1.035-.84 1.875-1.875 1.875H15a.75.75 0 0 1-.75-.75v-4.5a.75.75 0 0 0-.75-.75h-3a.75.75 0 0 0-.75.75V21a.75.75 0 0 1-.75.75H5.625a1.875 1.875 0 0 1-1.875-1.875v-6.198a2.29 2.29 0 0 0 .091-.086L12 5.432Z" />
</svg>
);
};
export default HomeIcon;

View file

@ -0,0 +1,31 @@
import type { Component } from "solid-js";
import { IconParameters, defaultFillIconParameters as defaults } from "./types";
const PlusIcon: Component<IconParameters> = ({
width,
height,
fill = defaults.fill,
stroke = defaults.stroke,
strokeWidth = defaults.strokeWidth,
}: IconParameters) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={height}
fill={fill}
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke={stroke}
>
<path
fill-rule="evenodd"
d="M12 3.75a.75.75 0 0 1 .75.75v6.75h6.75a.75.75 0 0 1 0 1.5h-6.75v6.75a.75.75 0 0 1-1.5 0v-6.75H4.5a.75.75 0 0 1 0-1.5h6.75V4.5a.75.75 0 0 1 .75-.75Z"
clip-rule="evenodd"
/>
</svg>
);
};
export default PlusIcon;

View file

@ -0,0 +1,31 @@
import type { Component } from "solid-js";
import { IconParameters, defaultFillIconParameters as defaults } from "./types";
const SettingsIcon: Component<IconParameters> = ({
width,
height,
fill = defaults.fill,
stroke = defaults.stroke,
strokeWidth = defaults.strokeWidth,
}: IconParameters) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={height}
fill={fill}
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke={stroke}
>
<path
fill-rule="evenodd"
d="M11.078 2.25c-.917 0-1.699.663-1.85 1.567L9.05 4.889c-.02.12-.115.26-.297.348a7.493 7.493 0 0 0-.986.57c-.166.115-.334.126-.45.083L6.3 5.508a1.875 1.875 0 0 0-2.282.819l-.922 1.597a1.875 1.875 0 0 0 .432 2.385l.84.692c.095.078.17.229.154.43a7.598 7.598 0 0 0 0 1.139c.015.2-.059.352-.153.43l-.841.692a1.875 1.875 0 0 0-.432 2.385l.922 1.597a1.875 1.875 0 0 0 2.282.818l1.019-.382c.115-.043.283-.031.45.082.312.214.641.405.985.57.182.088.277.228.297.35l.178 1.071c.151.904.933 1.567 1.85 1.567h1.844c.916 0 1.699-.663 1.85-1.567l.178-1.072c.02-.12.114-.26.297-.349.344-.165.673-.356.985-.57.167-.114.335-.125.45-.082l1.02.382a1.875 1.875 0 0 0 2.28-.819l.923-1.597a1.875 1.875 0 0 0-.432-2.385l-.84-.692c-.095-.078-.17-.229-.154-.43a7.614 7.614 0 0 0 0-1.139c-.016-.2.059-.352.153-.43l.84-.692c.708-.582.891-1.59.433-2.385l-.922-1.597a1.875 1.875 0 0 0-2.282-.818l-1.02.382c-.114.043-.282.031-.449-.083a7.49 7.49 0 0 0-.985-.57c-.183-.087-.277-.227-.297-.348l-.179-1.072a1.875 1.875 0 0 0-1.85-1.567h-1.843ZM12 15.75a3.75 3.75 0 1 0 0-7.5 3.75 3.75 0 0 0 0 7.5Z"
clip-rule="evenodd"
/>
</svg>
);
};
export default SettingsIcon;

View file

@ -0,0 +1,7 @@
import HomeIcon from "./HomeIcon";
import SettingsIcon from "./SettingsIcon";
import PlusIcon from "./PlusIcon";
import type { IconParameters } from "./types";
export { IconParameters, HomeIcon, SettingsIcon, PlusIcon };

View file

@ -0,0 +1,26 @@
interface IconParameters {
width?: number
height?: number
fill?: string
stroke?: string
strokeWidth?: number
}
const defaultFillIconParameters: IconParameters = {
width: 24,
height: 24,
fill: 'currentColor',
stroke: 'none',
strokeWidth: 0,
}
const defaultStrokeIconParameters: IconParameters = {
width: 24,
height: 24,
fill: 'none',
stroke: 'currentColor',
strokeWidth: 1.5,
}
export type { IconParameters }
export { defaultFillIconParameters, defaultStrokeIconParameters }

View file

@ -30,10 +30,15 @@ const createChannel = async (name: string, communityId: string) => {
}); });
}; };
const updateChannel = async (id: string, name?: string) => { const updateChannel = async (
id: string,
name?: string,
description?: string,
) => {
const data = await updateChannelApi({ const data = await updateChannelApi({
id: id, id: id,
name: name, name: name,
description: description,
}); });
dispatch({ dispatch({

View file

@ -1,14 +1,19 @@
import { import {
fetchCommunityApi, fetchCommunityApi,
createCommunityApi,
updateCommunityApi,
removeCommunityApi,
fetchCommunityChannelsApi, fetchCommunityChannelsApi,
fetchCommunityRolesApi, fetchCommunityRolesApi,
fetchCommunityMembersApi, fetchCommunityMembersApi,
fetchCommunityInvitesApi,
} from "../../api/community"; } from "../../api/community";
import { CommunityActionTypes } from "../../store/community"; import { CommunityActionTypes } from "../../store/community";
import { ChannelActionTypes } from "../../store/channel"; import { ChannelActionTypes } from "../../store/channel";
import { RoleActionTypes } from "../../store/role"; import { RoleActionTypes } from "../../store/role";
import { UserActionTypes } from "../../store/user"; import { UserActionTypes } from "../../store/user";
import { dispatch } from "../../store/state"; import { dispatch, state } from "../../store/state";
import { InviteActionTypes } from "../../store/invite";
const fetchCommunity = async (id: string) => { const fetchCommunity = async (id: string) => {
const data = await fetchCommunityApi({ const data = await fetchCommunityApi({
@ -21,6 +26,66 @@ const fetchCommunity = async (id: string) => {
}); });
}; };
const createCommunity = async (name: string) => {
const data = await createCommunityApi({
name: name,
});
dispatch({
type: CommunityActionTypes.CREATE_COMMUNITY_FINISH,
payload: data,
});
if (state.user.loggedUserId) {
dispatch({
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
payload: state.user.loggedUserId,
});
}
};
const updateCommunity = async (
id: string,
name?: string,
description?: string,
) => {
const data = await updateCommunityApi({
id: id,
name: name,
description: description,
});
dispatch({
type: CommunityActionTypes.UPDATE_COMMUNITY_FINISH,
payload: data,
});
if (state.user.loggedUserId) {
dispatch({
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
payload: state.user.loggedUserId,
});
}
};
const removeCommunity = async (id: string) => {
const data = await removeCommunityApi({
id: id,
});
dispatch({
type: CommunityActionTypes.REMOVE_COMMUNITY_FINISH,
payload: data,
});
if (state.user.loggedUserId) {
dispatch({
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
payload: state.user.loggedUserId,
});
}
};
const fetchCommunityChannels = async (id: string) => { const fetchCommunityChannels = async (id: string) => {
const data = await fetchCommunityChannelsApi({ const data = await fetchCommunityChannelsApi({
id: id, id: id,
@ -75,9 +140,31 @@ const fetchCommunityMembers = async (id: string) => {
}); });
}; };
const fetchCommunityInvites = async (id: string) => {
const data = await fetchCommunityInvitesApi({
id: id,
});
dispatch({
type: CommunityActionTypes.FETCH_COMMUNITY_INVITES_FINISH,
payload: data,
});
data.invites.forEach((invite) => {
dispatch({
type: InviteActionTypes.SET_INVITE,
payload: invite,
});
});
};
export { export {
fetchCommunity, fetchCommunity,
createCommunity,
updateCommunity,
removeCommunity,
fetchCommunityChannels, fetchCommunityChannels,
fetchCommunityRoles, fetchCommunityRoles,
fetchCommunityMembers, fetchCommunityMembers,
fetchCommunityInvites,
}; };

View file

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

View file

@ -0,0 +1,50 @@
import {
fetchInviteApi,
removeInviteApi,
acceptInviteApi,
} from "../../api/invite";
import { InviteActionTypes } from "../../store/invite";
import { dispatch, state } from "../../store/state";
import { UserActionTypes } from "../../store/user";
const fetchInvite = async (id: string) => {
const data = await fetchInviteApi({
id: id,
});
dispatch({
type: InviteActionTypes.FETCH_INVITE_FINISH,
payload: data,
});
};
const removeInvite = async (id: string) => {
const data = await removeInviteApi({
id: id,
});
dispatch({
type: InviteActionTypes.REMOVE_INVITE_FINISH,
payload: data,
});
};
const acceptInvite = async (id: string) => {
const data = await acceptInviteApi({
id: id,
});
dispatch({
type: InviteActionTypes.ACCEPT_INVITE_FINISH,
payload: data,
});
if (state.user.loggedUserId) {
dispatch({
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
payload: state.user.loggedUserId,
});
}
};
export { fetchInvite, removeInvite, acceptInvite };

View file

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

View file

@ -0,0 +1,27 @@
import { fetchSessionApi, removeSessionApi } from "../../api/session";
import { SessionActionTypes } from "../../store/session";
import { dispatch } from "../../store/state";
const fetchSession = async (id: string) => {
const data = await fetchSessionApi({
id: id,
});
dispatch({
type: SessionActionTypes.FETCH_SESSION_FINISH,
payload: data,
});
};
const removeSession = async (id: string) => {
const data = await removeSessionApi({
id: id,
});
dispatch({
type: SessionActionTypes.REMOVE_SESSION_FINISH,
payload: data,
});
};
export { fetchSession, removeSession };

View file

@ -1,11 +1,13 @@
import { import {
fetchLoggedUserApi, fetchLoggedUserApi,
fetchUserApi, fetchUserApi,
fetchUserSessionsApi,
fetchUserCommunitiesApi, fetchUserCommunitiesApi,
} from "../../api/user"; } from "../../api/user";
import { UserActionTypes } from "../../store/user"; import { UserActionTypes } from "../../store/user";
import { CommunityActionTypes } from "../../store/community"; import { CommunityActionTypes } from "../../store/community";
import { dispatch } from "../../store/state"; import { dispatch } from "../../store/state";
import { SessionActionTypes } from "../../store/session";
const fetchLoggedUser = async () => { const fetchLoggedUser = async () => {
const data = await fetchLoggedUserApi(); const data = await fetchLoggedUserApi();
@ -27,6 +29,24 @@ const fetchUser = async (id: string) => {
}); });
}; };
const fetchUserSessions = async (id: string) => {
const data = await fetchUserSessionsApi({
id: id,
});
dispatch({
type: UserActionTypes.FETCH_USER_SESSIONS_FINISH,
payload: data,
});
data.sessions.forEach((session) => {
dispatch({
type: SessionActionTypes.SET_SESSION,
payload: session,
});
});
};
const fetchUserCommunities = async (id: string) => { const fetchUserCommunities = async (id: string) => {
const data = await fetchUserCommunitiesApi({ const data = await fetchUserCommunitiesApi({
id: id, id: id,
@ -45,4 +65,4 @@ const fetchUserCommunities = async (id: string) => {
}); });
}; };
export { fetchLoggedUser, fetchUser, fetchUserCommunities }; export { fetchLoggedUser, fetchUser, fetchUserSessions, fetchUserCommunities };

View file

@ -1,21 +1,30 @@
import { AppActionTypes, AppAction } from "./app";
import { AuthActionTypes, AuthAction } from "./auth"; import { AuthActionTypes, AuthAction } from "./auth";
import { UserActionTypes, UserAction } from "./user"; import { UserActionTypes, UserAction } from "./user";
import { CommunityActionTypes, CommunityAction } from "./community"; import { CommunityActionTypes, CommunityAction } from "./community";
import { ChannelActionTypes, ChannelAction } from "./channel"; import { ChannelActionTypes, ChannelAction } from "./channel";
import { RoleActionTypes, RoleAction } from "./role"; import { RoleActionTypes, RoleAction } from "./role";
import { SessionActionTypes, SessionAction } from "./session";
import { InviteActionTypes, InviteAction } from "./invite";
type ActionTypes = type ActionTypes =
| AppActionTypes
| AuthActionTypes | AuthActionTypes
| UserActionTypes | UserActionTypes
| CommunityActionTypes | CommunityActionTypes
| ChannelActionTypes | ChannelActionTypes
| RoleActionTypes; | RoleActionTypes
| SessionActionTypes
| InviteActionTypes;
type Action = type Action =
| AppAction
| AuthAction | AuthAction
| UserAction | UserAction
| CommunityAction | CommunityAction
| ChannelAction | ChannelAction
| RoleAction; | RoleAction
| SessionAction
| InviteAction;
export { type Action, type ActionTypes }; export { type Action, type ActionTypes };

12
src/store/app/actions.ts Normal file
View file

@ -0,0 +1,12 @@
enum AppActionTypes {
SET_HOME_OPEN = "SET_HOME_OPEN",
SET_SETTINGS_OPEN = "SET_SETTINGS_OPEN",
SET_ADD_COMMUNITY_OPEN = "SET_ADD_COMMUNITY_OPEN",
}
type AppAction =
| { type: AppActionTypes.SET_HOME_OPEN; payload: boolean }
| { type: AppActionTypes.SET_SETTINGS_OPEN; payload: boolean }
| { type: AppActionTypes.SET_ADD_COMMUNITY_OPEN; payload: boolean };
export { AppActionTypes, type AppAction };

21
src/store/app/app.ts Normal file
View file

@ -0,0 +1,21 @@
import { setState } from "../state";
import { AppActionTypes, AppAction } from "./actions";
import { IAppState } from "./types";
function appReducer(_state: IAppState, action: AppAction) {
switch (action.type) {
case AppActionTypes.SET_HOME_OPEN:
setState("app", "homeOpen", action.payload);
break;
case AppActionTypes.SET_SETTINGS_OPEN:
setState("app", "dialogsOpen", "settingsOpen", false);
setState("app", "dialogsOpen", "settingsOpen", action.payload);
break;
case AppActionTypes.SET_ADD_COMMUNITY_OPEN:
setState("app", "dialogsOpen", "addCommunityOpen", false);
setState("app", "dialogsOpen", "addCommunityOpen", action.payload);
break;
}
}
export { appReducer };

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

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

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

@ -0,0 +1,11 @@
interface IAppState {
homeOpen: boolean;
dialogsOpen: IDialogsOpen;
}
interface IDialogsOpen {
settingsOpen: boolean;
addCommunityOpen: boolean;
}
export { type IAppState, type IDialogsOpen };

View file

@ -1,5 +1,6 @@
import { import {
ICreateChannelRequest, ICreateChannelRequest,
IUpdateChannelRequest,
IFetchChannelResponse, IFetchChannelResponse,
ICreateChannelResponse, ICreateChannelResponse,
IUpdateChannelResponse, IUpdateChannelResponse,
@ -27,7 +28,7 @@ type ChannelAction =
} }
| { | {
type: ChannelActionTypes.SET_ACTIVE_CHANNEL; type: ChannelActionTypes.SET_ACTIVE_CHANNEL;
payload: string; payload: string | undefined;
} }
| { type: ChannelActionTypes.FETCH_CHANNEL_START; payload: string } | { type: ChannelActionTypes.FETCH_CHANNEL_START; payload: string }
| { | {
@ -42,7 +43,10 @@ type ChannelAction =
type: ChannelActionTypes.CREATE_CHANNEL_FINISH; type: ChannelActionTypes.CREATE_CHANNEL_FINISH;
payload: ICreateChannelResponse; payload: ICreateChannelResponse;
} }
| { type: ChannelActionTypes.UPDATE_CHANNEL_START; payload: string } | {
type: ChannelActionTypes.UPDATE_CHANNEL_START;
payload: IUpdateChannelRequest;
}
| { | {
type: ChannelActionTypes.UPDATE_CHANNEL_FINISH; type: ChannelActionTypes.UPDATE_CHANNEL_FINISH;
payload: IUpdateChannelResponse; payload: IUpdateChannelResponse;

View file

@ -26,27 +26,26 @@ function channelReducer(state: IChannelState, action: ChannelAction) {
createChannel(action.payload.name, action.payload.communityId); createChannel(action.payload.name, action.payload.communityId);
break; break;
case ChannelActionTypes.CREATE_CHANNEL_FINISH: case ChannelActionTypes.CREATE_CHANNEL_FINISH:
setState("channel", "channels", { setState("channel", "channels", action.payload.id, action.payload);
...state.channels,
[action.payload.id]: action.payload,
});
break; break;
case ChannelActionTypes.UPDATE_CHANNEL_START: case ChannelActionTypes.UPDATE_CHANNEL_START:
updateChannel(action.payload); updateChannel(
action.payload.id,
action.payload.name,
action.payload.description,
);
break; break;
case ChannelActionTypes.UPDATE_CHANNEL_FINISH: case ChannelActionTypes.UPDATE_CHANNEL_FINISH:
setState("channel", "channels", { setState("channel", "channels", action.payload.id, action.payload);
...state.channels,
[action.payload.id]: action.payload,
});
break; break;
case ChannelActionTypes.REMOVE_CHANNEL_START: case ChannelActionTypes.REMOVE_CHANNEL_START:
removeChannel(action.payload); removeChannel(action.payload);
break; break;
case ChannelActionTypes.REMOVE_CHANNEL_FINISH: case ChannelActionTypes.REMOVE_CHANNEL_FINISH:
setState("channel", "channels", { setState("channel", "channels", (channels) => {
...state.channels, const copy = { ...channels };
[action.payload.id]: undefined, delete copy[action.payload.id];
return copy;
}); });
break; break;
} }

View file

@ -1,5 +1,5 @@
interface IChannelState { interface IChannelState {
active: string; active?: string;
channels: Record<string, IChannel>; channels: Record<string, IChannel>;
} }

View file

@ -1,8 +1,14 @@
import { import {
ICreateCommunityRequest,
IUpdateCommunityRequest,
IFetchCommunityResponse, IFetchCommunityResponse,
ICreateCommunityResponse,
IUpdateCommunityResponse,
IRemoveCommunityResponse,
IFetchCommunityChannelsResponse, IFetchCommunityChannelsResponse,
IFetchCommunityRolesResponse, IFetchCommunityRolesResponse,
IFetchCommunityMembersResponse, IFetchCommunityMembersResponse,
IFetchCommunityInvitesResponse,
} from "../../api/community"; } from "../../api/community";
import { IFetchUserCommunity } from "../../api/user"; import { IFetchUserCommunity } from "../../api/user";
@ -11,12 +17,20 @@ enum CommunityActionTypes {
SET_ACTIVE_COMMUNITY = "SET_ACTIVE_COMMUNITY", SET_ACTIVE_COMMUNITY = "SET_ACTIVE_COMMUNITY",
FETCH_COMMUNITY_START = "FETCH_COMMUNITY_START", FETCH_COMMUNITY_START = "FETCH_COMMUNITY_START",
FETCH_COMMUNITY_FINISH = "FETCH_COMMUNITY_FINISH", FETCH_COMMUNITY_FINISH = "FETCH_COMMUNITY_FINISH",
CREATE_COMMUNITY_START = "CREATE_COMMUNITY_START",
CREATE_COMMUNITY_FINISH = "CREATE_COMMUNITY_FINISH",
UPDATE_COMMUNITY_START = "UPDATE_COMMUNITY_START",
UPDATE_COMMUNITY_FINISH = "UPDATE_COMMUNITY_FINISH",
REMOVE_COMMUNITY_START = "REMOVE_COMMUNITY_START",
REMOVE_COMMUNITY_FINISH = "REMOVE_COMMUNITY_FINISH",
FETCH_COMMUNITY_CHANNELS_START = "FETCH_COMMUNITY_CHANNELS_START", FETCH_COMMUNITY_CHANNELS_START = "FETCH_COMMUNITY_CHANNELS_START",
FETCH_COMMUNITY_CHANNELS_FINISH = "FETCH_COMMUNITY_CHANNELS_FINISH", FETCH_COMMUNITY_CHANNELS_FINISH = "FETCH_COMMUNITY_CHANNELS_FINISH",
FETCH_COMMUNITY_ROLES_START = "FETCH_COMMUNITY_ROLES_START", FETCH_COMMUNITY_ROLES_START = "FETCH_COMMUNITY_ROLES_START",
FETCH_COMMUNITY_ROLES_FINISH = "FETCH_COMMUNITY_ROLES_FINISH", FETCH_COMMUNITY_ROLES_FINISH = "FETCH_COMMUNITY_ROLES_FINISH",
FETCH_COMMUNITY_MEMBERS_START = "FETCH_COMMUNITY_MEMBERS_START", FETCH_COMMUNITY_MEMBERS_START = "FETCH_COMMUNITY_MEMBERS_START",
FETCH_COMMUNITY_MEMBERS_FINISH = "FETCH_COMMUNITY_MEMBERS_FINISH", FETCH_COMMUNITY_MEMBERS_FINISH = "FETCH_COMMUNITY_MEMBERS_FINISH",
FETCH_COMMUNITY_INVITES_START = "FETCH_COMMUNITY_INVITES_START",
FETCH_COMMUNITY_INVITES_FINISH = "FETCH_COMMUNITY_INVITES_FINISH",
} }
type CommunityAction = type CommunityAction =
@ -26,13 +40,34 @@ type CommunityAction =
} }
| { | {
type: CommunityActionTypes.SET_ACTIVE_COMMUNITY; type: CommunityActionTypes.SET_ACTIVE_COMMUNITY;
payload: string; payload: string | undefined;
} }
| { type: CommunityActionTypes.FETCH_COMMUNITY_START; payload: string } | { type: CommunityActionTypes.FETCH_COMMUNITY_START; payload: string }
| { | {
type: CommunityActionTypes.FETCH_COMMUNITY_FINISH; type: CommunityActionTypes.FETCH_COMMUNITY_FINISH;
payload: IFetchCommunityResponse; payload: IFetchCommunityResponse;
} }
| {
type: CommunityActionTypes.CREATE_COMMUNITY_START;
payload: ICreateCommunityRequest;
}
| {
type: CommunityActionTypes.CREATE_COMMUNITY_FINISH;
payload: ICreateCommunityResponse;
}
| {
type: CommunityActionTypes.UPDATE_COMMUNITY_START;
payload: IUpdateCommunityRequest;
}
| {
type: CommunityActionTypes.UPDATE_COMMUNITY_FINISH;
payload: IUpdateCommunityResponse;
}
| { type: CommunityActionTypes.REMOVE_COMMUNITY_START; payload: string }
| {
type: CommunityActionTypes.REMOVE_COMMUNITY_FINISH;
payload: IRemoveCommunityResponse;
}
| { | {
type: CommunityActionTypes.FETCH_COMMUNITY_CHANNELS_START; type: CommunityActionTypes.FETCH_COMMUNITY_CHANNELS_START;
payload: string; payload: string;
@ -56,6 +91,14 @@ type CommunityAction =
| { | {
type: CommunityActionTypes.FETCH_COMMUNITY_MEMBERS_FINISH; type: CommunityActionTypes.FETCH_COMMUNITY_MEMBERS_FINISH;
payload: IFetchCommunityMembersResponse; payload: IFetchCommunityMembersResponse;
}
| {
type: CommunityActionTypes.FETCH_COMMUNITY_INVITES_START;
payload: string;
}
| {
type: CommunityActionTypes.FETCH_COMMUNITY_INVITES_FINISH;
payload: IFetchCommunityInvitesResponse;
}; };
export { CommunityActionTypes, type CommunityAction }; export { CommunityActionTypes, type CommunityAction };

View file

@ -1,8 +1,12 @@
import { import {
fetchCommunity, fetchCommunity,
createCommunity,
updateCommunity,
removeCommunity,
fetchCommunityChannels, fetchCommunityChannels,
fetchCommunityRoles, fetchCommunityRoles,
fetchCommunityMembers, fetchCommunityMembers,
fetchCommunityInvites,
} from "../../services/community"; } from "../../services/community";
import { setState } from "../state"; import { setState } from "../state";
import { CommunityActionTypes, CommunityAction } from "./actions"; import { CommunityActionTypes, CommunityAction } from "./actions";
@ -32,6 +36,42 @@ function communityReducer(state: ICommunityState, action: CommunityAction) {
action.payload, action.payload,
); );
break; break;
case CommunityActionTypes.CREATE_COMMUNITY_START:
createCommunity(action.payload.name);
break;
case CommunityActionTypes.CREATE_COMMUNITY_FINISH:
setState(
"community",
"communities",
action.payload.id,
action.payload,
);
break;
case CommunityActionTypes.UPDATE_COMMUNITY_START:
updateCommunity(
action.payload.id,
action.payload.name,
action.payload.description,
);
break;
case CommunityActionTypes.UPDATE_COMMUNITY_FINISH:
setState(
"community",
"communities",
action.payload.id,
action.payload,
);
break;
case CommunityActionTypes.REMOVE_COMMUNITY_START:
removeCommunity(action.payload);
break;
case CommunityActionTypes.REMOVE_COMMUNITY_FINISH:
setState("community", "communities", (communities) => {
const copy = { ...communities };
delete copy[action.payload.id];
return copy;
});
break;
case CommunityActionTypes.FETCH_COMMUNITY_CHANNELS_START: case CommunityActionTypes.FETCH_COMMUNITY_CHANNELS_START:
fetchCommunityChannels(action.payload); fetchCommunityChannels(action.payload);
break; break;
@ -56,6 +96,14 @@ function communityReducer(state: ICommunityState, action: CommunityAction) {
members: action.payload.members.map((member) => member.id), members: action.payload.members.map((member) => member.id),
}); });
break; break;
case CommunityActionTypes.FETCH_COMMUNITY_INVITES_START:
fetchCommunityInvites(action.payload);
break;
case CommunityActionTypes.FETCH_COMMUNITY_INVITES_FINISH:
setState("community", "communities", action.payload.id, {
invites: action.payload.invites.map((invite) => invite.id),
});
break;
} }
} }

View file

@ -1,5 +1,5 @@
interface ICommunityState { interface ICommunityState {
active: string; active?: string;
communities: Record<string, ICommunity>; communities: Record<string, ICommunity>;
} }
@ -12,6 +12,7 @@ interface ICommunity {
channels?: string[]; channels?: string[];
roles?: string[]; roles?: string[];
members?: string[]; members?: string[];
invites?: string[];
} }
export { type ICommunityState, type ICommunity }; export { type ICommunityState, type ICommunity };

View file

@ -0,0 +1,39 @@
import { IFetchCommunityInvite } from "../../api/community";
import {
IFetchInviteResponse,
IRemoveInviteResponse,
IAcceptInviteResponse,
} from "../../api/invite";
enum InviteActionTypes {
SET_INVITE = "SET_INVITE",
FETCH_INVITE_START = "FETCH_INVITE_START",
FETCH_INVITE_FINISH = "FETCH_INVITE_FINISH",
REMOVE_INVITE_START = "REMOVE_INVITE_START",
REMOVE_INVITE_FINISH = "REMOVE_INVITE_FINISH",
ACCEPT_INVITE_START = "ACCEPT_INVITE_START",
ACCEPT_INVITE_FINISH = "ACCEPT_INVITE_FINISH",
}
type InviteAction =
| {
type: InviteActionTypes.SET_INVITE;
payload: IFetchCommunityInvite;
}
| { type: InviteActionTypes.FETCH_INVITE_START; payload: string }
| {
type: InviteActionTypes.FETCH_INVITE_FINISH;
payload: IFetchInviteResponse;
}
| { type: InviteActionTypes.REMOVE_INVITE_START; payload: string }
| {
type: InviteActionTypes.REMOVE_INVITE_FINISH;
payload: IRemoveInviteResponse;
}
| { type: InviteActionTypes.ACCEPT_INVITE_START; payload: string }
| {
type: InviteActionTypes.ACCEPT_INVITE_FINISH;
payload: IAcceptInviteResponse;
};
export { InviteActionTypes, type InviteAction };

View file

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

View file

@ -0,0 +1,35 @@
import { fetchInvite, removeInvite, acceptInvite } from "../../services/invite";
import { setState } from "../state";
import { InviteActionTypes, InviteAction } from "./actions";
import { IInviteState } from "./types";
function inviteReducer(state: IInviteState, action: InviteAction) {
switch (action.type) {
case InviteActionTypes.SET_INVITE:
setState("invite", "invites", action.payload.id, action.payload);
break;
case InviteActionTypes.FETCH_INVITE_START:
fetchInvite(action.payload);
break;
case InviteActionTypes.FETCH_INVITE_FINISH:
setState("invite", "invites", action.payload.id, action.payload);
break;
case InviteActionTypes.REMOVE_INVITE_START:
removeInvite(action.payload);
break;
case InviteActionTypes.REMOVE_INVITE_FINISH:
setState("invite", "invites", (invites) => {
const copy = { ...invites };
delete copy[action.payload.id];
return copy;
});
break;
case InviteActionTypes.ACCEPT_INVITE_START:
acceptInvite(action.payload);
break;
case InviteActionTypes.ACCEPT_INVITE_FINISH:
break;
}
}
export { inviteReducer };

17
src/store/invite/types.ts Normal file
View file

@ -0,0 +1,17 @@
interface IInviteState {
invites: Record<string, IInvite>;
}
interface IInvite {
id: string;
communityId?: string;
valid?: boolean;
unlimitedInvites?: boolean;
hasExpiration?: boolean;
totalInvites?: number;
remainingInvites?: number;
creationDate?: number;
expirationDate?: number;
}
export { type IInviteState, type IInvite };

View file

@ -1,17 +1,23 @@
import { IState } from "./types"; import { IState } from "./types";
import { Action } from "./actions"; import { Action } from "./actions";
import { AppAction, appReducer } from "./app";
import { AuthAction, authReducer } from "./auth"; import { AuthAction, authReducer } from "./auth";
import { UserAction, userReducer } from "./user"; import { UserAction, userReducer } from "./user";
import { CommunityAction, communityReducer } from "./community"; import { CommunityAction, communityReducer } from "./community";
import { ChannelAction, channelReducer } from "./channel"; import { ChannelAction, channelReducer } from "./channel";
import { RoleAction, roleReducer } from "./role"; import { RoleAction, roleReducer } from "./role";
import { SessionAction, sessionReducer } from "./session";
import { InviteAction, inviteReducer } from "./invite";
function reducer(state: IState, action: Action) { function reducer(state: IState, action: Action) {
appReducer(state.app, action as AppAction);
authReducer(state.auth, action as AuthAction); authReducer(state.auth, action as AuthAction);
userReducer(state.user, action as UserAction); userReducer(state.user, action as UserAction);
communityReducer(state.community, action as CommunityAction); communityReducer(state.community, action as CommunityAction);
channelReducer(state.channel, action as ChannelAction); channelReducer(state.channel, action as ChannelAction);
roleReducer(state.role, action as RoleAction); roleReducer(state.role, action as RoleAction);
sessionReducer(state.session, action as SessionAction);
inviteReducer(state.invite, action as InviteAction);
} }
export { reducer }; export { reducer };

View file

@ -23,27 +23,22 @@ function roleReducer(state: IRoleState, action: RoleAction) {
createRole(action.payload.name, action.payload.communityId); createRole(action.payload.name, action.payload.communityId);
break; break;
case RoleActionTypes.CREATE_ROLE_FINISH: case RoleActionTypes.CREATE_ROLE_FINISH:
setState("role", "roles", { setState("role", "roles", action.payload.id, action.payload);
...state.roles,
[action.payload.id]: action.payload,
});
break; break;
case RoleActionTypes.UPDATE_ROLE_START: case RoleActionTypes.UPDATE_ROLE_START:
updateRole(action.payload); updateRole(action.payload);
break; break;
case RoleActionTypes.UPDATE_ROLE_FINISH: case RoleActionTypes.UPDATE_ROLE_FINISH:
setState("role", "roles", { setState("role", "roles", action.payload.id, action.payload);
...state.roles,
[action.payload.id]: action.payload,
});
break; break;
case RoleActionTypes.REMOVE_ROLE_START: case RoleActionTypes.REMOVE_ROLE_START:
removeRole(action.payload); removeRole(action.payload);
break; break;
case RoleActionTypes.REMOVE_ROLE_FINISH: case RoleActionTypes.REMOVE_ROLE_FINISH:
setState("role", "roles", { setState("role", "roles", (roles) => {
...state.roles, const copy = { ...roles };
[action.payload.id]: undefined, delete copy[action.payload.id];
return copy;
}); });
break; break;
} }

View file

@ -0,0 +1,31 @@
import { IFetchUserSession } from "../../api/user";
import {
IFetchSessionResponse,
IRemoveSessionResponse,
} from "../../api/session";
enum SessionActionTypes {
SET_SESSION = "SET_SESSION",
FETCH_SESSION_START = "FETCH_SESSION_START",
FETCH_SESSION_FINISH = "FETCH_SESSION_FINISH",
REMOVE_SESSION_START = "REMOVE_SESSION_START",
REMOVE_SESSION_FINISH = "REMOVE_SESSION_FINISH",
}
type SessionAction =
| {
type: SessionActionTypes.SET_SESSION;
payload: IFetchUserSession;
}
| { type: SessionActionTypes.FETCH_SESSION_START; payload: string }
| {
type: SessionActionTypes.FETCH_SESSION_FINISH;
payload: IFetchSessionResponse;
}
| { type: SessionActionTypes.REMOVE_SESSION_START; payload: string }
| {
type: SessionActionTypes.REMOVE_SESSION_FINISH;
payload: IRemoveSessionResponse;
};
export { SessionActionTypes, type SessionAction };

View file

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

View file

@ -0,0 +1,30 @@
import { fetchSession, removeSession } from "../../services/session";
import { setState } from "../state";
import { SessionActionTypes, SessionAction } from "./actions";
import { ISessionState } from "./types";
function sessionReducer(state: ISessionState, action: SessionAction) {
switch (action.type) {
case SessionActionTypes.SET_SESSION:
setState("session", "sessions", action.payload.id, action.payload);
break;
case SessionActionTypes.FETCH_SESSION_START:
fetchSession(action.payload);
break;
case SessionActionTypes.FETCH_SESSION_FINISH:
setState("session", "sessions", action.payload.id, action.payload);
break;
case SessionActionTypes.REMOVE_SESSION_START:
removeSession(action.payload);
break;
case SessionActionTypes.REMOVE_SESSION_FINISH:
setState("session", "sessions", (sessions) => {
const copy = { ...sessions };
delete copy[action.payload.id];
return copy;
});
break;
}
}
export { sessionReducer };

View file

@ -0,0 +1,11 @@
interface ISessionState {
sessions: Record<string, ISession>;
}
interface ISession {
id: string;
userId?: string;
creationDate?: number;
}
export { type ISessionState, type ISession };

View file

@ -4,6 +4,13 @@ import { Action } from "./actions";
import { reducer } from "./reducers"; import { reducer } from "./reducers";
const [state, setState] = createStore<IState>({ const [state, setState] = createStore<IState>({
app: {
homeOpen: true,
dialogsOpen: {
settingsOpen: false,
addCommunityOpen: false,
},
},
auth: { auth: {
session: undefined, session: undefined,
}, },
@ -20,6 +27,12 @@ const [state, setState] = createStore<IState>({
role: { role: {
roles: {}, roles: {},
}, },
session: {
sessions: {},
},
invite: {
invites: {},
},
}); });
function dispatch(action: Action) { function dispatch(action: Action) {

View file

@ -1,15 +1,21 @@
import { IAppState } from "./app";
import { IAuthState } from "./auth"; import { IAuthState } from "./auth";
import { IUserState } from "./user"; import { IUserState } from "./user";
import { ICommunityState } from "./community"; import { ICommunityState } from "./community";
import { IChannelState } from "./channel"; import { IChannelState } from "./channel";
import { IRoleState } from "./role"; import { IRoleState } from "./role";
import { ISessionState } from "./session";
import { IInviteState } from "./invite";
interface IState { interface IState {
app: IAppState;
auth: IAuthState; auth: IAuthState;
user: IUserState; user: IUserState;
community: ICommunityState; community: ICommunityState;
channel: IChannelState; channel: IChannelState;
role: IRoleState; role: IRoleState;
session: ISessionState;
invite: IInviteState;
} }
export { type IState }; export { type IState };

View file

@ -2,6 +2,7 @@ import { IFetchCommunityMember } from "../../api/community";
import { import {
IFetchLoggedUserResponse, IFetchLoggedUserResponse,
IFetchUserResponse, IFetchUserResponse,
IFetchUserSessionsResponse,
IFetchUserCommunitiesResponse, IFetchUserCommunitiesResponse,
} from "../../api/user"; } from "../../api/user";
@ -11,6 +12,8 @@ enum UserActionTypes {
FETCH_LOGGED_USER_ID_FINISH = "FETCH_LOGGED_USER_ID_FINISH", FETCH_LOGGED_USER_ID_FINISH = "FETCH_LOGGED_USER_ID_FINISH",
FETCH_USER_START = "FETCH_USER_START", FETCH_USER_START = "FETCH_USER_START",
FETCH_USER_FINISH = "FETCH_USER_FINISH", FETCH_USER_FINISH = "FETCH_USER_FINISH",
FETCH_USER_SESSIONS_START = "FETCH_USER_SESSIONS_START",
FETCH_USER_SESSIONS_FINISH = "FETCH_USER_SESSIONS_FINISH",
FETCH_USER_COMMUNITIES_START = "FETCH_USER_COMMUNITIES_START", FETCH_USER_COMMUNITIES_START = "FETCH_USER_COMMUNITIES_START",
FETCH_USER_COMMUNITIES_FINISH = "FETCH_USER_COMMUNITIES_FINISH", FETCH_USER_COMMUNITIES_FINISH = "FETCH_USER_COMMUNITIES_FINISH",
} }
@ -27,6 +30,11 @@ type UserAction =
} }
| { type: UserActionTypes.FETCH_USER_START; payload: string } | { type: UserActionTypes.FETCH_USER_START; payload: string }
| { type: UserActionTypes.FETCH_USER_FINISH; payload: IFetchUserResponse } | { type: UserActionTypes.FETCH_USER_FINISH; payload: IFetchUserResponse }
| { type: UserActionTypes.FETCH_USER_SESSIONS_START; payload: string }
| {
type: UserActionTypes.FETCH_USER_SESSIONS_FINISH;
payload: IFetchUserSessionsResponse;
}
| { type: UserActionTypes.FETCH_USER_COMMUNITIES_START; payload: string } | { type: UserActionTypes.FETCH_USER_COMMUNITIES_START; payload: string }
| { | {
type: UserActionTypes.FETCH_USER_COMMUNITIES_FINISH; type: UserActionTypes.FETCH_USER_COMMUNITIES_FINISH;

View file

@ -11,6 +11,7 @@ interface IUser {
admin?: boolean; admin?: boolean;
registerDate?: number; registerDate?: number;
lastLogin?: number; lastLogin?: number;
sessions?: string[];
communities?: string[]; communities?: string[];
} }

View file

@ -1,6 +1,7 @@
import { import {
fetchLoggedUser, fetchLoggedUser,
fetchUser, fetchUser,
fetchUserSessions,
fetchUserCommunities, fetchUserCommunities,
} from "../../services/user"; } from "../../services/user";
import { setState } from "../state"; import { setState } from "../state";
@ -24,6 +25,14 @@ function userReducer(state: IUserState, action: UserAction) {
case UserActionTypes.FETCH_USER_FINISH: case UserActionTypes.FETCH_USER_FINISH:
setState("user", "users", action.payload.id, action.payload); setState("user", "users", action.payload.id, action.payload);
break; break;
case UserActionTypes.FETCH_USER_SESSIONS_START:
fetchUserSessions(action.payload);
break;
case UserActionTypes.FETCH_USER_SESSIONS_FINISH:
setState("user", "users", action.payload.id, {
sessions: action.payload.sessions.map((session) => session.id),
});
break;
case UserActionTypes.FETCH_USER_COMMUNITIES_START: case UserActionTypes.FETCH_USER_COMMUNITIES_START:
fetchUserCommunities(action.payload); fetchUserCommunities(action.payload);
break; break;

View file

@ -1,5 +1,4 @@
import { createEffect, createMemo, type Component } from "solid-js"; import { createEffect, createMemo, type Component } from "solid-js";
import { CommunityView } from "../CommunityView";
import { dispatch, state } from "../../store/state"; import { dispatch, state } from "../../store/state";
import { CommunityActionTypes, ICommunity } from "../../store/community"; import { CommunityActionTypes, ICommunity } from "../../store/community";
import { ChannelActionTypes } from "../../store/channel"; import { ChannelActionTypes } from "../../store/channel";
@ -23,7 +22,7 @@ const ChannelView: Component = () => {
const communityInfo = createMemo<ICommunity | undefined>(() => { const communityInfo = createMemo<ICommunity | undefined>(() => {
const activeCommunityId = state.community.active; const activeCommunityId = state.community.active;
return state.community.communities[activeCommunityId]; return state.community.communities[activeCommunityId ?? 0];
}); });
createEffect(() => { createEffect(() => {
@ -63,21 +62,18 @@ const ChannelView: Component = () => {
}; };
return ( return (
<div class="flex flex-row bg-stone-900 w-80 shadow-panel z-20"> <div class="bg-stone-900 w-80 shadow-panel z-20 h-full relative">
<CommunityView /> <CommunityBar
<div class="h-full w-full relative"> id={communityInfo()?.id}
<CommunityBar name={communityInfo()?.name}
id={communityInfo()?.id} description={communityInfo()?.description}
name={communityInfo()?.name} avatar={
description={communityInfo()?.description} "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp"
avatar={ }
"https://img.daisyui.com/images/profile/demo/yellingcat@192.webp" />
} <ul class="h-full list flex flex-col p-2 gap-1 pt-18 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-500 scrollbar-track-gray-800">
/> {channelIds().map(mapChannel)}
<ul class="h-full list flex flex-col p-2 gap-1 pt-18 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-500 scrollbar-track-gray-800"> </ul>
{channelIds().map(mapChannel)}
</ul>
</div>
</div> </div>
); );
}; };

View file

@ -180,7 +180,7 @@ const ChatView: Component = () => {
const channelInfo = createMemo<IChannel | undefined>(() => { const channelInfo = createMemo<IChannel | undefined>(() => {
const activeChannelId = state.channel.active; const activeChannelId = state.channel.active;
return state.channel.channels[activeChannelId]; return state.channel.channels[activeChannelId ?? 0];
}); });
let scrollRef: HTMLUListElement | undefined; let scrollRef: HTMLUListElement | undefined;

View file

@ -2,6 +2,9 @@ import { createMemo, type Component } from "solid-js";
import { Community } from "../../components/Community"; import { Community } from "../../components/Community";
import { dispatch, state } from "../../store/state"; import { dispatch, state } from "../../store/state";
import { CommunityActionTypes } from "../../store/community"; import { CommunityActionTypes } from "../../store/community";
import { SidebarItem } from "../../components/SidebarItem";
import { AppActionTypes } from "../../store/app";
import { HomeIcon, PlusIcon, SettingsIcon } from "../../components/icons";
const CommunityView: Component = () => { const CommunityView: Component = () => {
const communityIds = createMemo(() => { const communityIds = createMemo(() => {
@ -18,7 +21,25 @@ const CommunityView: Component = () => {
return loggedUser.communities ?? []; return loggedUser.communities ?? [];
}); });
const onHomeClick = () => {
dispatch({
type: CommunityActionTypes.SET_ACTIVE_COMMUNITY,
payload: undefined,
});
dispatch({
type: AppActionTypes.SET_HOME_OPEN,
payload: true,
});
};
const onCommunityClick = (id: string) => { const onCommunityClick = (id: string) => {
if (state.app.homeOpen) {
dispatch({
type: AppActionTypes.SET_HOME_OPEN,
payload: false,
});
}
dispatch({ dispatch({
type: CommunityActionTypes.FETCH_COMMUNITY_START, type: CommunityActionTypes.FETCH_COMMUNITY_START,
payload: id, payload: id,
@ -29,6 +50,20 @@ const CommunityView: Component = () => {
}); });
}; };
const onNewClick = () => {
dispatch({
type: AppActionTypes.SET_ADD_COMMUNITY_OPEN,
payload: true,
});
};
const onSettingsClick = () => {
dispatch({
type: AppActionTypes.SET_SETTINGS_OPEN,
payload: true,
});
};
const mapCommunity = (communityId: string) => { const mapCommunity = (communityId: string) => {
const community = state.community.communities[communityId]; const community = state.community.communities[communityId];
if (!community) { if (!community) {
@ -49,8 +84,22 @@ const CommunityView: Component = () => {
}; };
return ( return (
<div class="bg-stone-950 w-18 h-full shadow-panel z-30 flex flex-col p-2 gap-2"> <div class="flex flex-col bg-stone-950 w-16 h-full shadow-panel z-30 p-2 gap-2">
<SidebarItem
icon={HomeIcon}
active={state.app.homeOpen}
onClick={onHomeClick}
/>
<div class="divider my-0"></div>
{communityIds().map(mapCommunity)} {communityIds().map(mapCommunity)}
<div class="divider my-0"></div>
<SidebarItem icon={PlusIcon} active={false} onClick={onNewClick} />
<div class="flex-1"></div>
<SidebarItem
icon={SettingsIcon}
active={false}
onClick={onSettingsClick}
/>
</div> </div>
); );
}; };

View file

@ -0,0 +1,42 @@
import { type Component } from "solid-js";
import { PlusIcon, SettingsIcon } from "../../components/icons";
import { HomeCard } from "../../components/HomeCard";
import { dispatch } from "../../store/state";
import { AppActionTypes } from "../../store/app";
const HomeView: Component = () => {
const onNewClick = () => {
dispatch({
type: AppActionTypes.SET_ADD_COMMUNITY_OPEN,
payload: true,
});
};
const onSettingsClick = () => {
dispatch({
type: AppActionTypes.SET_SETTINGS_OPEN,
payload: true,
});
};
return (
<div class="flex-1 flex flex-row bg-stone-900 shadow-panel z-0">
<div class="flex-1 flex flex-row items-center justify-center gap-8">
<HomeCard
title="Find a Community"
description="Find or create a new Community to chat"
icon={PlusIcon}
onClick={onNewClick}
/>
<HomeCard
title="Open Settings"
description="Adjust your settings"
icon={SettingsIcon}
onClick={onSettingsClick}
/>
</div>
</div>
);
};
export { HomeView };

View file

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

View file

@ -1,10 +1,13 @@
import { createEffect, createMemo, onMount, type Component } from "solid-js"; import { createEffect, onMount, type Component } from "solid-js";
import { ChannelView } from "../ChannelView"; import { ChannelView } from "../ChannelView";
import { ChatView } from "../ChatView"; import { ChatView } from "../ChatView";
import { MemberView } from "../MemberView"; import { MemberView } from "../MemberView";
import { dispatch, state } from "../../store/state"; import { dispatch, state } from "../../store/state";
import { UserActionTypes } from "../../store/user"; import { UserActionTypes } from "../../store/user";
import { AuthActionTypes } from "../../store/auth"; import { AuthActionTypes } from "../../store/auth";
import { HomeView } from "../HomeView";
import { CommunityView } from "../CommunityView";
import { ModalView } from "../ModalView";
const MainView: Component = () => { const MainView: Component = () => {
onMount(() => { onMount(() => {
@ -30,9 +33,19 @@ const MainView: Component = () => {
return ( return (
<div class="flex flex-row h-screen"> <div class="flex flex-row h-screen">
<ChannelView /> <ModalView />
<ChatView /> <CommunityView />
<MemberView /> {state.app.homeOpen ? (
<>
<HomeView />
</>
) : (
<>
<ChannelView />
<ChatView />
<MemberView />
</>
)}
</div> </div>
); );
}; };

View file

@ -0,0 +1,55 @@
import { createEffect, type Component } from "solid-js";
import { dispatch, state } from "../../store/state";
import SettingsModal from "../../components/SettingsModal/SettingsModal";
import CommunityModal from "../../components/CommunityModal/CommunityModal";
import { AppActionTypes } from "../../store/app";
const ModalView: Component = () => {
let settingsModal: HTMLDialogElement;
let communityModal: HTMLDialogElement;
createEffect(() => {
if (state.app.dialogsOpen.settingsOpen) {
settingsModal.showModal();
} else {
settingsModal.close();
}
});
createEffect(() => {
if (state.app.dialogsOpen.addCommunityOpen) {
communityModal.showModal();
} else {
communityModal.close();
}
});
const onCloseSettings = () => {
dispatch({
type: AppActionTypes.SET_SETTINGS_OPEN,
payload: false,
});
};
const onCloseCommunity = () => {
dispatch({
type: AppActionTypes.SET_ADD_COMMUNITY_OPEN,
payload: false,
});
};
return (
<>
<SettingsModal
dialogRef={(element) => (settingsModal = element)}
onClose={onCloseSettings}
/>
<CommunityModal
dialogRef={(element) => (communityModal = element)}
onClose={onCloseCommunity}
/>
</>
);
};
export { ModalView };

View file

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