Add real time messaging
This commit is contained in:
parent
0163eab540
commit
9153ba841d
61 changed files with 882 additions and 230 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "pulsar-web",
|
"name": "pulsar-web",
|
||||||
"version": "0.3.0",
|
"version": "0.4.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
10
src/App.tsx
10
src/App.tsx
|
|
@ -1,15 +1,17 @@
|
||||||
import { Route, Router } from "@solidjs/router";
|
import { Route, Router } from "@solidjs/router";
|
||||||
import type { Component } from "solid-js";
|
import type { Component } from "solid-js";
|
||||||
import { MainView } from "./views/MainView";
|
import { StartView } from "./views/StartView";
|
||||||
import { SettingsView } from "./views/SettingsView";
|
import { AppView } from "./views/AppView";
|
||||||
import { LoginView } from "./views/LoginView";
|
import { LoginView } from "./views/LoginView";
|
||||||
|
import { RegisterView } from "./views/RegisterView";
|
||||||
|
|
||||||
const App: Component = () => {
|
const App: Component = () => {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<Route path="/" component={MainView} />
|
<Route path="/" component={StartView} />
|
||||||
<Route path="/settings" component={SettingsView} />
|
<Route path="/app" component={AppView} />
|
||||||
<Route path="/login" component={LoginView} />
|
<Route path="/login" component={LoginView} />
|
||||||
|
<Route path="/register" component={RegisterView} />
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ import { callApi, HTTP } from "../tools";
|
||||||
import {
|
import {
|
||||||
IFetchLoginRequest,
|
IFetchLoginRequest,
|
||||||
IFetchLoginResponse,
|
IFetchLoginResponse,
|
||||||
IFetchRefreshResponse,
|
IFetchRefreshResponseError,
|
||||||
|
IFetchRefreshResponseSuccess,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
const fetchLoginApi = async (
|
const fetchLoginApi = async (
|
||||||
|
|
@ -11,7 +12,9 @@ const fetchLoginApi = async (
|
||||||
return await callApi(HTTP.POST, `auth/login`, request, true);
|
return await callApi(HTTP.POST, `auth/login`, request, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchRefreshApi = async (): Promise<IFetchRefreshResponse> => {
|
const fetchRefreshApi = async (): Promise<
|
||||||
|
IFetchRefreshResponseError | IFetchRefreshResponseSuccess
|
||||||
|
> => {
|
||||||
return await callApi(HTTP.GET, `auth/refresh`, undefined, true);
|
return await callApi(HTTP.GET, `auth/refresh`, undefined, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,11 @@ interface IFetchLoginResponse {
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchRefreshResponse {
|
interface IFetchRefreshResponseError {
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFetchRefreshResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
token: string;
|
token: string;
|
||||||
|
|
@ -17,5 +21,6 @@ interface IFetchRefreshResponse {
|
||||||
export {
|
export {
|
||||||
type IFetchLoginRequest,
|
type IFetchLoginRequest,
|
||||||
type IFetchLoginResponse,
|
type IFetchLoginResponse,
|
||||||
type IFetchRefreshResponse,
|
type IFetchRefreshResponseError,
|
||||||
|
type IFetchRefreshResponseSuccess,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import {
|
||||||
IUpdateChannelResponse,
|
IUpdateChannelResponse,
|
||||||
IRemoveChannelRequest,
|
IRemoveChannelRequest,
|
||||||
IRemoveChannelResponse,
|
IRemoveChannelResponse,
|
||||||
|
IFetchChannelMessagesRequest,
|
||||||
|
IFetchChannelMessagesResponse,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
const fetchChannelApi = async (
|
const fetchChannelApi = async (
|
||||||
|
|
@ -34,9 +36,16 @@ const removeChannelApi = async (
|
||||||
return await callApi(HTTP.DELETE, `channel/${request.id}`);
|
return await callApi(HTTP.DELETE, `channel/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchChannelMessagesApi = async (
|
||||||
|
request: IFetchChannelMessagesRequest,
|
||||||
|
): Promise<IFetchChannelMessagesResponse> => {
|
||||||
|
return await callApi(HTTP.GET, `channel/${request.id}/messages`);
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
fetchChannelApi,
|
fetchChannelApi,
|
||||||
createChannelApi,
|
createChannelApi,
|
||||||
updateChannelApi,
|
updateChannelApi,
|
||||||
removeChannelApi,
|
removeChannelApi,
|
||||||
|
fetchChannelMessagesApi,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,23 @@ interface IRemoveChannelResponse {
|
||||||
communityId: string;
|
communityId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IFetchChannelMessagesRequest {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFetchChannelMessagesResponse {
|
||||||
|
id: string;
|
||||||
|
messages: IFetchChannelMessage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFetchChannelMessage {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
edited: boolean;
|
||||||
|
ownerId: string;
|
||||||
|
creationDate: number;
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type IFetchChannel,
|
type IFetchChannel,
|
||||||
type IFetchChannelRequest,
|
type IFetchChannelRequest,
|
||||||
|
|
@ -46,4 +63,7 @@ export {
|
||||||
type IUpdateChannelResponse,
|
type IUpdateChannelResponse,
|
||||||
type IRemoveChannelResponse,
|
type IRemoveChannelResponse,
|
||||||
type IRemoveChannelRequest,
|
type IRemoveChannelRequest,
|
||||||
|
type IFetchChannelMessagesRequest,
|
||||||
|
type IFetchChannelMessagesResponse,
|
||||||
|
type IFetchChannelMessage,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
2
src/api/message/index.ts
Normal file
2
src/api/message/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./message";
|
||||||
|
export * from "./types";
|
||||||
42
src/api/message/message.ts
Normal file
42
src/api/message/message.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { callApi, HTTP } from "../tools";
|
||||||
|
import {
|
||||||
|
IFetchMessageRequest,
|
||||||
|
IFetchMessageResponse,
|
||||||
|
ICreateMessageRequest,
|
||||||
|
ICreateMessageResponse,
|
||||||
|
IUpdateMessageRequest,
|
||||||
|
IUpdateMessageResponse,
|
||||||
|
IRemoveMessageRequest,
|
||||||
|
IRemoveMessageResponse,
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
|
const fetchMessageApi = async (
|
||||||
|
request: IFetchMessageRequest,
|
||||||
|
): Promise<IFetchMessageResponse> => {
|
||||||
|
return await callApi(HTTP.GET, `message/${request.id}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createMessageApi = async (
|
||||||
|
request: ICreateMessageRequest,
|
||||||
|
): Promise<ICreateMessageResponse> => {
|
||||||
|
return await callApi(HTTP.POST, `message`, request);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateMessageApi = async (
|
||||||
|
request: IUpdateMessageRequest,
|
||||||
|
): Promise<IUpdateMessageResponse> => {
|
||||||
|
return await callApi(HTTP.PATCH, `message/${request.id}`, request);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeMessageApi = async (
|
||||||
|
request: IRemoveMessageRequest,
|
||||||
|
): Promise<IRemoveMessageResponse> => {
|
||||||
|
return await callApi(HTTP.DELETE, `message/${request.id}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
fetchMessageApi,
|
||||||
|
createMessageApi,
|
||||||
|
updateMessageApi,
|
||||||
|
removeMessageApi,
|
||||||
|
};
|
||||||
51
src/api/message/types.ts
Normal file
51
src/api/message/types.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
interface IFetchMessage {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
editHistory: string[];
|
||||||
|
edited: boolean;
|
||||||
|
ownerId: string;
|
||||||
|
channelId: string;
|
||||||
|
creationDate: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFetchMessageRequest {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFetchMessageResponse extends IFetchMessage {}
|
||||||
|
|
||||||
|
interface ICreateMessageRequest {
|
||||||
|
text: string;
|
||||||
|
channelId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICreateMessageResponse extends IFetchMessage {}
|
||||||
|
|
||||||
|
interface IUpdateMessageRequest {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUpdateMessageResponse extends IFetchMessage {}
|
||||||
|
|
||||||
|
interface IRemoveMessageRequest {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRemoveMessageResponse {
|
||||||
|
id: string;
|
||||||
|
ownerId: string;
|
||||||
|
channelId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
type IFetchMessage,
|
||||||
|
type IFetchMessageRequest,
|
||||||
|
type IFetchMessageResponse,
|
||||||
|
type ICreateMessageRequest,
|
||||||
|
type ICreateMessageResponse,
|
||||||
|
type IUpdateMessageRequest,
|
||||||
|
type IUpdateMessageResponse,
|
||||||
|
type IRemoveMessageResponse,
|
||||||
|
type IRemoveMessageRequest,
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Component } from "solid-js";
|
import type { Component } from "solid-js";
|
||||||
import { ICommunityBarProps } from "./types";
|
import { ICommunityBarProps } from "./types";
|
||||||
|
import { SettingsIcon } from "../icons";
|
||||||
|
|
||||||
const CommunityBar: Component<ICommunityBarProps> = (
|
const CommunityBar: Component<ICommunityBarProps> = (
|
||||||
props: ICommunityBarProps,
|
props: ICommunityBarProps,
|
||||||
|
|
@ -8,10 +9,18 @@ const CommunityBar: Component<ICommunityBarProps> = (
|
||||||
<div
|
<div
|
||||||
class={`absolute w-full top-0 z-10 bg-cover bg-top bg-no-repeat bg-[url('${props.avatar}')]`}
|
class={`absolute w-full top-0 z-10 bg-cover bg-top bg-no-repeat bg-[url('${props.avatar}')]`}
|
||||||
>
|
>
|
||||||
<div class="flex flex-col justify-center bg-stone-800/25 backdrop-blur-md h-16 w-full shadow-bar px-5">
|
<div class="flex flex-row justify-between items-center bg-stone-800/25 backdrop-blur-md h-16 shadow-bar pl-5 pr-3">
|
||||||
|
<div class="flex flex-col justify-center">
|
||||||
<h2 class="text-sm font-bold">{props.name}</h2>
|
<h2 class="text-sm font-bold">{props.name}</h2>
|
||||||
<p class="text-xs">{props.description}</p>
|
<p class="text-xs">{props.description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="bg-stone-950/25 cursor-pointer rounded-full w-10 h-10 p-2 hover:bg-stone-950/75"
|
||||||
|
onClick={props.onSettingsClick}
|
||||||
|
>
|
||||||
|
<SettingsIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ interface ICommunityBarProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
|
onSettingsClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { type ICommunityBarProps };
|
export { type ICommunityBarProps };
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,19 @@ import { CommunityActionTypes } from "../../store/community";
|
||||||
import { InviteActionTypes } from "../../store/invite";
|
import { InviteActionTypes } from "../../store/invite";
|
||||||
|
|
||||||
const CommunityModal: Component<ICommunityModalProps> = (props) => {
|
const CommunityModal: Component<ICommunityModalProps> = (props) => {
|
||||||
const [getCommunityName, setCommunityName] = createSignal<string>("");
|
const [getCommunityName, setCommunityName] = createSignal("");
|
||||||
const [getInviteId, setInviteId] = createSignal<string>("");
|
const [getInviteId, setInviteId] = createSignal("");
|
||||||
|
|
||||||
const onCreateCommunity = () => {
|
const onCreateCommunity = () => {
|
||||||
|
const communityName = getCommunityName();
|
||||||
|
if (!communityName || communityName.trim().length < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CommunityActionTypes.CREATE_COMMUNITY_START,
|
type: CommunityActionTypes.CREATE_COMMUNITY_START,
|
||||||
payload: {
|
payload: {
|
||||||
name: getCommunityName(),
|
name: communityName,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -22,9 +27,14 @@ const CommunityModal: Component<ICommunityModalProps> = (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onJoinCommunity = () => {
|
const onJoinCommunity = () => {
|
||||||
|
const inviteId = getInviteId();
|
||||||
|
if (!inviteId || inviteId.trim().length < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: InviteActionTypes.ACCEPT_INVITE_START,
|
type: InviteActionTypes.ACCEPT_INVITE_START,
|
||||||
payload: getInviteId(),
|
payload: inviteId,
|
||||||
});
|
});
|
||||||
|
|
||||||
setInviteId("");
|
setInviteId("");
|
||||||
|
|
@ -32,6 +42,12 @@ const CommunityModal: Component<ICommunityModalProps> = (props) => {
|
||||||
props.onClose?.();
|
props.onClose?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleEnter = (e: KeyboardEvent, callback: () => void) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const createCommunityHtml = () => (
|
const createCommunityHtml = () => (
|
||||||
<>
|
<>
|
||||||
<h3 class="text-lg font-bold text-center mb-6">
|
<h3 class="text-lg font-bold text-center mb-6">
|
||||||
|
|
@ -44,6 +60,7 @@ const CommunityModal: Component<ICommunityModalProps> = (props) => {
|
||||||
placeholder="Enter name of the new community"
|
placeholder="Enter name of the new community"
|
||||||
value={getCommunityName()}
|
value={getCommunityName()}
|
||||||
onInput={(e) => setCommunityName(e.currentTarget.value)}
|
onInput={(e) => setCommunityName(e.currentTarget.value)}
|
||||||
|
onKeyDown={(e) => handleEnter(e, onCreateCommunity)}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<button
|
<button
|
||||||
|
|
@ -68,6 +85,7 @@ const CommunityModal: Component<ICommunityModalProps> = (props) => {
|
||||||
placeholder="Enter invite ID"
|
placeholder="Enter invite ID"
|
||||||
value={getInviteId()}
|
value={getInviteId()}
|
||||||
onInput={(e) => setInviteId(e.currentTarget.value)}
|
onInput={(e) => setInviteId(e.currentTarget.value)}
|
||||||
|
onKeyDown={(e) => handleEnter(e, onJoinCommunity)}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import type { Component } from "solid-js";
|
||||||
|
import { ICommunitySettingsModalProps } from "./types";
|
||||||
|
|
||||||
|
const CommunitySettingsModal: Component<ICommunitySettingsModalProps> = (
|
||||||
|
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">
|
||||||
|
Community 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 CommunitySettingsModal;
|
||||||
2
src/components/CommunitySettingsModal/index.ts
Normal file
2
src/components/CommunitySettingsModal/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./CommunitySettingsModal";
|
||||||
|
export * from "./types";
|
||||||
6
src/components/CommunitySettingsModal/types.ts
Normal file
6
src/components/CommunitySettingsModal/types.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
interface ICommunitySettingsModalProps {
|
||||||
|
dialogRef?: (element: HTMLDialogElement) => void;
|
||||||
|
onClose?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { type ICommunitySettingsModalProps };
|
||||||
|
|
@ -1,13 +1,31 @@
|
||||||
import type { Component } from "solid-js";
|
import type { Component } from "solid-js";
|
||||||
|
import { IMessageBarProps } from "./types";
|
||||||
|
|
||||||
|
const MessageBar: Component<IMessageBarProps> = (props: IMessageBarProps) => {
|
||||||
|
const handleEnter = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
props.onSend?.();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const MessageBar: Component = () => {
|
|
||||||
return (
|
return (
|
||||||
<div class="absolute w-full bottom-0 p-4 z-10">
|
<div class="absolute w-full bottom-0 p-4 z-10">
|
||||||
<div class="bg-stone-800/25 backdrop-blur-lg h-16 shadow-bar p-2 flex flex-row gap-2 rounded-full">
|
<div class="bg-stone-800/25 backdrop-blur-lg h-16 shadow-bar p-2 flex flex-row gap-2 rounded-full">
|
||||||
<label class="bg-stone-800/50 backdrop-blur-lg input w-full h-full rounded-full focus:border-none outline-none">
|
<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..."
|
||||||
|
value={props.text}
|
||||||
|
onInput={(e) =>
|
||||||
|
props.onChangeText?.(e.currentTarget.value)
|
||||||
|
}
|
||||||
|
onKeyDown={handleEnter}
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
<button class="bg-stone-950/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"
|
||||||
|
onClick={props.onSend}
|
||||||
|
>
|
||||||
Send
|
Send
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
export * from "./MessageBar";
|
export * from "./MessageBar";
|
||||||
|
export * from "./types";
|
||||||
|
|
|
||||||
7
src/components/MessageBar/types.ts
Normal file
7
src/components/MessageBar/types.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
interface IMessageBarProps {
|
||||||
|
text: string;
|
||||||
|
onChangeText?: (text: string) => void;
|
||||||
|
onSend?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { type IMessageBarProps };
|
||||||
|
|
@ -12,6 +12,10 @@ const fetchLogin = async (username: string, password: string) => {
|
||||||
type: AuthActionTypes.FETCH_LOGIN_FINISH,
|
type: AuthActionTypes.FETCH_LOGIN_FINISH,
|
||||||
payload: data,
|
payload: data,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: AuthActionTypes.FETCH_REFRESH_START,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchRefresh = async () => {
|
const fetchRefresh = async () => {
|
||||||
|
|
@ -21,6 +25,11 @@ const fetchRefresh = async () => {
|
||||||
type: AuthActionTypes.FETCH_REFRESH_FINISH,
|
type: AuthActionTypes.FETCH_REFRESH_FINISH,
|
||||||
payload: data,
|
payload: data,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: AuthActionTypes.SET_LOGGED_IN,
|
||||||
|
payload: "id" in data,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export { fetchLogin, fetchRefresh };
|
export { fetchLogin, fetchRefresh };
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import {
|
||||||
createChannelApi,
|
createChannelApi,
|
||||||
updateChannelApi,
|
updateChannelApi,
|
||||||
removeChannelApi,
|
removeChannelApi,
|
||||||
|
fetchChannelMessagesApi,
|
||||||
} from "../../api/channel";
|
} from "../../api/channel";
|
||||||
import { ChannelActionTypes } from "../../store/channel";
|
import { ChannelActionTypes } from "../../store/channel";
|
||||||
import { dispatch } from "../../store/state";
|
import { dispatch } from "../../store/state";
|
||||||
|
|
@ -58,4 +59,21 @@ const removeChannel = async (id: string) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export { fetchChannel, createChannel, updateChannel, removeChannel };
|
const fetchChannelMessages = async (id: string) => {
|
||||||
|
const data = await fetchChannelMessagesApi({
|
||||||
|
id: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ChannelActionTypes.FETCH_CHANNEL_MESSAGES_FINISH,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
fetchChannel,
|
||||||
|
createChannel,
|
||||||
|
updateChannel,
|
||||||
|
removeChannel,
|
||||||
|
fetchChannelMessages,
|
||||||
|
};
|
||||||
|
|
|
||||||
1
src/services/message/index.ts
Normal file
1
src/services/message/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./message";
|
||||||
56
src/services/message/message.ts
Normal file
56
src/services/message/message.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import {
|
||||||
|
fetchMessageApi,
|
||||||
|
createMessageApi,
|
||||||
|
updateMessageApi,
|
||||||
|
removeMessageApi,
|
||||||
|
} from "../../api/message";
|
||||||
|
import { MessageActionTypes } from "../../store/message";
|
||||||
|
import { dispatch } from "../../store/state";
|
||||||
|
|
||||||
|
const fetchMessage = async (id: string) => {
|
||||||
|
const data = await fetchMessageApi({
|
||||||
|
id: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: MessageActionTypes.FETCH_MESSAGE_FINISH,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createMessage = async (text: string, channelId: string) => {
|
||||||
|
const data = await createMessageApi({
|
||||||
|
text: text,
|
||||||
|
channelId: channelId,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: MessageActionTypes.CREATE_MESSAGE_FINISH,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateMessage = async (id: string, text: string) => {
|
||||||
|
const data = await updateMessageApi({
|
||||||
|
id: id,
|
||||||
|
text: text,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: MessageActionTypes.UPDATE_MESSAGE_FINISH,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeMessage = async (id: string) => {
|
||||||
|
const data = await removeMessageApi({
|
||||||
|
id: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: MessageActionTypes.REMOVE_MESSAGE_FINISH,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { fetchMessage, createMessage, updateMessage, removeMessage };
|
||||||
6
src/services/websocket/config.json
Normal file
6
src/services/websocket/config.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"schema": "ws",
|
||||||
|
"url": "localhost",
|
||||||
|
"port": 3012,
|
||||||
|
"path": "ws"
|
||||||
|
}
|
||||||
2
src/services/websocket/index.ts
Normal file
2
src/services/websocket/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./websocket";
|
||||||
|
export * from "./types";
|
||||||
46
src/services/websocket/types.ts
Normal file
46
src/services/websocket/types.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { IFetchChannelMessage } from "../../api/channel";
|
||||||
|
|
||||||
|
enum SocketRequestTypes {
|
||||||
|
PING = "PING",
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SocketMessageTypes {
|
||||||
|
NEW_ANNOUNCEMENT = "NEW_ANNOUNCEMENT",
|
||||||
|
NEW_MESSAGE = "NEW_MESSAGE",
|
||||||
|
NEW_CHANNEL = "NEW_CHANNEL",
|
||||||
|
}
|
||||||
|
|
||||||
|
type SocketRequest = {
|
||||||
|
type: SocketRequestTypes.PING;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SocketMessage =
|
||||||
|
| {
|
||||||
|
type: SocketMessageTypes.NEW_ANNOUNCEMENT;
|
||||||
|
payload: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: SocketMessageTypes.NEW_MESSAGE;
|
||||||
|
payload: {
|
||||||
|
channelId: string;
|
||||||
|
message: IFetchChannelMessage;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: SocketMessageTypes.NEW_CHANNEL;
|
||||||
|
payload: {
|
||||||
|
id: string;
|
||||||
|
communityId: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
SocketRequestTypes,
|
||||||
|
SocketMessageTypes,
|
||||||
|
type SocketRequest,
|
||||||
|
type SocketMessage,
|
||||||
|
};
|
||||||
57
src/services/websocket/websocket.ts
Normal file
57
src/services/websocket/websocket.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { ChannelActionTypes } from "../../store/channel";
|
||||||
|
import { dispatch } from "../../store/state";
|
||||||
|
import config from "./config.json";
|
||||||
|
import { SocketMessage, SocketMessageTypes } from "./types";
|
||||||
|
|
||||||
|
let connection: WebSocket;
|
||||||
|
|
||||||
|
const connectWs = () => {
|
||||||
|
connection = new WebSocket(
|
||||||
|
`${config.schema}://${config.url}:${config.port}/${config.path}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
connection.onclose = onCloseWs;
|
||||||
|
connection.onmessage = onMessageWs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCloseWs = () => {
|
||||||
|
setTimeout(connectWs, 10000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMessageWs = (event: MessageEvent) => {
|
||||||
|
const message = parseMessage(event.data);
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
handleMessage(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseMessage = (data: string): SocketMessage | null => {
|
||||||
|
const request = JSON.parse(data) as SocketMessage;
|
||||||
|
|
||||||
|
if (!request.type) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMessage = (message: SocketMessage) => {
|
||||||
|
switch (message.type) {
|
||||||
|
case SocketMessageTypes.NEW_MESSAGE: {
|
||||||
|
dispatch({
|
||||||
|
type: ChannelActionTypes.SET_CHANNEL_MESSAGE,
|
||||||
|
payload: {
|
||||||
|
id: message.payload.channelId,
|
||||||
|
message: message.payload.message,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
case SocketMessageTypes.NEW_CHANNEL: {
|
||||||
|
}
|
||||||
|
case SocketMessageTypes.NEW_ANNOUNCEMENT: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { connection, connectWs };
|
||||||
|
|
@ -6,6 +6,7 @@ import { ChannelActionTypes, ChannelAction } from "./channel";
|
||||||
import { RoleActionTypes, RoleAction } from "./role";
|
import { RoleActionTypes, RoleAction } from "./role";
|
||||||
import { SessionActionTypes, SessionAction } from "./session";
|
import { SessionActionTypes, SessionAction } from "./session";
|
||||||
import { InviteActionTypes, InviteAction } from "./invite";
|
import { InviteActionTypes, InviteAction } from "./invite";
|
||||||
|
import { MessageActionTypes, MessageAction } from "./message";
|
||||||
|
|
||||||
type ActionTypes =
|
type ActionTypes =
|
||||||
| AppActionTypes
|
| AppActionTypes
|
||||||
|
|
@ -15,7 +16,8 @@ type ActionTypes =
|
||||||
| ChannelActionTypes
|
| ChannelActionTypes
|
||||||
| RoleActionTypes
|
| RoleActionTypes
|
||||||
| SessionActionTypes
|
| SessionActionTypes
|
||||||
| InviteActionTypes;
|
| InviteActionTypes
|
||||||
|
| MessageActionTypes;
|
||||||
|
|
||||||
type Action =
|
type Action =
|
||||||
| AppAction
|
| AppAction
|
||||||
|
|
@ -25,6 +27,7 @@ type Action =
|
||||||
| ChannelAction
|
| ChannelAction
|
||||||
| RoleAction
|
| RoleAction
|
||||||
| SessionAction
|
| SessionAction
|
||||||
| InviteAction;
|
| InviteAction
|
||||||
|
| MessageAction;
|
||||||
|
|
||||||
export { type Action, type ActionTypes };
|
export { type Action, type ActionTypes };
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,16 @@ enum AppActionTypes {
|
||||||
SET_HOME_OPEN = "SET_HOME_OPEN",
|
SET_HOME_OPEN = "SET_HOME_OPEN",
|
||||||
SET_SETTINGS_OPEN = "SET_SETTINGS_OPEN",
|
SET_SETTINGS_OPEN = "SET_SETTINGS_OPEN",
|
||||||
SET_ADD_COMMUNITY_OPEN = "SET_ADD_COMMUNITY_OPEN",
|
SET_ADD_COMMUNITY_OPEN = "SET_ADD_COMMUNITY_OPEN",
|
||||||
|
SET_ADD_COMMUNITY_SETTINGS_OPEN = "SET_ADD_COMMUNITY_SETTINGS_OPEN",
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppAction =
|
type AppAction =
|
||||||
| { type: AppActionTypes.SET_HOME_OPEN; payload: boolean }
|
| { type: AppActionTypes.SET_HOME_OPEN; payload: boolean }
|
||||||
| { type: AppActionTypes.SET_SETTINGS_OPEN; payload: boolean }
|
| { type: AppActionTypes.SET_SETTINGS_OPEN; payload: boolean }
|
||||||
| { type: AppActionTypes.SET_ADD_COMMUNITY_OPEN; payload: boolean };
|
| { type: AppActionTypes.SET_ADD_COMMUNITY_OPEN; payload: boolean }
|
||||||
|
| {
|
||||||
|
type: AppActionTypes.SET_ADD_COMMUNITY_SETTINGS_OPEN;
|
||||||
|
payload: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export { AppActionTypes, type AppAction };
|
export { AppActionTypes, type AppAction };
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,15 @@ function appReducer(_state: IAppState, action: AppAction) {
|
||||||
setState("app", "dialogsOpen", "addCommunityOpen", false);
|
setState("app", "dialogsOpen", "addCommunityOpen", false);
|
||||||
setState("app", "dialogsOpen", "addCommunityOpen", action.payload);
|
setState("app", "dialogsOpen", "addCommunityOpen", action.payload);
|
||||||
break;
|
break;
|
||||||
|
case AppActionTypes.SET_ADD_COMMUNITY_SETTINGS_OPEN:
|
||||||
|
setState("app", "dialogsOpen", "communitySettingsOpen", false);
|
||||||
|
setState(
|
||||||
|
"app",
|
||||||
|
"dialogsOpen",
|
||||||
|
"communitySettingsOpen",
|
||||||
|
action.payload,
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ interface IAppState {
|
||||||
interface IDialogsOpen {
|
interface IDialogsOpen {
|
||||||
settingsOpen: boolean;
|
settingsOpen: boolean;
|
||||||
addCommunityOpen: boolean;
|
addCommunityOpen: boolean;
|
||||||
|
communitySettingsOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { type IAppState, type IDialogsOpen };
|
export { type IAppState, type IDialogsOpen };
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import {
|
import {
|
||||||
IFetchLoginRequest,
|
IFetchLoginRequest,
|
||||||
IFetchLoginResponse,
|
IFetchLoginResponse,
|
||||||
IFetchRefreshResponse,
|
IFetchRefreshResponseError,
|
||||||
|
IFetchRefreshResponseSuccess,
|
||||||
} from "../../api/auth";
|
} from "../../api/auth";
|
||||||
|
|
||||||
enum AuthActionTypes {
|
enum AuthActionTypes {
|
||||||
|
SET_LOGGED_IN = "SET_LOGGED_IN",
|
||||||
FETCH_LOGIN_START = "FETCH_LOGIN_START",
|
FETCH_LOGIN_START = "FETCH_LOGIN_START",
|
||||||
FETCH_LOGIN_FINISH = "FETCH_LOGIN_FINISH",
|
FETCH_LOGIN_FINISH = "FETCH_LOGIN_FINISH",
|
||||||
FETCH_REFRESH_START = "FETCH_REFRESH_START",
|
FETCH_REFRESH_START = "FETCH_REFRESH_START",
|
||||||
|
|
@ -12,6 +14,7 @@ enum AuthActionTypes {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthAction =
|
type AuthAction =
|
||||||
|
| { type: AuthActionTypes.SET_LOGGED_IN; payload: boolean }
|
||||||
| { type: AuthActionTypes.FETCH_LOGIN_START; payload: IFetchLoginRequest }
|
| { type: AuthActionTypes.FETCH_LOGIN_START; payload: IFetchLoginRequest }
|
||||||
| {
|
| {
|
||||||
type: AuthActionTypes.FETCH_LOGIN_FINISH;
|
type: AuthActionTypes.FETCH_LOGIN_FINISH;
|
||||||
|
|
@ -20,7 +23,7 @@ type AuthAction =
|
||||||
| { type: AuthActionTypes.FETCH_REFRESH_START }
|
| { type: AuthActionTypes.FETCH_REFRESH_START }
|
||||||
| {
|
| {
|
||||||
type: AuthActionTypes.FETCH_REFRESH_FINISH;
|
type: AuthActionTypes.FETCH_REFRESH_FINISH;
|
||||||
payload: IFetchRefreshResponse;
|
payload: IFetchRefreshResponseError | IFetchRefreshResponseSuccess;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { AuthActionTypes, type AuthAction };
|
export { AuthActionTypes, type AuthAction };
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ import { IAuthState } from "./types";
|
||||||
|
|
||||||
function authReducer(_state: IAuthState, action: AuthAction) {
|
function authReducer(_state: IAuthState, action: AuthAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case AuthActionTypes.SET_LOGGED_IN:
|
||||||
|
setState("auth", "loggedIn", action.payload);
|
||||||
|
break;
|
||||||
case AuthActionTypes.FETCH_LOGIN_START:
|
case AuthActionTypes.FETCH_LOGIN_START:
|
||||||
fetchLogin(action.payload.username, action.payload.password);
|
fetchLogin(action.payload.username, action.payload.password);
|
||||||
break;
|
break;
|
||||||
|
|
@ -15,7 +18,9 @@ function authReducer(_state: IAuthState, action: AuthAction) {
|
||||||
fetchRefresh();
|
fetchRefresh();
|
||||||
break;
|
break;
|
||||||
case AuthActionTypes.FETCH_REFRESH_FINISH:
|
case AuthActionTypes.FETCH_REFRESH_FINISH:
|
||||||
|
if ("id" in action.payload) {
|
||||||
setState("auth", "session", action.payload);
|
setState("auth", "session", action.payload);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
interface IAuthState {
|
interface IAuthState {
|
||||||
|
loggedIn?: boolean;
|
||||||
session?: ISession;
|
session?: ISession;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,16 @@ import {
|
||||||
ICreateChannelResponse,
|
ICreateChannelResponse,
|
||||||
IUpdateChannelResponse,
|
IUpdateChannelResponse,
|
||||||
IRemoveChannelResponse,
|
IRemoveChannelResponse,
|
||||||
|
IFetchChannelMessagesResponse,
|
||||||
|
IFetchChannelMessage,
|
||||||
} from "../../api/channel";
|
} from "../../api/channel";
|
||||||
import { IFetchCommunityChannel } from "../../api/community";
|
import { IFetchCommunityChannel } from "../../api/community";
|
||||||
|
|
||||||
enum ChannelActionTypes {
|
enum ChannelActionTypes {
|
||||||
SET_CHANNEL = "SET_CHANNEL",
|
SET_CHANNEL = "SET_CHANNEL",
|
||||||
SET_ACTIVE_CHANNEL = "SET_ACTIVE_CHANNEL",
|
SET_ACTIVE_CHANNEL = "SET_ACTIVE_CHANNEL",
|
||||||
|
SET_TEXT = "SET_TEXT",
|
||||||
|
SET_CHANNEL_MESSAGE = "SET_CHANNEL_MESSAGE",
|
||||||
FETCH_CHANNEL_START = "FETCH_CHANNEL_START",
|
FETCH_CHANNEL_START = "FETCH_CHANNEL_START",
|
||||||
FETCH_CHANNEL_FINISH = "FETCH_CHANNEL_FINISH",
|
FETCH_CHANNEL_FINISH = "FETCH_CHANNEL_FINISH",
|
||||||
CREATE_CHANNEL_START = "CREATE_CHANNEL_START",
|
CREATE_CHANNEL_START = "CREATE_CHANNEL_START",
|
||||||
|
|
@ -19,6 +23,8 @@ enum ChannelActionTypes {
|
||||||
UPDATE_CHANNEL_FINISH = "UPDATE_CHANNEL_FINISH",
|
UPDATE_CHANNEL_FINISH = "UPDATE_CHANNEL_FINISH",
|
||||||
REMOVE_CHANNEL_START = "REMOVE_CHANNEL_START",
|
REMOVE_CHANNEL_START = "REMOVE_CHANNEL_START",
|
||||||
REMOVE_CHANNEL_FINISH = "REMOVE_CHANNEL_FINISH",
|
REMOVE_CHANNEL_FINISH = "REMOVE_CHANNEL_FINISH",
|
||||||
|
FETCH_CHANNEL_MESSAGES_START = "FETCH_CHANNEL_MESSAGES_START",
|
||||||
|
FETCH_CHANNEL_MESSAGES_FINISH = "FETCH_CHANNEL_MESSAGES_FINISH",
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelAction =
|
type ChannelAction =
|
||||||
|
|
@ -30,6 +36,14 @@ type ChannelAction =
|
||||||
type: ChannelActionTypes.SET_ACTIVE_CHANNEL;
|
type: ChannelActionTypes.SET_ACTIVE_CHANNEL;
|
||||||
payload: string | undefined;
|
payload: string | undefined;
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: ChannelActionTypes.SET_TEXT;
|
||||||
|
payload: { id: string; text: string };
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ChannelActionTypes.SET_CHANNEL_MESSAGE;
|
||||||
|
payload: { id: string; message: IFetchChannelMessage };
|
||||||
|
}
|
||||||
| { type: ChannelActionTypes.FETCH_CHANNEL_START; payload: string }
|
| { type: ChannelActionTypes.FETCH_CHANNEL_START; payload: string }
|
||||||
| {
|
| {
|
||||||
type: ChannelActionTypes.FETCH_CHANNEL_FINISH;
|
type: ChannelActionTypes.FETCH_CHANNEL_FINISH;
|
||||||
|
|
@ -55,6 +69,11 @@ type ChannelAction =
|
||||||
| {
|
| {
|
||||||
type: ChannelActionTypes.REMOVE_CHANNEL_FINISH;
|
type: ChannelActionTypes.REMOVE_CHANNEL_FINISH;
|
||||||
payload: IRemoveChannelResponse;
|
payload: IRemoveChannelResponse;
|
||||||
|
}
|
||||||
|
| { type: ChannelActionTypes.FETCH_CHANNEL_MESSAGES_START; payload: string }
|
||||||
|
| {
|
||||||
|
type: ChannelActionTypes.FETCH_CHANNEL_MESSAGES_FINISH;
|
||||||
|
payload: IFetchChannelMessagesResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { ChannelActionTypes, type ChannelAction };
|
export { ChannelActionTypes, type ChannelAction };
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,13 @@ import {
|
||||||
createChannel,
|
createChannel,
|
||||||
updateChannel,
|
updateChannel,
|
||||||
removeChannel,
|
removeChannel,
|
||||||
|
fetchChannelMessages,
|
||||||
} from "../../services/channel";
|
} from "../../services/channel";
|
||||||
import { setState } from "../state";
|
import { setState } from "../state";
|
||||||
import { ChannelActionTypes, ChannelAction } from "./actions";
|
import { ChannelActionTypes, ChannelAction } from "./actions";
|
||||||
import { IChannelState } from "./types";
|
import { IChannelState } from "./types";
|
||||||
|
|
||||||
function channelReducer(state: IChannelState, action: ChannelAction) {
|
function channelReducer(_state: IChannelState, action: ChannelAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ChannelActionTypes.SET_CHANNEL:
|
case ChannelActionTypes.SET_CHANNEL:
|
||||||
setState("channel", "channels", action.payload.id, action.payload);
|
setState("channel", "channels", action.payload.id, action.payload);
|
||||||
|
|
@ -16,6 +17,25 @@ function channelReducer(state: IChannelState, action: ChannelAction) {
|
||||||
case ChannelActionTypes.SET_ACTIVE_CHANNEL:
|
case ChannelActionTypes.SET_ACTIVE_CHANNEL:
|
||||||
setState("channel", "active", action.payload);
|
setState("channel", "active", action.payload);
|
||||||
break;
|
break;
|
||||||
|
case ChannelActionTypes.SET_TEXT:
|
||||||
|
setState(
|
||||||
|
"channel",
|
||||||
|
"channels",
|
||||||
|
action.payload.id,
|
||||||
|
"text",
|
||||||
|
action.payload.text,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case ChannelActionTypes.SET_CHANNEL_MESSAGE:
|
||||||
|
setState(
|
||||||
|
"channel",
|
||||||
|
"channels",
|
||||||
|
action.payload.id,
|
||||||
|
"messages",
|
||||||
|
action.payload.message.id,
|
||||||
|
action.payload.message,
|
||||||
|
);
|
||||||
|
break;
|
||||||
case ChannelActionTypes.FETCH_CHANNEL_START:
|
case ChannelActionTypes.FETCH_CHANNEL_START:
|
||||||
fetchChannel(action.payload);
|
fetchChannel(action.payload);
|
||||||
break;
|
break;
|
||||||
|
|
@ -48,6 +68,25 @@ function channelReducer(state: IChannelState, action: ChannelAction) {
|
||||||
return copy;
|
return copy;
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case ChannelActionTypes.FETCH_CHANNEL_MESSAGES_START:
|
||||||
|
fetchChannelMessages(action.payload);
|
||||||
|
break;
|
||||||
|
case ChannelActionTypes.FETCH_CHANNEL_MESSAGES_FINISH:
|
||||||
|
setState(
|
||||||
|
"channel",
|
||||||
|
"channels",
|
||||||
|
action.payload.id,
|
||||||
|
"messages",
|
||||||
|
(messages) => {
|
||||||
|
const newMessages = Object.fromEntries(
|
||||||
|
action.payload.messages.map((item) => [item.id, item]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const copy = { ...messages, ...newMessages };
|
||||||
|
return copy;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { IMessage } from "../message";
|
||||||
|
|
||||||
interface IChannelState {
|
interface IChannelState {
|
||||||
active?: string;
|
active?: string;
|
||||||
channels: Record<string, IChannel>;
|
channels: Record<string, IChannel>;
|
||||||
|
|
@ -9,6 +11,8 @@ interface IChannel {
|
||||||
description?: string;
|
description?: string;
|
||||||
communityId?: string;
|
communityId?: string;
|
||||||
creationDate?: number;
|
creationDate?: number;
|
||||||
|
text?: string;
|
||||||
|
messages?: Record<string, IMessage>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { type IChannelState, type IChannel };
|
export { type IChannelState, type IChannel };
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import { setState } from "../state";
|
||||||
import { CommunityActionTypes, CommunityAction } from "./actions";
|
import { CommunityActionTypes, CommunityAction } from "./actions";
|
||||||
import { ICommunityState } from "./types";
|
import { ICommunityState } from "./types";
|
||||||
|
|
||||||
function communityReducer(state: ICommunityState, action: CommunityAction) {
|
function communityReducer(_state: ICommunityState, action: CommunityAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case CommunityActionTypes.SET_COMMUNITY:
|
case CommunityActionTypes.SET_COMMUNITY:
|
||||||
setState(
|
setState(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { setState } from "../state";
|
||||||
import { InviteActionTypes, InviteAction } from "./actions";
|
import { InviteActionTypes, InviteAction } from "./actions";
|
||||||
import { IInviteState } from "./types";
|
import { IInviteState } from "./types";
|
||||||
|
|
||||||
function inviteReducer(state: IInviteState, action: InviteAction) {
|
function inviteReducer(_state: IInviteState, action: InviteAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case InviteActionTypes.SET_INVITE:
|
case InviteActionTypes.SET_INVITE:
|
||||||
setState("invite", "invites", action.payload.id, action.payload);
|
setState("invite", "invites", action.payload.id, action.payload);
|
||||||
|
|
|
||||||
49
src/store/message/actions.ts
Normal file
49
src/store/message/actions.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import {
|
||||||
|
ICreateMessageRequest,
|
||||||
|
IUpdateMessageRequest,
|
||||||
|
IFetchMessageResponse,
|
||||||
|
ICreateMessageResponse,
|
||||||
|
IUpdateMessageResponse,
|
||||||
|
IRemoveMessageResponse,
|
||||||
|
} from "../../api/message";
|
||||||
|
|
||||||
|
enum MessageActionTypes {
|
||||||
|
FETCH_MESSAGE_START = "FETCH_MESSAGE_START",
|
||||||
|
FETCH_MESSAGE_FINISH = "FETCH_MESSAGE_FINISH",
|
||||||
|
CREATE_MESSAGE_START = "CREATE_MESSAGE_START",
|
||||||
|
CREATE_MESSAGE_FINISH = "CREATE_MESSAGE_FINISH",
|
||||||
|
UPDATE_MESSAGE_START = "UPDATE_MESSAGE_START",
|
||||||
|
UPDATE_MESSAGE_FINISH = "UPDATE_MESSAGE_FINISH",
|
||||||
|
REMOVE_MESSAGE_START = "REMOVE_MESSAGE_START",
|
||||||
|
REMOVE_MESSAGE_FINISH = "REMOVE_MESSAGE_FINISH",
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageAction =
|
||||||
|
| { type: MessageActionTypes.FETCH_MESSAGE_START; payload: string }
|
||||||
|
| {
|
||||||
|
type: MessageActionTypes.FETCH_MESSAGE_FINISH;
|
||||||
|
payload: IFetchMessageResponse;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: MessageActionTypes.CREATE_MESSAGE_START;
|
||||||
|
payload: ICreateMessageRequest;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: MessageActionTypes.CREATE_MESSAGE_FINISH;
|
||||||
|
payload: ICreateMessageResponse;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: MessageActionTypes.UPDATE_MESSAGE_START;
|
||||||
|
payload: IUpdateMessageRequest;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: MessageActionTypes.UPDATE_MESSAGE_FINISH;
|
||||||
|
payload: IUpdateMessageResponse;
|
||||||
|
}
|
||||||
|
| { type: MessageActionTypes.REMOVE_MESSAGE_START; payload: string }
|
||||||
|
| {
|
||||||
|
type: MessageActionTypes.REMOVE_MESSAGE_FINISH;
|
||||||
|
payload: IRemoveMessageResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { MessageActionTypes, type MessageAction };
|
||||||
3
src/store/message/index.ts
Normal file
3
src/store/message/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from "./message";
|
||||||
|
export * from "./actions";
|
||||||
|
export * from "./types";
|
||||||
59
src/store/message/message.ts
Normal file
59
src/store/message/message.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import {
|
||||||
|
fetchMessage,
|
||||||
|
createMessage,
|
||||||
|
updateMessage,
|
||||||
|
removeMessage,
|
||||||
|
} from "../../services/message";
|
||||||
|
import { setState } from "../state";
|
||||||
|
import { MessageActionTypes, MessageAction } from "./actions";
|
||||||
|
import { IMessageState } from "./types";
|
||||||
|
|
||||||
|
function messageReducer(_state: IMessageState, action: MessageAction) {
|
||||||
|
switch (action.type) {
|
||||||
|
case MessageActionTypes.FETCH_MESSAGE_START:
|
||||||
|
fetchMessage(action.payload);
|
||||||
|
break;
|
||||||
|
case MessageActionTypes.FETCH_MESSAGE_FINISH:
|
||||||
|
setState("message", "message", action.payload);
|
||||||
|
setState(
|
||||||
|
"channel",
|
||||||
|
"channels",
|
||||||
|
action.payload.channelId,
|
||||||
|
action.payload,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MessageActionTypes.CREATE_MESSAGE_START:
|
||||||
|
createMessage(action.payload.text, action.payload.channelId);
|
||||||
|
break;
|
||||||
|
case MessageActionTypes.CREATE_MESSAGE_FINISH:
|
||||||
|
setState("message", "message", action.payload);
|
||||||
|
setState(
|
||||||
|
"channel",
|
||||||
|
"channels",
|
||||||
|
action.payload.channelId,
|
||||||
|
"messages",
|
||||||
|
action.payload.id,
|
||||||
|
action.payload,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MessageActionTypes.UPDATE_MESSAGE_START:
|
||||||
|
updateMessage(action.payload.id, action.payload.text);
|
||||||
|
break;
|
||||||
|
case MessageActionTypes.UPDATE_MESSAGE_FINISH:
|
||||||
|
setState("message", "message", action.payload);
|
||||||
|
setState(
|
||||||
|
"channel",
|
||||||
|
"channels",
|
||||||
|
action.payload.channelId,
|
||||||
|
action.payload,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MessageActionTypes.REMOVE_MESSAGE_START:
|
||||||
|
removeMessage(action.payload);
|
||||||
|
break;
|
||||||
|
case MessageActionTypes.REMOVE_MESSAGE_FINISH:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { messageReducer };
|
||||||
15
src/store/message/types.ts
Normal file
15
src/store/message/types.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
interface IMessageState {
|
||||||
|
message?: IMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IMessage {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
editHistory?: string[];
|
||||||
|
edited: boolean;
|
||||||
|
ownerId: string;
|
||||||
|
channelId?: string;
|
||||||
|
creationDate: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { type IMessageState, type IMessage };
|
||||||
|
|
@ -8,6 +8,7 @@ import { ChannelAction, channelReducer } from "./channel";
|
||||||
import { RoleAction, roleReducer } from "./role";
|
import { RoleAction, roleReducer } from "./role";
|
||||||
import { SessionAction, sessionReducer } from "./session";
|
import { SessionAction, sessionReducer } from "./session";
|
||||||
import { InviteAction, inviteReducer } from "./invite";
|
import { InviteAction, inviteReducer } from "./invite";
|
||||||
|
import { MessageAction, messageReducer } from "./message";
|
||||||
|
|
||||||
function reducer(state: IState, action: Action) {
|
function reducer(state: IState, action: Action) {
|
||||||
appReducer(state.app, action as AppAction);
|
appReducer(state.app, action as AppAction);
|
||||||
|
|
@ -18,6 +19,7 @@ function reducer(state: IState, action: Action) {
|
||||||
roleReducer(state.role, action as RoleAction);
|
roleReducer(state.role, action as RoleAction);
|
||||||
sessionReducer(state.session, action as SessionAction);
|
sessionReducer(state.session, action as SessionAction);
|
||||||
inviteReducer(state.invite, action as InviteAction);
|
inviteReducer(state.invite, action as InviteAction);
|
||||||
|
messageReducer(state.message, action as MessageAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { reducer };
|
export { reducer };
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { IFetchCommunityRole } from "../../api/community";
|
import { IFetchCommunityRole } from "../../api/community";
|
||||||
import {
|
import {
|
||||||
ICreateRoleRequest,
|
ICreateRoleRequest,
|
||||||
|
IUpdateRoleRequest,
|
||||||
IFetchRoleResponse,
|
IFetchRoleResponse,
|
||||||
ICreateRoleResponse,
|
ICreateRoleResponse,
|
||||||
IUpdateRoleResponse,
|
IUpdateRoleResponse,
|
||||||
|
|
@ -34,7 +35,7 @@ type RoleAction =
|
||||||
type: RoleActionTypes.CREATE_ROLE_FINISH;
|
type: RoleActionTypes.CREATE_ROLE_FINISH;
|
||||||
payload: ICreateRoleResponse;
|
payload: ICreateRoleResponse;
|
||||||
}
|
}
|
||||||
| { type: RoleActionTypes.UPDATE_ROLE_START; payload: string }
|
| { type: RoleActionTypes.UPDATE_ROLE_START; payload: IUpdateRoleRequest }
|
||||||
| {
|
| {
|
||||||
type: RoleActionTypes.UPDATE_ROLE_FINISH;
|
type: RoleActionTypes.UPDATE_ROLE_FINISH;
|
||||||
payload: IUpdateRoleResponse;
|
payload: IUpdateRoleResponse;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { setState } from "../state";
|
||||||
import { RoleActionTypes, RoleAction } from "./actions";
|
import { RoleActionTypes, RoleAction } from "./actions";
|
||||||
import { IRoleState } from "./types";
|
import { IRoleState } from "./types";
|
||||||
|
|
||||||
function roleReducer(state: IRoleState, action: RoleAction) {
|
function roleReducer(_state: IRoleState, action: RoleAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case RoleActionTypes.SET_ROLE:
|
case RoleActionTypes.SET_ROLE:
|
||||||
setState("role", "roles", action.payload.id, action.payload);
|
setState("role", "roles", action.payload.id, action.payload);
|
||||||
|
|
@ -26,7 +26,7 @@ function roleReducer(state: IRoleState, action: RoleAction) {
|
||||||
setState("role", "roles", action.payload.id, action.payload);
|
setState("role", "roles", action.payload.id, action.payload);
|
||||||
break;
|
break;
|
||||||
case RoleActionTypes.UPDATE_ROLE_START:
|
case RoleActionTypes.UPDATE_ROLE_START:
|
||||||
updateRole(action.payload);
|
updateRole(action.payload.id, action.payload.name);
|
||||||
break;
|
break;
|
||||||
case RoleActionTypes.UPDATE_ROLE_FINISH:
|
case RoleActionTypes.UPDATE_ROLE_FINISH:
|
||||||
setState("role", "roles", action.payload.id, action.payload);
|
setState("role", "roles", action.payload.id, action.payload);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { setState } from "../state";
|
||||||
import { SessionActionTypes, SessionAction } from "./actions";
|
import { SessionActionTypes, SessionAction } from "./actions";
|
||||||
import { ISessionState } from "./types";
|
import { ISessionState } from "./types";
|
||||||
|
|
||||||
function sessionReducer(state: ISessionState, action: SessionAction) {
|
function sessionReducer(_state: ISessionState, action: SessionAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case SessionActionTypes.SET_SESSION:
|
case SessionActionTypes.SET_SESSION:
|
||||||
setState("session", "sessions", action.payload.id, action.payload);
|
setState("session", "sessions", action.payload.id, action.payload);
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@ const [state, setState] = createStore<IState>({
|
||||||
dialogsOpen: {
|
dialogsOpen: {
|
||||||
settingsOpen: false,
|
settingsOpen: false,
|
||||||
addCommunityOpen: false,
|
addCommunityOpen: false,
|
||||||
|
communitySettingsOpen: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
auth: {
|
auth: {
|
||||||
|
loggedIn: undefined,
|
||||||
session: undefined,
|
session: undefined,
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
|
|
@ -33,6 +35,9 @@ const [state, setState] = createStore<IState>({
|
||||||
invite: {
|
invite: {
|
||||||
invites: {},
|
invites: {},
|
||||||
},
|
},
|
||||||
|
message: {
|
||||||
|
message: undefined,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function dispatch(action: Action) {
|
function dispatch(action: Action) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { IChannelState } from "./channel";
|
||||||
import { IRoleState } from "./role";
|
import { IRoleState } from "./role";
|
||||||
import { ISessionState } from "./session";
|
import { ISessionState } from "./session";
|
||||||
import { IInviteState } from "./invite";
|
import { IInviteState } from "./invite";
|
||||||
|
import { IMessageState } from "./message";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
app: IAppState;
|
app: IAppState;
|
||||||
|
|
@ -16,6 +17,7 @@ interface IState {
|
||||||
role: IRoleState;
|
role: IRoleState;
|
||||||
session: ISessionState;
|
session: ISessionState;
|
||||||
invite: IInviteState;
|
invite: IInviteState;
|
||||||
|
message: IMessageState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { type IState };
|
export { type IState };
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { setState } from "../state";
|
||||||
import { UserActionTypes, UserAction } from "./actions";
|
import { UserActionTypes, UserAction } from "./actions";
|
||||||
import { IUserState } from "./types";
|
import { IUserState } from "./types";
|
||||||
|
|
||||||
function userReducer(state: IUserState, action: UserAction) {
|
function userReducer(_state: IUserState, action: UserAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case UserActionTypes.SET_USER:
|
case UserActionTypes.SET_USER:
|
||||||
setState("user", "users", action.payload.id, action.payload);
|
setState("user", "users", action.payload.id, action.payload);
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,12 @@ import { AuthActionTypes } from "../../store/auth";
|
||||||
import { HomeView } from "../HomeView";
|
import { HomeView } from "../HomeView";
|
||||||
import { CommunityView } from "../CommunityView";
|
import { CommunityView } from "../CommunityView";
|
||||||
import { ModalView } from "../ModalView";
|
import { ModalView } from "../ModalView";
|
||||||
|
import { useNavigate } from "@solidjs/router";
|
||||||
|
import { connectWs } from "../../services/websocket";
|
||||||
|
|
||||||
|
const AppView: Component = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const MainView: Component = () => {
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: AuthActionTypes.FETCH_REFRESH_START,
|
type: AuthActionTypes.FETCH_REFRESH_START,
|
||||||
|
|
@ -31,6 +35,14 @@ const MainView: Component = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (state.auth.loggedIn === false) {
|
||||||
|
navigate("/login");
|
||||||
|
} else {
|
||||||
|
connectWs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-row h-screen">
|
<div class="flex flex-row h-screen">
|
||||||
<ModalView />
|
<ModalView />
|
||||||
|
|
@ -50,4 +62,4 @@ const MainView: Component = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { MainView };
|
export { AppView };
|
||||||
1
src/views/AppView/index.ts
Normal file
1
src/views/AppView/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./AppView";
|
||||||
|
|
@ -4,6 +4,7 @@ import { CommunityActionTypes, ICommunity } from "../../store/community";
|
||||||
import { ChannelActionTypes } from "../../store/channel";
|
import { ChannelActionTypes } from "../../store/channel";
|
||||||
import { Channel } from "../../components/Channel";
|
import { Channel } from "../../components/Channel";
|
||||||
import { CommunityBar } from "../../components/CommunityBar";
|
import { CommunityBar } from "../../components/CommunityBar";
|
||||||
|
import { AppActionTypes } from "../../store/app";
|
||||||
|
|
||||||
const ChannelView: Component = () => {
|
const ChannelView: Component = () => {
|
||||||
const channelIds = createMemo(() => {
|
const channelIds = createMemo(() => {
|
||||||
|
|
@ -34,6 +35,13 @@ const ChannelView: Component = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onCommunitySettingsClick = () => {
|
||||||
|
dispatch({
|
||||||
|
type: AppActionTypes.SET_ADD_COMMUNITY_SETTINGS_OPEN,
|
||||||
|
payload: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const onChannelClick = (id: string) => {
|
const onChannelClick = (id: string) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ChannelActionTypes.FETCH_CHANNEL_START,
|
type: ChannelActionTypes.FETCH_CHANNEL_START,
|
||||||
|
|
@ -70,6 +78,7 @@ const ChannelView: Component = () => {
|
||||||
avatar={
|
avatar={
|
||||||
"https://img.daisyui.com/images/profile/demo/yellingcat@192.webp"
|
"https://img.daisyui.com/images/profile/demo/yellingcat@192.webp"
|
||||||
}
|
}
|
||||||
|
onSettingsClick={onCommunitySettingsClick}
|
||||||
/>
|
/>
|
||||||
<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 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)}
|
{channelIds().map(mapChannel)}
|
||||||
|
|
|
||||||
|
|
@ -1,189 +1,77 @@
|
||||||
import { createMemo, onMount, type Component } from "solid-js";
|
import { createEffect, createMemo, onMount, type Component } from "solid-js";
|
||||||
import { ChannelBar } from "../../components/ChannelBar";
|
import { ChannelBar } from "../../components/ChannelBar";
|
||||||
import { MessageBar } from "../../components/MessageBar";
|
import { MessageBar } from "../../components/MessageBar";
|
||||||
import { Message } from "../../components/Message";
|
import { Message } from "../../components/Message";
|
||||||
import { dispatch, state } from "../../store/state";
|
import { dispatch, state } from "../../store/state";
|
||||||
import { UserActionTypes } from "../../store/user";
|
import { UserActionTypes } from "../../store/user";
|
||||||
import { IChannel } from "../../store/channel";
|
import { ChannelActionTypes, IChannel } from "../../store/channel";
|
||||||
|
import { MessageActionTypes } from "../../store/message";
|
||||||
|
|
||||||
const ChatView: Component = () => {
|
const ChatView: Component = () => {
|
||||||
const testMessages = [
|
let scrollRef: HTMLUListElement | undefined;
|
||||||
{
|
let lastScrollTop = 0;
|
||||||
messageId: "432432",
|
let autoScroll = true;
|
||||||
message: "Hello this is a test message",
|
|
||||||
userId: "12121",
|
const scrollToBottom = () => {
|
||||||
username: "Aslan",
|
if (!scrollRef || !autoScroll) {
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
return;
|
||||||
},
|
}
|
||||||
{
|
|
||||||
messageId: "432433",
|
scrollRef.scrollTop = scrollRef.scrollHeight;
|
||||||
message: "Hi hi",
|
};
|
||||||
userId: "12122",
|
|
||||||
username: "TestUser",
|
const handleScroll = () => {
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
if (!scrollRef) {
|
||||||
},
|
return;
|
||||||
{
|
}
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
if (scrollRef.scrollTop < lastScrollTop) {
|
||||||
userId: "12121",
|
autoScroll = false;
|
||||||
username: "Aslan",
|
}
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
if (
|
||||||
},
|
scrollRef.scrollTop + scrollRef.clientHeight >=
|
||||||
{
|
scrollRef.scrollHeight
|
||||||
messageId: "432434",
|
) {
|
||||||
message: "Nooooo",
|
autoScroll = true;
|
||||||
userId: "12121",
|
}
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
lastScrollTop = scrollRef.scrollTop;
|
||||||
},
|
};
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
messageId: "432434",
|
|
||||||
message: "Nooooo",
|
|
||||||
userId: "12121",
|
|
||||||
username: "Aslan",
|
|
||||||
avatar: "https://img.daisyui.com/images/profile/demo/yellingcat@192.webp",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
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 ?? 0];
|
if (!activeChannelId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
autoScroll = true;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ChannelActionTypes.FETCH_CHANNEL_MESSAGES_START,
|
||||||
|
payload: activeChannelId,
|
||||||
|
});
|
||||||
|
return state.channel.channels[activeChannelId];
|
||||||
|
});
|
||||||
|
|
||||||
|
const messages = createMemo(() => {
|
||||||
|
const channelId = channelInfo()?.id;
|
||||||
|
if (!channelId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const channel = state.channel.channels[channelId];
|
||||||
|
if (!channel) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(channel.messages ?? []).length;
|
||||||
|
|
||||||
|
setTimeout(scrollToBottom, 20);
|
||||||
|
setTimeout(scrollToBottom, 100);
|
||||||
|
|
||||||
|
return channel.messages ?? [];
|
||||||
});
|
});
|
||||||
|
|
||||||
let scrollRef: HTMLUListElement | undefined;
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (scrollRef) {
|
if (scrollRef) {
|
||||||
scrollRef.scrollTop = scrollRef.scrollHeight;
|
scrollRef.scrollTop = scrollRef.scrollHeight;
|
||||||
|
|
@ -197,6 +85,50 @@ const ChatView: Component = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onChangeMessageText = (text: string) => {
|
||||||
|
const channel = channelInfo();
|
||||||
|
if (!channel?.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ChannelActionTypes.SET_TEXT,
|
||||||
|
payload: {
|
||||||
|
id: channel.id,
|
||||||
|
text: text,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMessageSend = () => {
|
||||||
|
autoScroll = true;
|
||||||
|
|
||||||
|
const channel = channelInfo();
|
||||||
|
if (!channel?.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = channel.text;
|
||||||
|
if (!text || text.trim().length < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ChannelActionTypes.SET_TEXT,
|
||||||
|
payload: {
|
||||||
|
id: channel.id,
|
||||||
|
text: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: MessageActionTypes.CREATE_MESSAGE_START,
|
||||||
|
payload: {
|
||||||
|
channelId: channel.id,
|
||||||
|
text: text,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="bg-stone-800 flex-1 z-0 relative">
|
<div class="bg-stone-800 flex-1 z-0 relative">
|
||||||
<div class="h-full">
|
<div class="h-full">
|
||||||
|
|
@ -207,20 +139,29 @@ const ChatView: Component = () => {
|
||||||
/>
|
/>
|
||||||
<ul
|
<ul
|
||||||
ref={scrollRef}
|
ref={scrollRef}
|
||||||
|
onScrollEnd={handleScroll}
|
||||||
class="h-full list flex flex-col p-2 pt-18 pb-24 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-500 scrollbar-track-gray-800"
|
class="h-full list flex flex-col p-2 pt-18 pb-24 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-500 scrollbar-track-gray-800"
|
||||||
>
|
>
|
||||||
{testMessages.map((msg) => (
|
{Object.entries(messages()).map(([_key, message]) => (
|
||||||
<Message
|
<Message
|
||||||
messageId={msg.messageId}
|
messageId={message.id}
|
||||||
message={msg.message}
|
message={message.text}
|
||||||
userId={msg.userId}
|
userId={message.ownerId}
|
||||||
username={msg.username}
|
username={
|
||||||
avatar={msg.avatar}
|
state.user.users[message.ownerId].username ?? ""
|
||||||
|
}
|
||||||
|
avatar={
|
||||||
|
"https://img.daisyui.com/images/profile/demo/yellingcat@192.webp"
|
||||||
|
}
|
||||||
onProfileClick={onProfileClick}
|
onProfileClick={onProfileClick}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<MessageBar />
|
<MessageBar
|
||||||
|
text={channelInfo()?.text ?? ""}
|
||||||
|
onChangeText={onChangeMessageText}
|
||||||
|
onSend={onMessageSend}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { createEffect, createSignal, type Component } from "solid-js";
|
import { createEffect, createSignal, onMount, type Component } from "solid-js";
|
||||||
import { dispatch, state } from "../../store/state";
|
import { dispatch, state } from "../../store/state";
|
||||||
import { AuthActionTypes } from "../../store/auth";
|
import { AuthActionTypes } from "../../store/auth";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
|
|
@ -9,6 +9,12 @@ const LoginView: Component = () => {
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
dispatch({
|
||||||
|
type: AuthActionTypes.FETCH_REFRESH_START,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const onLogin = () => {
|
const onLogin = () => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: AuthActionTypes.FETCH_LOGIN_START,
|
type: AuthActionTypes.FETCH_LOGIN_START,
|
||||||
|
|
@ -20,8 +26,8 @@ const LoginView: Component = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (state.auth.session?.id) {
|
if (state.auth.loggedIn) {
|
||||||
navigate("/");
|
navigate("/app");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export * from "./MainView";
|
|
||||||
|
|
@ -2,11 +2,13 @@ import { createEffect, type Component } from "solid-js";
|
||||||
import { dispatch, state } from "../../store/state";
|
import { dispatch, state } from "../../store/state";
|
||||||
import SettingsModal from "../../components/SettingsModal/SettingsModal";
|
import SettingsModal from "../../components/SettingsModal/SettingsModal";
|
||||||
import CommunityModal from "../../components/CommunityModal/CommunityModal";
|
import CommunityModal from "../../components/CommunityModal/CommunityModal";
|
||||||
|
import CommunitySettingsModal from "../../components/CommunitySettingsModal/CommunitySettingsModal";
|
||||||
import { AppActionTypes } from "../../store/app";
|
import { AppActionTypes } from "../../store/app";
|
||||||
|
|
||||||
const ModalView: Component = () => {
|
const ModalView: Component = () => {
|
||||||
let settingsModal: HTMLDialogElement;
|
let settingsModal: HTMLDialogElement;
|
||||||
let communityModal: HTMLDialogElement;
|
let communityModal: HTMLDialogElement;
|
||||||
|
let communitySettingsModal: HTMLDialogElement;
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (state.app.dialogsOpen.settingsOpen) {
|
if (state.app.dialogsOpen.settingsOpen) {
|
||||||
|
|
@ -24,6 +26,14 @@ const ModalView: Component = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (state.app.dialogsOpen.communitySettingsOpen) {
|
||||||
|
communitySettingsModal.showModal();
|
||||||
|
} else {
|
||||||
|
communitySettingsModal.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const onCloseSettings = () => {
|
const onCloseSettings = () => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: AppActionTypes.SET_SETTINGS_OPEN,
|
type: AppActionTypes.SET_SETTINGS_OPEN,
|
||||||
|
|
@ -38,6 +48,13 @@ const ModalView: Component = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onCloseCommunitySettings = () => {
|
||||||
|
dispatch({
|
||||||
|
type: AppActionTypes.SET_ADD_COMMUNITY_SETTINGS_OPEN,
|
||||||
|
payload: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SettingsModal
|
<SettingsModal
|
||||||
|
|
@ -48,6 +65,10 @@ const ModalView: Component = () => {
|
||||||
dialogRef={(element) => (communityModal = element)}
|
dialogRef={(element) => (communityModal = element)}
|
||||||
onClose={onCloseCommunity}
|
onClose={onCloseCommunity}
|
||||||
/>
|
/>
|
||||||
|
<CommunitySettingsModal
|
||||||
|
dialogRef={(element) => (communitySettingsModal = element)}
|
||||||
|
onClose={onCloseCommunitySettings}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,27 @@
|
||||||
import type { Component } from "solid-js";
|
import { useNavigate } from "@solidjs/router";
|
||||||
|
import { createSignal, createEffect, type Component, onMount } from "solid-js";
|
||||||
|
import { dispatch, state } from "../../store/state";
|
||||||
|
import { AuthActionTypes } from "../../store/auth";
|
||||||
|
|
||||||
const RegisterView: Component = () => {
|
const RegisterView: Component = () => {
|
||||||
|
const [getUsername, setUsername] = createSignal("");
|
||||||
|
const [getEmail, setEmail] = createSignal("");
|
||||||
|
const [getPassword, setPassword] = createSignal("");
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
dispatch({
|
||||||
|
type: AuthActionTypes.FETCH_REFRESH_START,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (state.auth.loggedIn) {
|
||||||
|
navigate("/app");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return <div></div>;
|
return <div></div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
import type { Component } from "solid-js";
|
|
||||||
|
|
||||||
const SettingsView: Component = () => {
|
|
||||||
return <div></div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { SettingsView };
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export * from "./SettingsView";
|
|
||||||
7
src/views/StartView/StartView.tsx
Normal file
7
src/views/StartView/StartView.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { type Component } from "solid-js";
|
||||||
|
|
||||||
|
const StartView: Component = () => {
|
||||||
|
return <div></div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { StartView };
|
||||||
1
src/views/StartView/index.ts
Normal file
1
src/views/StartView/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./StartView";
|
||||||
Loading…
Add table
Add a link
Reference in a new issue