diff --git a/src/api/channel/channel.ts b/src/api/channel/channel.ts index beb42c9..987baa1 100644 --- a/src/api/channel/channel.ts +++ b/src/api/channel/channel.ts @@ -31,7 +31,7 @@ const updateChannelApi = async ( const removeChannelApi = async ( request: IRemoveChannelRequest, ): Promise => { - return await callApi(HTTP.DELETE, `channel/${request.id}`, request); + return await callApi(HTTP.DELETE, `channel/${request.id}`); }; export { diff --git a/src/api/channel/types.ts b/src/api/channel/types.ts index c76b499..7717193 100644 --- a/src/api/channel/types.ts +++ b/src/api/channel/types.ts @@ -22,6 +22,7 @@ interface ICreateChannelResponse extends IFetchChannel {} interface IUpdateChannelRequest { id: string; name?: string; + description?: string; } interface IUpdateChannelResponse extends IFetchChannel {} diff --git a/src/api/community/community.ts b/src/api/community/community.ts index dfbed6a..8bd67d0 100644 --- a/src/api/community/community.ts +++ b/src/api/community/community.ts @@ -2,12 +2,20 @@ import { callApi, HTTP } from "../tools"; import { IFetchCommunityRequest, IFetchCommunityResponse, + ICreateCommunityRequest, + ICreateCommunityResponse, + IUpdateCommunityRequest, + IUpdateCommunityResponse, + IRemoveCommunityRequest, + IRemoveCommunityResponse, IFetchCommunityChannelsRequest, IFetchCommunityChannelsResponse, IFetchCommunityRolesRequest, IFetchCommunityRolesResponse, IFetchCommunityMembersRequest, IFetchCommunityMembersResponse, + IFetchCommunityInvitesRequest, + IFetchCommunityInvitesResponse, } from "./types"; const fetchCommunityApi = async ( @@ -16,6 +24,24 @@ const fetchCommunityApi = async ( return await callApi(HTTP.GET, `community/${request.id}`); }; +const createCommunityApi = async ( + request: ICreateCommunityRequest, +): Promise => { + return await callApi(HTTP.POST, `community`, request); +}; + +const updateCommunityApi = async ( + request: IUpdateCommunityRequest, +): Promise => { + return await callApi(HTTP.PATCH, `community/${request.id}`, request); +}; + +const removeCommunityApi = async ( + request: IRemoveCommunityRequest, +): Promise => { + return await callApi(HTTP.DELETE, `community/${request.id}`); +}; + const fetchCommunityChannelsApi = async ( request: IFetchCommunityChannelsRequest, ): Promise => { @@ -34,9 +60,19 @@ const fetchCommunityMembersApi = async ( return await callApi(HTTP.GET, `community/${request.id}/members`); }; +const fetchCommunityInvitesApi = async ( + request: IFetchCommunityInvitesRequest, +): Promise => { + return await callApi(HTTP.GET, `community/${request.id}/invites`); +}; + export { fetchCommunityApi, + createCommunityApi, + updateCommunityApi, + removeCommunityApi, fetchCommunityChannelsApi, fetchCommunityRolesApi, fetchCommunityMembersApi, + fetchCommunityInvitesApi, }; diff --git a/src/api/community/types.ts b/src/api/community/types.ts index 464380c..7666e3f 100644 --- a/src/api/community/types.ts +++ b/src/api/community/types.ts @@ -12,6 +12,28 @@ interface IFetchCommunityRequest { 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 { id: string; } @@ -54,10 +76,29 @@ interface IFetchCommunityMember { username: string; } +interface IFetchCommunityInvitesRequest { + id: string; +} + +interface IFetchCommunityInvitesResponse { + id: string; + invites: IFetchCommunityInvite[]; +} + +interface IFetchCommunityInvite { + id: string; +} + export { type IFetchCommunity, type IFetchCommunityRequest, type IFetchCommunityResponse, + type ICreateCommunityRequest, + type ICreateCommunityResponse, + type IUpdateCommunityRequest, + type IUpdateCommunityResponse, + type IRemoveCommunityRequest, + type IRemoveCommunityResponse, type IFetchCommunityChannelsRequest, type IFetchCommunityChannelsResponse, type IFetchCommunityChannel, @@ -67,4 +108,7 @@ export { type IFetchCommunityMembersRequest, type IFetchCommunityMembersResponse, type IFetchCommunityMember, + type IFetchCommunityInvitesRequest, + type IFetchCommunityInvitesResponse, + type IFetchCommunityInvite, }; diff --git a/src/api/invite/index.ts b/src/api/invite/index.ts new file mode 100644 index 0000000..d0a9cdf --- /dev/null +++ b/src/api/invite/index.ts @@ -0,0 +1,2 @@ +export * from "./invite"; +export * from "./types"; diff --git a/src/api/invite/invite.ts b/src/api/invite/invite.ts new file mode 100644 index 0000000..0774817 --- /dev/null +++ b/src/api/invite/invite.ts @@ -0,0 +1,29 @@ +import { callApi, HTTP } from "../tools"; +import { + IFetchInviteRequest, + IFetchInviteResponse, + IRemoveInviteRequest, + IRemoveInviteResponse, + IAcceptInviteRequest, + IAcceptInviteResponse, +} from "./types"; + +const fetchInviteApi = async ( + request: IFetchInviteRequest, +): Promise => { + return await callApi(HTTP.GET, `invite/${request.id}`); +}; + +const removeInviteApi = async ( + request: IRemoveInviteRequest, +): Promise => { + return await callApi(HTTP.DELETE, `invite/${request.id}`); +}; + +const acceptInviteApi = async ( + request: IAcceptInviteRequest, +): Promise => { + return await callApi(HTTP.GET, `invite/${request.id}/accept`); +}; + +export { fetchInviteApi, removeInviteApi, acceptInviteApi }; diff --git a/src/api/invite/types.ts b/src/api/invite/types.ts new file mode 100644 index 0000000..a79ad1a --- /dev/null +++ b/src/api/invite/types.ts @@ -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, +}; diff --git a/src/api/role/role.ts b/src/api/role/role.ts index 4c9d762..3b50acf 100644 --- a/src/api/role/role.ts +++ b/src/api/role/role.ts @@ -31,7 +31,7 @@ const updateRoleApi = async ( const removeRoleApi = async ( request: IRemoveRoleRequest, ): Promise => { - return await callApi(HTTP.DELETE, `role/${request.id}`, request); + return await callApi(HTTP.DELETE, `role/${request.id}`); }; export { fetchRoleApi, createRoleApi, updateRoleApi, removeRoleApi }; diff --git a/src/api/session/index.ts b/src/api/session/index.ts new file mode 100644 index 0000000..69bf84b --- /dev/null +++ b/src/api/session/index.ts @@ -0,0 +1,2 @@ +export * from "./session"; +export * from "./types"; diff --git a/src/api/session/session.ts b/src/api/session/session.ts new file mode 100644 index 0000000..e0f33e9 --- /dev/null +++ b/src/api/session/session.ts @@ -0,0 +1,21 @@ +import { callApi, HTTP } from "../tools"; +import { + IFetchSessionRequest, + IFetchSessionResponse, + IRemoveSessionRequest, + IRemoveSessionResponse, +} from "./types"; + +const fetchSessionApi = async ( + request: IFetchSessionRequest, +): Promise => { + return await callApi(HTTP.GET, `session/${request.id}`); +}; + +const removeSessionApi = async ( + request: IRemoveSessionRequest, +): Promise => { + return await callApi(HTTP.DELETE, `session/${request.id}`); +}; + +export { fetchSessionApi, removeSessionApi }; diff --git a/src/api/session/types.ts b/src/api/session/types.ts new file mode 100644 index 0000000..bb6ee5d --- /dev/null +++ b/src/api/session/types.ts @@ -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, +}; diff --git a/src/api/user/types.ts b/src/api/user/types.ts index 769293b..bc2ffb2 100644 --- a/src/api/user/types.ts +++ b/src/api/user/types.ts @@ -18,6 +18,20 @@ interface IFetchUserRequest { interface IFetchUserResponse extends IFetchUser {} +interface IFetchUserSessionsRequest { + id: string; +} + +interface IFetchUserSessionsResponse { + id: string; + sessions: IFetchUserSession[]; +} + +interface IFetchUserSession { + id: string; + userId: string; +} + interface IFetchUserCommunitiesRequest { id: string; } @@ -30,7 +44,6 @@ interface IFetchUserCommunitiesResponse { interface IFetchUserCommunity { id: string; name: string; - description: string; } export { @@ -38,6 +51,9 @@ export { type IFetchLoggedUserResponse, type IFetchUserRequest, type IFetchUserResponse, + type IFetchUserSessionsRequest, + type IFetchUserSessionsResponse, + type IFetchUserSession, type IFetchUserCommunitiesRequest, type IFetchUserCommunitiesResponse, type IFetchUserCommunity, diff --git a/src/api/user/user.ts b/src/api/user/user.ts index 9d20a6d..3020875 100644 --- a/src/api/user/user.ts +++ b/src/api/user/user.ts @@ -3,6 +3,8 @@ import { IFetchLoggedUserResponse, IFetchUserRequest, IFetchUserResponse, + IFetchUserSessionsRequest, + IFetchUserSessionsResponse, IFetchUserCommunitiesRequest, IFetchUserCommunitiesResponse, } from "./types"; @@ -17,10 +19,21 @@ const fetchUserApi = async ( return await callApi(HTTP.GET, `user/${request.id}`); }; +const fetchUserSessionsApi = async ( + request: IFetchUserSessionsRequest, +): Promise => { + return await callApi(HTTP.GET, `user/${request.id}/sessions`); +}; + const fetchUserCommunitiesApi = async ( request: IFetchUserCommunitiesRequest, ): Promise => { return await callApi(HTTP.GET, `user/${request.id}/communities`); }; -export { fetchLoggedUserApi, fetchUserApi, fetchUserCommunitiesApi }; +export { + fetchLoggedUserApi, + fetchUserApi, + fetchUserSessionsApi, + fetchUserCommunitiesApi, +}; diff --git a/src/components/Channel/Channel.tsx b/src/components/Channel/Channel.tsx index 6b2066d..815f9c2 100644 --- a/src/components/Channel/Channel.tsx +++ b/src/components/Channel/Channel.tsx @@ -5,7 +5,7 @@ const Channel: Component = (props: IChannelProps) => { return (
  • props.onChannelClick(props.id)} + onClick={() => props.onChannelClick?.(props.id)} >
     #
    {props.name}
    diff --git a/src/components/Channel/types.ts b/src/components/Channel/types.ts index be06e76..7a75709 100644 --- a/src/components/Channel/types.ts +++ b/src/components/Channel/types.ts @@ -2,7 +2,7 @@ interface IChannelProps { id: string; name: string; active: boolean; - onChannelClick: (id: string) => void; + onChannelClick?: (id: string) => void; } export { type IChannelProps }; diff --git a/src/components/Community/Community.tsx b/src/components/Community/Community.tsx index 6f40e49..3fbd739 100644 --- a/src/components/Community/Community.tsx +++ b/src/components/Community/Community.tsx @@ -5,10 +5,10 @@ const Community: Component = (props: ICommunityProps) => { return (
    props.onCommunityClick(props.id)} + onClick={() => props.onCommunityClick?.(props.id)} >
    diff --git a/src/components/Community/types.ts b/src/components/Community/types.ts index 7a47526..a24b93f 100644 --- a/src/components/Community/types.ts +++ b/src/components/Community/types.ts @@ -3,7 +3,7 @@ interface ICommunityProps { name: string; avatar: string; active: boolean; - onCommunityClick: (id: string) => void; + onCommunityClick?: (id: string) => void; } export { type ICommunityProps }; diff --git a/src/components/CommunityModal/CommunityModal.tsx b/src/components/CommunityModal/CommunityModal.tsx new file mode 100644 index 0000000..d5cb2b2 --- /dev/null +++ b/src/components/CommunityModal/CommunityModal.tsx @@ -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 = (props) => { + const [getCommunityName, setCommunityName] = createSignal(""); + const [getInviteId, setInviteId] = createSignal(""); + + 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 = () => ( + <> +

    + Create a new Community +

    +
    + + +
    + + ); + + const joinCommunityHtml = () => ( + <> +

    + Join an existing Community +

    +
    + + +
    + + ); + + return ( +
    + + + + +
    + ); +}; + +export default CommunityModal; diff --git a/src/components/CommunityModal/index.ts b/src/components/CommunityModal/index.ts new file mode 100644 index 0000000..59e2444 --- /dev/null +++ b/src/components/CommunityModal/index.ts @@ -0,0 +1,2 @@ +export * from "./CommunityModal"; +export * from "./types"; diff --git a/src/components/CommunityModal/types.ts b/src/components/CommunityModal/types.ts new file mode 100644 index 0000000..58b8f77 --- /dev/null +++ b/src/components/CommunityModal/types.ts @@ -0,0 +1,6 @@ +interface ICommunityModalProps { + dialogRef?: (element: HTMLDialogElement) => void; + onClose?: () => void; +} + +export { type ICommunityModalProps }; diff --git a/src/components/HomeCard/HomeCard.tsx b/src/components/HomeCard/HomeCard.tsx new file mode 100644 index 0000000..e929d1b --- /dev/null +++ b/src/components/HomeCard/HomeCard.tsx @@ -0,0 +1,22 @@ +import type { Component } from "solid-js"; +import { IHomeCardProps } from "./types"; +import { Dynamic } from "solid-js/web"; + +const HomeCard: Component = (props: IHomeCardProps) => { + return ( + +
    +
    +
    + +
    +
    +

    {props.title}

    +

    {props.description}

    +
    +
    +
    + ); +}; + +export { HomeCard }; diff --git a/src/components/HomeCard/index.ts b/src/components/HomeCard/index.ts new file mode 100644 index 0000000..a8d8fd3 --- /dev/null +++ b/src/components/HomeCard/index.ts @@ -0,0 +1,2 @@ +export * from "./HomeCard"; +export * from "./types"; diff --git a/src/components/HomeCard/types.ts b/src/components/HomeCard/types.ts new file mode 100644 index 0000000..2e2890b --- /dev/null +++ b/src/components/HomeCard/types.ts @@ -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 }; diff --git a/src/components/Member/Member.tsx b/src/components/Member/Member.tsx index d6d43e7..55bbabb 100644 --- a/src/components/Member/Member.tsx +++ b/src/components/Member/Member.tsx @@ -5,7 +5,7 @@ const Member: Component = (props: IMemberProps) => { return (
  • props.onMemberClick(props.id)} + onClick={() => props.onMemberClick?.(props.id)} >
    diff --git a/src/components/Member/types.ts b/src/components/Member/types.ts index 5cdf127..61f14c5 100644 --- a/src/components/Member/types.ts +++ b/src/components/Member/types.ts @@ -3,7 +3,7 @@ interface IMemberProps { username: string; avatar: string; active: boolean; - onMemberClick: (id: string) => void; + onMemberClick?: (id: string) => void; } export { type IMemberProps }; diff --git a/src/components/Message/Message.tsx b/src/components/Message/Message.tsx index 316c681..2c33ea6 100644 --- a/src/components/Message/Message.tsx +++ b/src/components/Message/Message.tsx @@ -6,7 +6,7 @@ const Message: Component = (props: IMessageProps) => {
  • props.onProfileClick(props.userId)} + onClick={() => props.onProfileClick?.(props.userId)} >
    diff --git a/src/components/Message/types.ts b/src/components/Message/types.ts index 90ca425..3f041f6 100644 --- a/src/components/Message/types.ts +++ b/src/components/Message/types.ts @@ -4,7 +4,7 @@ interface IMessageProps { userId: string; username: string; avatar: string; - onProfileClick: (userId: string) => void; + onProfileClick?: (userId: string) => void; } export { type IMessageProps }; diff --git a/src/components/MessageBar/MessageBar.tsx b/src/components/MessageBar/MessageBar.tsx index 47aedad..4e86497 100644 --- a/src/components/MessageBar/MessageBar.tsx +++ b/src/components/MessageBar/MessageBar.tsx @@ -1,5 +1,4 @@ import type { Component } from "solid-js"; -import { state } from "../../store/state"; const MessageBar: Component = () => { return ( @@ -8,7 +7,7 @@ const MessageBar: Component = () => { -
    diff --git a/src/components/SettingsModal/SettingsModal.tsx b/src/components/SettingsModal/SettingsModal.tsx new file mode 100644 index 0000000..911ac06 --- /dev/null +++ b/src/components/SettingsModal/SettingsModal.tsx @@ -0,0 +1,22 @@ +import type { Component } from "solid-js"; +import { ISettingsModalProps } from "./types"; + +const SettingsModal: Component = (props) => { + return ( +
    + + + + +
    + ); +}; + +export default SettingsModal; diff --git a/src/components/SettingsModal/index.ts b/src/components/SettingsModal/index.ts new file mode 100644 index 0000000..e0e8c4a --- /dev/null +++ b/src/components/SettingsModal/index.ts @@ -0,0 +1,2 @@ +export * from "./SettingsModal"; +export * from "./types"; diff --git a/src/components/SettingsModal/types.ts b/src/components/SettingsModal/types.ts new file mode 100644 index 0000000..4a0d823 --- /dev/null +++ b/src/components/SettingsModal/types.ts @@ -0,0 +1,6 @@ +interface ISettingsModalProps { + dialogRef?: (element: HTMLDialogElement) => void; + onClose?: () => void; +} + +export { type ISettingsModalProps }; diff --git a/src/components/SidebarItem/SidebarItem.tsx b/src/components/SidebarItem/SidebarItem.tsx new file mode 100644 index 0000000..3fe3472 --- /dev/null +++ b/src/components/SidebarItem/SidebarItem.tsx @@ -0,0 +1,18 @@ +import type { Component } from "solid-js"; +import { ISidebarItemProps } from "./types"; +import { Dynamic } from "solid-js/web"; + +const SidebarItem: Component = ( + props: ISidebarItemProps, +) => { + return ( +
    props.onClick?.()} + > + +
    + ); +}; + +export { SidebarItem }; diff --git a/src/components/SidebarItem/index.ts b/src/components/SidebarItem/index.ts new file mode 100644 index 0000000..28ad5f2 --- /dev/null +++ b/src/components/SidebarItem/index.ts @@ -0,0 +1,2 @@ +export * from "./SidebarItem"; +export * from "./types"; diff --git a/src/components/SidebarItem/types.ts b/src/components/SidebarItem/types.ts new file mode 100644 index 0000000..3ff2224 --- /dev/null +++ b/src/components/SidebarItem/types.ts @@ -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 }; diff --git a/src/components/icons/HomeIcon.tsx b/src/components/icons/HomeIcon.tsx new file mode 100644 index 0000000..af8d185 --- /dev/null +++ b/src/components/icons/HomeIcon.tsx @@ -0,0 +1,28 @@ +import type { Component } from "solid-js"; + +import { IconParameters, defaultFillIconParameters as defaults } from "./types"; + +const HomeIcon: Component = ({ + width, + height, + fill = defaults.fill, + stroke = defaults.stroke, + strokeWidth = defaults.strokeWidth, +}: IconParameters) => { + return ( + + + + + ); +}; + +export default HomeIcon; diff --git a/src/components/icons/PlusIcon.tsx b/src/components/icons/PlusIcon.tsx new file mode 100644 index 0000000..90d5456 --- /dev/null +++ b/src/components/icons/PlusIcon.tsx @@ -0,0 +1,31 @@ +import type { Component } from "solid-js"; + +import { IconParameters, defaultFillIconParameters as defaults } from "./types"; + +const PlusIcon: Component = ({ + width, + height, + fill = defaults.fill, + stroke = defaults.stroke, + strokeWidth = defaults.strokeWidth, +}: IconParameters) => { + return ( + + + + ); +}; + +export default PlusIcon; diff --git a/src/components/icons/SettingsIcon.tsx b/src/components/icons/SettingsIcon.tsx new file mode 100644 index 0000000..4b2dba0 --- /dev/null +++ b/src/components/icons/SettingsIcon.tsx @@ -0,0 +1,31 @@ +import type { Component } from "solid-js"; + +import { IconParameters, defaultFillIconParameters as defaults } from "./types"; + +const SettingsIcon: Component = ({ + width, + height, + fill = defaults.fill, + stroke = defaults.stroke, + strokeWidth = defaults.strokeWidth, +}: IconParameters) => { + return ( + + + + ); +}; + +export default SettingsIcon; diff --git a/src/components/icons/index.ts b/src/components/icons/index.ts new file mode 100644 index 0000000..d9d5e2a --- /dev/null +++ b/src/components/icons/index.ts @@ -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 }; diff --git a/src/components/icons/types.ts b/src/components/icons/types.ts new file mode 100644 index 0000000..2fb9fef --- /dev/null +++ b/src/components/icons/types.ts @@ -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 } \ No newline at end of file diff --git a/src/services/channel/channel.ts b/src/services/channel/channel.ts index aeab36c..773427d 100644 --- a/src/services/channel/channel.ts +++ b/src/services/channel/channel.ts @@ -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({ id: id, name: name, + description: description, }); dispatch({ diff --git a/src/services/community/community.ts b/src/services/community/community.ts index 206eff9..65040c9 100644 --- a/src/services/community/community.ts +++ b/src/services/community/community.ts @@ -1,14 +1,19 @@ import { fetchCommunityApi, + createCommunityApi, + updateCommunityApi, + removeCommunityApi, fetchCommunityChannelsApi, fetchCommunityRolesApi, fetchCommunityMembersApi, + fetchCommunityInvitesApi, } from "../../api/community"; import { CommunityActionTypes } from "../../store/community"; import { ChannelActionTypes } from "../../store/channel"; import { RoleActionTypes } from "../../store/role"; 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 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 data = await fetchCommunityChannelsApi({ 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 { fetchCommunity, + createCommunity, + updateCommunity, + removeCommunity, fetchCommunityChannels, fetchCommunityRoles, fetchCommunityMembers, + fetchCommunityInvites, }; diff --git a/src/services/invite/index.ts b/src/services/invite/index.ts new file mode 100644 index 0000000..d5a8bb7 --- /dev/null +++ b/src/services/invite/index.ts @@ -0,0 +1 @@ +export * from "./invite"; diff --git a/src/services/invite/invite.ts b/src/services/invite/invite.ts new file mode 100644 index 0000000..26a6f87 --- /dev/null +++ b/src/services/invite/invite.ts @@ -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 }; diff --git a/src/services/session/index.ts b/src/services/session/index.ts new file mode 100644 index 0000000..efe610f --- /dev/null +++ b/src/services/session/index.ts @@ -0,0 +1 @@ +export * from "./session"; diff --git a/src/services/session/session.ts b/src/services/session/session.ts new file mode 100644 index 0000000..6c319ac --- /dev/null +++ b/src/services/session/session.ts @@ -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 }; diff --git a/src/services/user/user.ts b/src/services/user/user.ts index 00193ce..f260f8f 100644 --- a/src/services/user/user.ts +++ b/src/services/user/user.ts @@ -1,11 +1,13 @@ import { fetchLoggedUserApi, fetchUserApi, + fetchUserSessionsApi, fetchUserCommunitiesApi, } from "../../api/user"; import { UserActionTypes } from "../../store/user"; import { CommunityActionTypes } from "../../store/community"; import { dispatch } from "../../store/state"; +import { SessionActionTypes } from "../../store/session"; const fetchLoggedUser = async () => { 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 data = await fetchUserCommunitiesApi({ id: id, @@ -45,4 +65,4 @@ const fetchUserCommunities = async (id: string) => { }); }; -export { fetchLoggedUser, fetchUser, fetchUserCommunities }; +export { fetchLoggedUser, fetchUser, fetchUserSessions, fetchUserCommunities }; diff --git a/src/store/actions.ts b/src/store/actions.ts index cfbeedb..bd664d9 100644 --- a/src/store/actions.ts +++ b/src/store/actions.ts @@ -1,21 +1,30 @@ +import { AppActionTypes, AppAction } from "./app"; import { AuthActionTypes, AuthAction } from "./auth"; import { UserActionTypes, UserAction } from "./user"; import { CommunityActionTypes, CommunityAction } from "./community"; import { ChannelActionTypes, ChannelAction } from "./channel"; import { RoleActionTypes, RoleAction } from "./role"; +import { SessionActionTypes, SessionAction } from "./session"; +import { InviteActionTypes, InviteAction } from "./invite"; type ActionTypes = + | AppActionTypes | AuthActionTypes | UserActionTypes | CommunityActionTypes | ChannelActionTypes - | RoleActionTypes; + | RoleActionTypes + | SessionActionTypes + | InviteActionTypes; type Action = + | AppAction | AuthAction | UserAction | CommunityAction | ChannelAction - | RoleAction; + | RoleAction + | SessionAction + | InviteAction; export { type Action, type ActionTypes }; diff --git a/src/store/app/actions.ts b/src/store/app/actions.ts new file mode 100644 index 0000000..8c60c2d --- /dev/null +++ b/src/store/app/actions.ts @@ -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 }; diff --git a/src/store/app/app.ts b/src/store/app/app.ts new file mode 100644 index 0000000..9df5573 --- /dev/null +++ b/src/store/app/app.ts @@ -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 }; diff --git a/src/store/app/index.ts b/src/store/app/index.ts new file mode 100644 index 0000000..c516023 --- /dev/null +++ b/src/store/app/index.ts @@ -0,0 +1,3 @@ +export * from "./app"; +export * from "./actions"; +export * from "./types"; diff --git a/src/store/app/types.ts b/src/store/app/types.ts new file mode 100644 index 0000000..8091315 --- /dev/null +++ b/src/store/app/types.ts @@ -0,0 +1,11 @@ +interface IAppState { + homeOpen: boolean; + dialogsOpen: IDialogsOpen; +} + +interface IDialogsOpen { + settingsOpen: boolean; + addCommunityOpen: boolean; +} + +export { type IAppState, type IDialogsOpen }; diff --git a/src/store/channel/actions.ts b/src/store/channel/actions.ts index dacfa99..6c29458 100644 --- a/src/store/channel/actions.ts +++ b/src/store/channel/actions.ts @@ -1,5 +1,6 @@ import { ICreateChannelRequest, + IUpdateChannelRequest, IFetchChannelResponse, ICreateChannelResponse, IUpdateChannelResponse, @@ -27,7 +28,7 @@ type ChannelAction = } | { type: ChannelActionTypes.SET_ACTIVE_CHANNEL; - payload: string; + payload: string | undefined; } | { type: ChannelActionTypes.FETCH_CHANNEL_START; payload: string } | { @@ -42,7 +43,10 @@ type ChannelAction = type: ChannelActionTypes.CREATE_CHANNEL_FINISH; payload: ICreateChannelResponse; } - | { type: ChannelActionTypes.UPDATE_CHANNEL_START; payload: string } + | { + type: ChannelActionTypes.UPDATE_CHANNEL_START; + payload: IUpdateChannelRequest; + } | { type: ChannelActionTypes.UPDATE_CHANNEL_FINISH; payload: IUpdateChannelResponse; diff --git a/src/store/channel/channel.ts b/src/store/channel/channel.ts index d2fc70e..704f5ed 100644 --- a/src/store/channel/channel.ts +++ b/src/store/channel/channel.ts @@ -26,27 +26,26 @@ function channelReducer(state: IChannelState, action: ChannelAction) { createChannel(action.payload.name, action.payload.communityId); break; case ChannelActionTypes.CREATE_CHANNEL_FINISH: - setState("channel", "channels", { - ...state.channels, - [action.payload.id]: action.payload, - }); + setState("channel", "channels", action.payload.id, action.payload); break; case ChannelActionTypes.UPDATE_CHANNEL_START: - updateChannel(action.payload); + updateChannel( + action.payload.id, + action.payload.name, + action.payload.description, + ); break; case ChannelActionTypes.UPDATE_CHANNEL_FINISH: - setState("channel", "channels", { - ...state.channels, - [action.payload.id]: action.payload, - }); + setState("channel", "channels", action.payload.id, action.payload); break; case ChannelActionTypes.REMOVE_CHANNEL_START: removeChannel(action.payload); break; case ChannelActionTypes.REMOVE_CHANNEL_FINISH: - setState("channel", "channels", { - ...state.channels, - [action.payload.id]: undefined, + setState("channel", "channels", (channels) => { + const copy = { ...channels }; + delete copy[action.payload.id]; + return copy; }); break; } diff --git a/src/store/channel/types.ts b/src/store/channel/types.ts index c7646f0..1b23130 100644 --- a/src/store/channel/types.ts +++ b/src/store/channel/types.ts @@ -1,5 +1,5 @@ interface IChannelState { - active: string; + active?: string; channels: Record; } diff --git a/src/store/community/actions.ts b/src/store/community/actions.ts index 8cd1c43..b7c3713 100644 --- a/src/store/community/actions.ts +++ b/src/store/community/actions.ts @@ -1,8 +1,14 @@ import { + ICreateCommunityRequest, + IUpdateCommunityRequest, IFetchCommunityResponse, + ICreateCommunityResponse, + IUpdateCommunityResponse, + IRemoveCommunityResponse, IFetchCommunityChannelsResponse, IFetchCommunityRolesResponse, IFetchCommunityMembersResponse, + IFetchCommunityInvitesResponse, } from "../../api/community"; import { IFetchUserCommunity } from "../../api/user"; @@ -11,12 +17,20 @@ enum CommunityActionTypes { SET_ACTIVE_COMMUNITY = "SET_ACTIVE_COMMUNITY", FETCH_COMMUNITY_START = "FETCH_COMMUNITY_START", 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_FINISH = "FETCH_COMMUNITY_CHANNELS_FINISH", FETCH_COMMUNITY_ROLES_START = "FETCH_COMMUNITY_ROLES_START", FETCH_COMMUNITY_ROLES_FINISH = "FETCH_COMMUNITY_ROLES_FINISH", FETCH_COMMUNITY_MEMBERS_START = "FETCH_COMMUNITY_MEMBERS_START", 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 = @@ -26,13 +40,34 @@ type CommunityAction = } | { type: CommunityActionTypes.SET_ACTIVE_COMMUNITY; - payload: string; + payload: string | undefined; } | { type: CommunityActionTypes.FETCH_COMMUNITY_START; payload: string } | { type: CommunityActionTypes.FETCH_COMMUNITY_FINISH; 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; payload: string; @@ -56,6 +91,14 @@ type CommunityAction = | { type: CommunityActionTypes.FETCH_COMMUNITY_MEMBERS_FINISH; payload: IFetchCommunityMembersResponse; + } + | { + type: CommunityActionTypes.FETCH_COMMUNITY_INVITES_START; + payload: string; + } + | { + type: CommunityActionTypes.FETCH_COMMUNITY_INVITES_FINISH; + payload: IFetchCommunityInvitesResponse; }; export { CommunityActionTypes, type CommunityAction }; diff --git a/src/store/community/community.ts b/src/store/community/community.ts index b9cb676..8a67c0f 100644 --- a/src/store/community/community.ts +++ b/src/store/community/community.ts @@ -1,8 +1,12 @@ import { fetchCommunity, + createCommunity, + updateCommunity, + removeCommunity, fetchCommunityChannels, fetchCommunityRoles, fetchCommunityMembers, + fetchCommunityInvites, } from "../../services/community"; import { setState } from "../state"; import { CommunityActionTypes, CommunityAction } from "./actions"; @@ -32,6 +36,42 @@ function communityReducer(state: ICommunityState, action: CommunityAction) { action.payload, ); 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: fetchCommunityChannels(action.payload); break; @@ -56,6 +96,14 @@ function communityReducer(state: ICommunityState, action: CommunityAction) { members: action.payload.members.map((member) => member.id), }); 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; } } diff --git a/src/store/community/types.ts b/src/store/community/types.ts index f12663b..cab5dce 100644 --- a/src/store/community/types.ts +++ b/src/store/community/types.ts @@ -1,5 +1,5 @@ interface ICommunityState { - active: string; + active?: string; communities: Record; } @@ -12,6 +12,7 @@ interface ICommunity { channels?: string[]; roles?: string[]; members?: string[]; + invites?: string[]; } export { type ICommunityState, type ICommunity }; diff --git a/src/store/invite/actions.ts b/src/store/invite/actions.ts new file mode 100644 index 0000000..884e6e4 --- /dev/null +++ b/src/store/invite/actions.ts @@ -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 }; diff --git a/src/store/invite/index.ts b/src/store/invite/index.ts new file mode 100644 index 0000000..78cfeec --- /dev/null +++ b/src/store/invite/index.ts @@ -0,0 +1,3 @@ +export * from "./invite"; +export * from "./actions"; +export * from "./types"; diff --git a/src/store/invite/invite.ts b/src/store/invite/invite.ts new file mode 100644 index 0000000..1123afd --- /dev/null +++ b/src/store/invite/invite.ts @@ -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 }; diff --git a/src/store/invite/types.ts b/src/store/invite/types.ts new file mode 100644 index 0000000..f606cc6 --- /dev/null +++ b/src/store/invite/types.ts @@ -0,0 +1,17 @@ +interface IInviteState { + invites: Record; +} + +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 }; diff --git a/src/store/reducers.ts b/src/store/reducers.ts index aa54721..ca6376c 100644 --- a/src/store/reducers.ts +++ b/src/store/reducers.ts @@ -1,17 +1,23 @@ import { IState } from "./types"; import { Action } from "./actions"; +import { AppAction, appReducer } from "./app"; import { AuthAction, authReducer } from "./auth"; import { UserAction, userReducer } from "./user"; import { CommunityAction, communityReducer } from "./community"; import { ChannelAction, channelReducer } from "./channel"; import { RoleAction, roleReducer } from "./role"; +import { SessionAction, sessionReducer } from "./session"; +import { InviteAction, inviteReducer } from "./invite"; function reducer(state: IState, action: Action) { + appReducer(state.app, action as AppAction); authReducer(state.auth, action as AuthAction); userReducer(state.user, action as UserAction); communityReducer(state.community, action as CommunityAction); channelReducer(state.channel, action as ChannelAction); roleReducer(state.role, action as RoleAction); + sessionReducer(state.session, action as SessionAction); + inviteReducer(state.invite, action as InviteAction); } export { reducer }; diff --git a/src/store/role/role.ts b/src/store/role/role.ts index ac9fb2b..7428257 100644 --- a/src/store/role/role.ts +++ b/src/store/role/role.ts @@ -23,27 +23,22 @@ function roleReducer(state: IRoleState, action: RoleAction) { createRole(action.payload.name, action.payload.communityId); break; case RoleActionTypes.CREATE_ROLE_FINISH: - setState("role", "roles", { - ...state.roles, - [action.payload.id]: action.payload, - }); + setState("role", "roles", action.payload.id, action.payload); break; case RoleActionTypes.UPDATE_ROLE_START: updateRole(action.payload); break; case RoleActionTypes.UPDATE_ROLE_FINISH: - setState("role", "roles", { - ...state.roles, - [action.payload.id]: action.payload, - }); + setState("role", "roles", action.payload.id, action.payload); break; case RoleActionTypes.REMOVE_ROLE_START: removeRole(action.payload); break; case RoleActionTypes.REMOVE_ROLE_FINISH: - setState("role", "roles", { - ...state.roles, - [action.payload.id]: undefined, + setState("role", "roles", (roles) => { + const copy = { ...roles }; + delete copy[action.payload.id]; + return copy; }); break; } diff --git a/src/store/session/actions.ts b/src/store/session/actions.ts new file mode 100644 index 0000000..9cd2fd9 --- /dev/null +++ b/src/store/session/actions.ts @@ -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 }; diff --git a/src/store/session/index.ts b/src/store/session/index.ts new file mode 100644 index 0000000..1024550 --- /dev/null +++ b/src/store/session/index.ts @@ -0,0 +1,3 @@ +export * from "./session"; +export * from "./actions"; +export * from "./types"; diff --git a/src/store/session/session.ts b/src/store/session/session.ts new file mode 100644 index 0000000..a3133d2 --- /dev/null +++ b/src/store/session/session.ts @@ -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 }; diff --git a/src/store/session/types.ts b/src/store/session/types.ts new file mode 100644 index 0000000..bee14e1 --- /dev/null +++ b/src/store/session/types.ts @@ -0,0 +1,11 @@ +interface ISessionState { + sessions: Record; +} + +interface ISession { + id: string; + userId?: string; + creationDate?: number; +} + +export { type ISessionState, type ISession }; diff --git a/src/store/state.ts b/src/store/state.ts index d9b2380..4710d2f 100644 --- a/src/store/state.ts +++ b/src/store/state.ts @@ -4,6 +4,13 @@ import { Action } from "./actions"; import { reducer } from "./reducers"; const [state, setState] = createStore({ + app: { + homeOpen: true, + dialogsOpen: { + settingsOpen: false, + addCommunityOpen: false, + }, + }, auth: { session: undefined, }, @@ -20,6 +27,12 @@ const [state, setState] = createStore({ role: { roles: {}, }, + session: { + sessions: {}, + }, + invite: { + invites: {}, + }, }); function dispatch(action: Action) { diff --git a/src/store/types.ts b/src/store/types.ts index 894ea7e..e0e7ced 100644 --- a/src/store/types.ts +++ b/src/store/types.ts @@ -1,15 +1,21 @@ +import { IAppState } from "./app"; import { IAuthState } from "./auth"; import { IUserState } from "./user"; import { ICommunityState } from "./community"; import { IChannelState } from "./channel"; import { IRoleState } from "./role"; +import { ISessionState } from "./session"; +import { IInviteState } from "./invite"; interface IState { + app: IAppState; auth: IAuthState; user: IUserState; community: ICommunityState; channel: IChannelState; role: IRoleState; + session: ISessionState; + invite: IInviteState; } export { type IState }; diff --git a/src/store/user/actions.ts b/src/store/user/actions.ts index 5065531..e292919 100644 --- a/src/store/user/actions.ts +++ b/src/store/user/actions.ts @@ -2,6 +2,7 @@ import { IFetchCommunityMember } from "../../api/community"; import { IFetchLoggedUserResponse, IFetchUserResponse, + IFetchUserSessionsResponse, IFetchUserCommunitiesResponse, } from "../../api/user"; @@ -11,6 +12,8 @@ enum UserActionTypes { FETCH_LOGGED_USER_ID_FINISH = "FETCH_LOGGED_USER_ID_FINISH", FETCH_USER_START = "FETCH_USER_START", 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_FINISH = "FETCH_USER_COMMUNITIES_FINISH", } @@ -27,6 +30,11 @@ type UserAction = } | { type: UserActionTypes.FETCH_USER_START; payload: string } | { 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_FINISH; diff --git a/src/store/user/types.ts b/src/store/user/types.ts index c8cd60d..edd258e 100644 --- a/src/store/user/types.ts +++ b/src/store/user/types.ts @@ -11,6 +11,7 @@ interface IUser { admin?: boolean; registerDate?: number; lastLogin?: number; + sessions?: string[]; communities?: string[]; } diff --git a/src/store/user/user.ts b/src/store/user/user.ts index 65d8d5f..c8511b0 100644 --- a/src/store/user/user.ts +++ b/src/store/user/user.ts @@ -1,6 +1,7 @@ import { fetchLoggedUser, fetchUser, + fetchUserSessions, fetchUserCommunities, } from "../../services/user"; import { setState } from "../state"; @@ -24,6 +25,14 @@ function userReducer(state: IUserState, action: UserAction) { case UserActionTypes.FETCH_USER_FINISH: setState("user", "users", action.payload.id, action.payload); 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: fetchUserCommunities(action.payload); break; diff --git a/src/views/ChannelView/ChannelView.tsx b/src/views/ChannelView/ChannelView.tsx index 438358d..edf21ab 100644 --- a/src/views/ChannelView/ChannelView.tsx +++ b/src/views/ChannelView/ChannelView.tsx @@ -1,5 +1,4 @@ import { createEffect, createMemo, type Component } from "solid-js"; -import { CommunityView } from "../CommunityView"; import { dispatch, state } from "../../store/state"; import { CommunityActionTypes, ICommunity } from "../../store/community"; import { ChannelActionTypes } from "../../store/channel"; @@ -23,7 +22,7 @@ const ChannelView: Component = () => { const communityInfo = createMemo(() => { const activeCommunityId = state.community.active; - return state.community.communities[activeCommunityId]; + return state.community.communities[activeCommunityId ?? 0]; }); createEffect(() => { @@ -63,21 +62,18 @@ const ChannelView: Component = () => { }; return ( -
    - -
    - -
      - {channelIds().map(mapChannel)} -
    -
    +
    + +
      + {channelIds().map(mapChannel)} +
    ); }; diff --git a/src/views/ChatView/ChatView.tsx b/src/views/ChatView/ChatView.tsx index 5b92fbb..2954fb7 100644 --- a/src/views/ChatView/ChatView.tsx +++ b/src/views/ChatView/ChatView.tsx @@ -180,7 +180,7 @@ const ChatView: Component = () => { const channelInfo = createMemo(() => { const activeChannelId = state.channel.active; - return state.channel.channels[activeChannelId]; + return state.channel.channels[activeChannelId ?? 0]; }); let scrollRef: HTMLUListElement | undefined; diff --git a/src/views/CommunityView/CommunityView.tsx b/src/views/CommunityView/CommunityView.tsx index f1adb71..ffd2aab 100644 --- a/src/views/CommunityView/CommunityView.tsx +++ b/src/views/CommunityView/CommunityView.tsx @@ -2,6 +2,9 @@ import { createMemo, type Component } from "solid-js"; import { Community } from "../../components/Community"; import { dispatch, state } from "../../store/state"; 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 communityIds = createMemo(() => { @@ -18,7 +21,25 @@ const CommunityView: Component = () => { 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) => { + if (state.app.homeOpen) { + dispatch({ + type: AppActionTypes.SET_HOME_OPEN, + payload: false, + }); + } + dispatch({ type: CommunityActionTypes.FETCH_COMMUNITY_START, 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 community = state.community.communities[communityId]; if (!community) { @@ -49,8 +84,22 @@ const CommunityView: Component = () => { }; return ( -
    +
    + +
    {communityIds().map(mapCommunity)} +
    + +
    +
    ); }; diff --git a/src/views/HomeView/HomeView.tsx b/src/views/HomeView/HomeView.tsx new file mode 100644 index 0000000..226c710 --- /dev/null +++ b/src/views/HomeView/HomeView.tsx @@ -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 ( +
    +
    + + +
    +
    + ); +}; + +export { HomeView }; diff --git a/src/views/HomeView/index.ts b/src/views/HomeView/index.ts new file mode 100644 index 0000000..030411f --- /dev/null +++ b/src/views/HomeView/index.ts @@ -0,0 +1 @@ +export * from "./HomeView"; diff --git a/src/views/MainView/MainView.tsx b/src/views/MainView/MainView.tsx index 71c6d45..0b609d8 100644 --- a/src/views/MainView/MainView.tsx +++ b/src/views/MainView/MainView.tsx @@ -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 { ChatView } from "../ChatView"; import { MemberView } from "../MemberView"; import { dispatch, state } from "../../store/state"; import { UserActionTypes } from "../../store/user"; import { AuthActionTypes } from "../../store/auth"; +import { HomeView } from "../HomeView"; +import { CommunityView } from "../CommunityView"; +import { ModalView } from "../ModalView"; const MainView: Component = () => { onMount(() => { @@ -30,9 +33,19 @@ const MainView: Component = () => { return (
    - - - + + + {state.app.homeOpen ? ( + <> + + + ) : ( + <> + + + + + )}
    ); }; diff --git a/src/views/ModalView/ModalView.tsx b/src/views/ModalView/ModalView.tsx new file mode 100644 index 0000000..241bd10 --- /dev/null +++ b/src/views/ModalView/ModalView.tsx @@ -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 = element)} + onClose={onCloseSettings} + /> + (communityModal = element)} + onClose={onCloseCommunity} + /> + + ); +}; + +export { ModalView }; diff --git a/src/views/ModalView/index.ts b/src/views/ModalView/index.ts new file mode 100644 index 0000000..e85b5db --- /dev/null +++ b/src/views/ModalView/index.ts @@ -0,0 +1 @@ +export * from "./ModalView";