Added end to end encryption
This commit is contained in:
parent
9153ba841d
commit
575e9e2010
131 changed files with 2289 additions and 1670 deletions
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "pulsar-web",
|
||||
"version": "0.3.0",
|
||||
"version": "0.5.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pulsar-web",
|
||||
"version": "0.3.0",
|
||||
"version": "0.5.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@solidjs/router": "^0.15.4",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "pulsar-web",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.2",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,21 +1,29 @@
|
|||
import { callApi, HTTP } from "../tools";
|
||||
import { IResponseError } from "../types";
|
||||
import {
|
||||
IFetchRegisterRequest,
|
||||
IFetchRegisterResponse,
|
||||
IFetchLoginRequest,
|
||||
IFetchLoginResponse,
|
||||
IFetchRefreshResponseError,
|
||||
IFetchRefreshResponseSuccess,
|
||||
IFetchRefreshResponse,
|
||||
} from "./types";
|
||||
|
||||
const fetchRegisterApi = async (
|
||||
request: IFetchRegisterRequest,
|
||||
): Promise<IFetchRegisterResponse | IResponseError> => {
|
||||
return await callApi(HTTP.POST, `auth/register`, request, true);
|
||||
};
|
||||
|
||||
const fetchLoginApi = async (
|
||||
request: IFetchLoginRequest,
|
||||
): Promise<IFetchLoginResponse> => {
|
||||
): Promise<IFetchLoginResponse | IResponseError> => {
|
||||
return await callApi(HTTP.POST, `auth/login`, request, true);
|
||||
};
|
||||
|
||||
const fetchRefreshApi = async (): Promise<
|
||||
IFetchRefreshResponseError | IFetchRefreshResponseSuccess
|
||||
IFetchRefreshResponse | IResponseError
|
||||
> => {
|
||||
return await callApi(HTTP.GET, `auth/refresh`, undefined, true);
|
||||
};
|
||||
|
||||
export { fetchLoginApi, fetchRefreshApi };
|
||||
export { fetchRegisterApi, fetchLoginApi, fetchRefreshApi };
|
||||
|
|
|
|||
|
|
@ -1,26 +1,37 @@
|
|||
import { IResponseSuccess } from "../types";
|
||||
|
||||
interface IFetchRegisterRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface IFetchRegisterResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
ownerId: string;
|
||||
}
|
||||
|
||||
interface IFetchLoginRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface IFetchLoginResponse {
|
||||
interface IFetchLoginResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
ownerId: string;
|
||||
}
|
||||
|
||||
interface IFetchRefreshResponseError {
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface IFetchRefreshResponseSuccess {
|
||||
interface IFetchRefreshResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
ownerId: string;
|
||||
token: string;
|
||||
storageSecret: string;
|
||||
}
|
||||
|
||||
export {
|
||||
type IFetchRegisterRequest,
|
||||
type IFetchRegisterResponse,
|
||||
type IFetchLoginRequest,
|
||||
type IFetchLoginResponse,
|
||||
type IFetchRefreshResponseError,
|
||||
type IFetchRefreshResponseSuccess,
|
||||
type IFetchRefreshResponse,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { callApi, HTTP } from "../tools";
|
||||
import { IResponseError } from "../types";
|
||||
import {
|
||||
IFetchChannelRequest,
|
||||
IFetchChannelResponse,
|
||||
|
|
@ -14,31 +15,31 @@ import {
|
|||
|
||||
const fetchChannelApi = async (
|
||||
request: IFetchChannelRequest,
|
||||
): Promise<IFetchChannelResponse> => {
|
||||
): Promise<IFetchChannelResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `channel/${request.id}`);
|
||||
};
|
||||
|
||||
const createChannelApi = async (
|
||||
request: ICreateChannelRequest,
|
||||
): Promise<ICreateChannelResponse> => {
|
||||
): Promise<ICreateChannelResponse | IResponseError> => {
|
||||
return await callApi(HTTP.POST, `channel`, request);
|
||||
};
|
||||
|
||||
const updateChannelApi = async (
|
||||
request: IUpdateChannelRequest,
|
||||
): Promise<IUpdateChannelResponse> => {
|
||||
): Promise<IUpdateChannelResponse | IResponseError> => {
|
||||
return await callApi(HTTP.PATCH, `channel/${request.id}`, request);
|
||||
};
|
||||
|
||||
const removeChannelApi = async (
|
||||
request: IRemoveChannelRequest,
|
||||
): Promise<IRemoveChannelResponse> => {
|
||||
): Promise<IRemoveChannelResponse | IResponseError> => {
|
||||
return await callApi(HTTP.DELETE, `channel/${request.id}`);
|
||||
};
|
||||
|
||||
const fetchChannelMessagesApi = async (
|
||||
request: IFetchChannelMessagesRequest,
|
||||
): Promise<IFetchChannelMessagesResponse> => {
|
||||
): Promise<IFetchChannelMessagesResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `channel/${request.id}/messages`);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { IResponseSuccess } from "../types";
|
||||
|
||||
interface IFetchChannel {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
@ -10,14 +12,14 @@ interface IFetchChannelRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchChannelResponse extends IFetchChannel {}
|
||||
interface IFetchChannelResponse extends IResponseSuccess, IFetchChannel {}
|
||||
|
||||
interface ICreateChannelRequest {
|
||||
name: string;
|
||||
communityId: string;
|
||||
}
|
||||
|
||||
interface ICreateChannelResponse extends IFetchChannel {}
|
||||
interface ICreateChannelResponse extends IResponseSuccess, IFetchChannel {}
|
||||
|
||||
interface IUpdateChannelRequest {
|
||||
id: string;
|
||||
|
|
@ -25,13 +27,13 @@ interface IUpdateChannelRequest {
|
|||
description?: string;
|
||||
}
|
||||
|
||||
interface IUpdateChannelResponse extends IFetchChannel {}
|
||||
interface IUpdateChannelResponse extends IResponseSuccess, IFetchChannel {}
|
||||
|
||||
interface IRemoveChannelRequest {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface IRemoveChannelResponse {
|
||||
interface IRemoveChannelResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
communityId: string;
|
||||
}
|
||||
|
|
@ -40,7 +42,7 @@ interface IFetchChannelMessagesRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchChannelMessagesResponse {
|
||||
interface IFetchChannelMessagesResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
messages: IFetchChannelMessage[];
|
||||
}
|
||||
|
|
@ -48,6 +50,7 @@ interface IFetchChannelMessagesResponse {
|
|||
interface IFetchChannelMessage {
|
||||
id: string;
|
||||
text: string;
|
||||
iv: string;
|
||||
edited: boolean;
|
||||
ownerId: string;
|
||||
creationDate: number;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { callApi, HTTP } from "../tools";
|
||||
import { IResponseError } from "../types";
|
||||
import {
|
||||
IFetchCommunityRequest,
|
||||
IFetchCommunityResponse,
|
||||
|
|
@ -20,49 +21,49 @@ import {
|
|||
|
||||
const fetchCommunityApi = async (
|
||||
request: IFetchCommunityRequest,
|
||||
): Promise<IFetchCommunityResponse> => {
|
||||
): Promise<IFetchCommunityResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `community/${request.id}`);
|
||||
};
|
||||
|
||||
const createCommunityApi = async (
|
||||
request: ICreateCommunityRequest,
|
||||
): Promise<ICreateCommunityResponse> => {
|
||||
): Promise<ICreateCommunityResponse | IResponseError> => {
|
||||
return await callApi(HTTP.POST, `community`, request);
|
||||
};
|
||||
|
||||
const updateCommunityApi = async (
|
||||
request: IUpdateCommunityRequest,
|
||||
): Promise<IUpdateCommunityResponse> => {
|
||||
): Promise<IUpdateCommunityResponse | IResponseError> => {
|
||||
return await callApi(HTTP.PATCH, `community/${request.id}`, request);
|
||||
};
|
||||
|
||||
const removeCommunityApi = async (
|
||||
request: IRemoveCommunityRequest,
|
||||
): Promise<IRemoveCommunityResponse> => {
|
||||
): Promise<IRemoveCommunityResponse | IResponseError> => {
|
||||
return await callApi(HTTP.DELETE, `community/${request.id}`);
|
||||
};
|
||||
|
||||
const fetchCommunityChannelsApi = async (
|
||||
request: IFetchCommunityChannelsRequest,
|
||||
): Promise<IFetchCommunityChannelsResponse> => {
|
||||
): Promise<IFetchCommunityChannelsResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `community/${request.id}/channels`);
|
||||
};
|
||||
|
||||
const fetchCommunityRolesApi = async (
|
||||
request: IFetchCommunityRolesRequest,
|
||||
): Promise<IFetchCommunityRolesResponse> => {
|
||||
): Promise<IFetchCommunityRolesResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `community/${request.id}/roles`);
|
||||
};
|
||||
|
||||
const fetchCommunityMembersApi = async (
|
||||
request: IFetchCommunityMembersRequest,
|
||||
): Promise<IFetchCommunityMembersResponse> => {
|
||||
): Promise<IFetchCommunityMembersResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `community/${request.id}/members`);
|
||||
};
|
||||
|
||||
const fetchCommunityInvitesApi = async (
|
||||
request: IFetchCommunityInvitesRequest,
|
||||
): Promise<IFetchCommunityInvitesResponse> => {
|
||||
): Promise<IFetchCommunityInvitesResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `community/${request.id}/invites`);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { IResponseSuccess } from "../types";
|
||||
|
||||
interface IFetchCommunity {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
@ -10,13 +12,13 @@ interface IFetchCommunityRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchCommunityResponse extends IFetchCommunity {}
|
||||
interface IFetchCommunityResponse extends IResponseSuccess, IFetchCommunity {}
|
||||
|
||||
interface ICreateCommunityRequest {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface ICreateCommunityResponse extends IFetchCommunity {}
|
||||
interface ICreateCommunityResponse extends IResponseSuccess, IFetchCommunity {}
|
||||
|
||||
interface IUpdateCommunityRequest {
|
||||
id: string;
|
||||
|
|
@ -24,13 +26,13 @@ interface IUpdateCommunityRequest {
|
|||
description?: string;
|
||||
}
|
||||
|
||||
interface IUpdateCommunityResponse extends IFetchCommunity {}
|
||||
interface IUpdateCommunityResponse extends IResponseSuccess, IFetchCommunity {}
|
||||
|
||||
interface IRemoveCommunityRequest {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface IRemoveCommunityResponse {
|
||||
interface IRemoveCommunityResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +40,7 @@ interface IFetchCommunityChannelsRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchCommunityChannelsResponse {
|
||||
interface IFetchCommunityChannelsResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
channels: IFetchCommunityChannel[];
|
||||
}
|
||||
|
|
@ -52,7 +54,7 @@ interface IFetchCommunityRolesRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchCommunityRolesResponse {
|
||||
interface IFetchCommunityRolesResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
roles: IFetchCommunityRole[];
|
||||
}
|
||||
|
|
@ -66,7 +68,7 @@ interface IFetchCommunityMembersRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchCommunityMembersResponse {
|
||||
interface IFetchCommunityMembersResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
members: IFetchCommunityMember[];
|
||||
}
|
||||
|
|
@ -80,7 +82,7 @@ interface IFetchCommunityInvitesRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchCommunityInvitesResponse {
|
||||
interface IFetchCommunityInvitesResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
invites: IFetchCommunityInvite[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { callApi, HTTP } from "../tools";
|
||||
import { IResponseError } from "../types";
|
||||
import {
|
||||
IFetchInviteRequest,
|
||||
IFetchInviteResponse,
|
||||
|
|
@ -10,19 +11,19 @@ import {
|
|||
|
||||
const fetchInviteApi = async (
|
||||
request: IFetchInviteRequest,
|
||||
): Promise<IFetchInviteResponse> => {
|
||||
): Promise<IFetchInviteResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `invite/${request.id}`);
|
||||
};
|
||||
|
||||
const removeInviteApi = async (
|
||||
request: IRemoveInviteRequest,
|
||||
): Promise<IRemoveInviteResponse> => {
|
||||
): Promise<IRemoveInviteResponse | IResponseError> => {
|
||||
return await callApi(HTTP.DELETE, `invite/${request.id}`);
|
||||
};
|
||||
|
||||
const acceptInviteApi = async (
|
||||
request: IAcceptInviteRequest,
|
||||
): Promise<IAcceptInviteResponse> => {
|
||||
): Promise<IAcceptInviteResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `invite/${request.id}/accept`);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { IResponseSuccess } from "../types";
|
||||
|
||||
interface IFetchInvite {
|
||||
id: string;
|
||||
communityId: string;
|
||||
|
|
@ -14,13 +16,13 @@ interface IFetchInviteRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchInviteResponse extends IFetchInvite {}
|
||||
interface IFetchInviteResponse extends IResponseSuccess, IFetchInvite {}
|
||||
|
||||
interface IRemoveInviteRequest {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface IRemoveInviteResponse {
|
||||
interface IRemoveInviteResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
userId: string;
|
||||
}
|
||||
|
|
@ -29,7 +31,7 @@ interface IAcceptInviteRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IAcceptInviteResponse {
|
||||
interface IAcceptInviteResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
userId: string;
|
||||
userName: string;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { callApi, HTTP } from "../tools";
|
||||
import { IResponseError } from "../types";
|
||||
import {
|
||||
IFetchMessageRequest,
|
||||
IFetchMessageResponse,
|
||||
|
|
@ -12,25 +13,25 @@ import {
|
|||
|
||||
const fetchMessageApi = async (
|
||||
request: IFetchMessageRequest,
|
||||
): Promise<IFetchMessageResponse> => {
|
||||
): Promise<IFetchMessageResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `message/${request.id}`);
|
||||
};
|
||||
|
||||
const createMessageApi = async (
|
||||
request: ICreateMessageRequest,
|
||||
): Promise<ICreateMessageResponse> => {
|
||||
): Promise<ICreateMessageResponse | IResponseError> => {
|
||||
return await callApi(HTTP.POST, `message`, request);
|
||||
};
|
||||
|
||||
const updateMessageApi = async (
|
||||
request: IUpdateMessageRequest,
|
||||
): Promise<IUpdateMessageResponse> => {
|
||||
): Promise<IUpdateMessageResponse | IResponseError> => {
|
||||
return await callApi(HTTP.PATCH, `message/${request.id}`, request);
|
||||
};
|
||||
|
||||
const removeMessageApi = async (
|
||||
request: IRemoveMessageRequest,
|
||||
): Promise<IRemoveMessageResponse> => {
|
||||
): Promise<IRemoveMessageResponse | IResponseError> => {
|
||||
return await callApi(HTTP.DELETE, `message/${request.id}`);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { IResponseSuccess } from "../types";
|
||||
|
||||
interface IFetchMessage {
|
||||
id: string;
|
||||
text: string;
|
||||
iv: string;
|
||||
editHistory: string[];
|
||||
edited: boolean;
|
||||
ownerId: string;
|
||||
|
|
@ -12,27 +15,28 @@ interface IFetchMessageRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchMessageResponse extends IFetchMessage {}
|
||||
interface IFetchMessageResponse extends IResponseSuccess, IFetchMessage {}
|
||||
|
||||
interface ICreateMessageRequest {
|
||||
text: string;
|
||||
iv: string;
|
||||
channelId: string;
|
||||
}
|
||||
|
||||
interface ICreateMessageResponse extends IFetchMessage {}
|
||||
interface ICreateMessageResponse extends IResponseSuccess, IFetchMessage {}
|
||||
|
||||
interface IUpdateMessageRequest {
|
||||
id: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface IUpdateMessageResponse extends IFetchMessage {}
|
||||
interface IUpdateMessageResponse extends IResponseSuccess, IFetchMessage {}
|
||||
|
||||
interface IRemoveMessageRequest {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface IRemoveMessageResponse {
|
||||
interface IRemoveMessageResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
ownerId: string;
|
||||
channelId: string;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { callApi, HTTP } from "../tools";
|
||||
import { IResponseError } from "../types";
|
||||
import {
|
||||
IFetchRoleRequest,
|
||||
IFetchRoleResponse,
|
||||
|
|
@ -12,25 +13,25 @@ import {
|
|||
|
||||
const fetchRoleApi = async (
|
||||
request: IFetchRoleRequest,
|
||||
): Promise<IFetchRoleResponse> => {
|
||||
): Promise<IFetchRoleResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `role/${request.id}`);
|
||||
};
|
||||
|
||||
const createRoleApi = async (
|
||||
request: ICreateRoleRequest,
|
||||
): Promise<ICreateRoleResponse> => {
|
||||
): Promise<ICreateRoleResponse | IResponseError> => {
|
||||
return await callApi(HTTP.POST, `role`, request);
|
||||
};
|
||||
|
||||
const updateRoleApi = async (
|
||||
request: IUpdateRoleRequest,
|
||||
): Promise<IUpdateRoleResponse> => {
|
||||
): Promise<IUpdateRoleResponse | IResponseError> => {
|
||||
return await callApi(HTTP.PATCH, `role/${request.id}`, request);
|
||||
};
|
||||
|
||||
const removeRoleApi = async (
|
||||
request: IRemoveRoleRequest,
|
||||
): Promise<IRemoveRoleResponse> => {
|
||||
): Promise<IRemoveRoleResponse | IResponseError> => {
|
||||
return await callApi(HTTP.DELETE, `role/${request.id}`);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { IResponseSuccess } from "../types";
|
||||
|
||||
interface IFetchRole {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
@ -11,27 +13,27 @@ interface IFetchRoleRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchRoleResponse extends IFetchRole {}
|
||||
interface IFetchRoleResponse extends IResponseSuccess, IFetchRole {}
|
||||
|
||||
interface ICreateRoleRequest {
|
||||
name: string;
|
||||
communityId: string;
|
||||
}
|
||||
|
||||
interface ICreateRoleResponse extends IFetchRole {}
|
||||
interface ICreateRoleResponse extends IResponseSuccess, IFetchRole {}
|
||||
|
||||
interface IUpdateRoleRequest {
|
||||
id: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
interface IUpdateRoleResponse extends IFetchRole {}
|
||||
interface IUpdateRoleResponse extends IResponseSuccess, IFetchRole {}
|
||||
|
||||
interface IRemoveRoleRequest {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface IRemoveRoleResponse {
|
||||
interface IRemoveRoleResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
communityId: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { callApi, HTTP } from "../tools";
|
||||
import { IResponseError } from "../types";
|
||||
import {
|
||||
IFetchSessionRequest,
|
||||
IFetchSessionResponse,
|
||||
|
|
@ -8,13 +9,13 @@ import {
|
|||
|
||||
const fetchSessionApi = async (
|
||||
request: IFetchSessionRequest,
|
||||
): Promise<IFetchSessionResponse> => {
|
||||
): Promise<IFetchSessionResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `session/${request.id}`);
|
||||
};
|
||||
|
||||
const removeSessionApi = async (
|
||||
request: IRemoveSessionRequest,
|
||||
): Promise<IRemoveSessionResponse> => {
|
||||
): Promise<IRemoveSessionResponse | IResponseError> => {
|
||||
return await callApi(HTTP.DELETE, `session/${request.id}`);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,25 @@
|
|||
import { IResponseSuccess } from "../types";
|
||||
|
||||
interface IFetchSession {
|
||||
id: string;
|
||||
userId: string;
|
||||
name: string;
|
||||
userAgent: string;
|
||||
creationDate: number;
|
||||
refreshDate: number;
|
||||
}
|
||||
|
||||
interface IFetchSessionRequest {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchSessionResponse extends IFetchSession {}
|
||||
interface IFetchSessionResponse extends IResponseSuccess, IFetchSession {}
|
||||
|
||||
interface IRemoveSessionRequest {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface IRemoveSessionResponse {
|
||||
interface IRemoveSessionResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
userId: string;
|
||||
}
|
||||
|
|
|
|||
9
src/api/types.ts
Normal file
9
src/api/types.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
interface IResponseError {
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface IResponseSuccess {
|
||||
error: null;
|
||||
}
|
||||
|
||||
export { type IResponseError, type IResponseSuccess };
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { IResponseSuccess } from "../types";
|
||||
|
||||
interface IFetchUser {
|
||||
id: string;
|
||||
username: string;
|
||||
|
|
@ -8,7 +10,7 @@ interface IFetchUser {
|
|||
lastLogin: number;
|
||||
}
|
||||
|
||||
interface IFetchLoggedUserResponse {
|
||||
interface IFetchLoggedUserResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
}
|
||||
|
||||
|
|
@ -16,13 +18,13 @@ interface IFetchUserRequest {
|
|||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchUserResponse extends IFetchUser {}
|
||||
interface IFetchUserResponse extends IResponseSuccess, IFetchUser {}
|
||||
|
||||
interface IFetchUserSessionsRequest {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchUserSessionsResponse {
|
||||
interface IFetchUserSessionsResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
sessions: IFetchUserSession[];
|
||||
}
|
||||
|
|
@ -30,13 +32,17 @@ interface IFetchUserSessionsResponse {
|
|||
interface IFetchUserSession {
|
||||
id: string;
|
||||
userId: string;
|
||||
name: string;
|
||||
userAgent: string;
|
||||
creationDate: number;
|
||||
refreshDate: number;
|
||||
}
|
||||
|
||||
interface IFetchUserCommunitiesRequest {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface IFetchUserCommunitiesResponse {
|
||||
interface IFetchUserCommunitiesResponse extends IResponseSuccess {
|
||||
id: string;
|
||||
communities: IFetchUserCommunity[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { callApi, HTTP } from "../tools";
|
||||
import { IResponseError } from "../types";
|
||||
import {
|
||||
IFetchLoggedUserResponse,
|
||||
IFetchUserRequest,
|
||||
|
|
@ -9,25 +10,27 @@ import {
|
|||
IFetchUserCommunitiesResponse,
|
||||
} from "./types";
|
||||
|
||||
const fetchLoggedUserApi = async (): Promise<IFetchLoggedUserResponse> => {
|
||||
const fetchLoggedUserApi = async (): Promise<
|
||||
IFetchLoggedUserResponse | IResponseError
|
||||
> => {
|
||||
return await callApi(HTTP.GET, `user/logged`);
|
||||
};
|
||||
|
||||
const fetchUserApi = async (
|
||||
request: IFetchUserRequest,
|
||||
): Promise<IFetchUserResponse> => {
|
||||
): Promise<IFetchUserResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `user/${request.id}`);
|
||||
};
|
||||
|
||||
const fetchUserSessionsApi = async (
|
||||
request: IFetchUserSessionsRequest,
|
||||
): Promise<IFetchUserSessionsResponse> => {
|
||||
): Promise<IFetchUserSessionsResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `user/${request.id}/sessions`);
|
||||
};
|
||||
|
||||
const fetchUserCommunitiesApi = async (
|
||||
request: IFetchUserCommunitiesRequest,
|
||||
): Promise<IFetchUserCommunitiesResponse> => {
|
||||
): Promise<IFetchUserCommunitiesResponse | IResponseError> => {
|
||||
return await callApi(HTTP.GET, `user/${request.id}/communities`);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const Community: Component<ICommunityProps> = (props: ICommunityProps) => {
|
|||
onClick={() => props.onCommunityClick?.(props.id)}
|
||||
>
|
||||
<div
|
||||
class={`w-full transition-[border-radius] duration-300 outline-stone-300 ${props.active ? "rounded-lg outline-3 hover:outline-3" : "rounded-4xl hover:outline-2"}`}
|
||||
class={`w-full transition-all duration-300 hover:outline-stone-300 ${props.active ? "rounded-lg outline-3 outline-stone-300 hover:outline-3" : "outline-transparent rounded-4xl outline-2"}`}
|
||||
>
|
||||
<img src={props.avatar} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,36 @@
|
|||
import type { Component } from "solid-js";
|
||||
import { type Component, type JSXElement } from "solid-js";
|
||||
import { ICommunityBarProps } from "./types";
|
||||
import { SettingsIcon } from "../icons";
|
||||
import { SettingsIcon } from "../../icons";
|
||||
import { state } from "../../store/state";
|
||||
|
||||
const CommunityBar: Component<ICommunityBarProps> = (
|
||||
props: ICommunityBarProps,
|
||||
) => {
|
||||
const settingsIconHtml = (): JSXElement | undefined => {
|
||||
const activeCommunityId = state.community.active;
|
||||
if (!activeCommunityId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const community = state.community.communities[activeCommunityId];
|
||||
if (!community) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (false) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
class="bg-stone-950/25 cursor-pointer rounded-full w-10 h-10 p-2 hover:bg-stone-950/75 transition-transform duration-300 hover:rotate-30"
|
||||
onClick={props.onSettingsClick}
|
||||
>
|
||||
<SettingsIcon />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
class={`absolute w-full top-0 z-10 bg-cover bg-top bg-no-repeat bg-[url('${props.avatar}')]`}
|
||||
|
|
@ -14,12 +40,7 @@ const CommunityBar: Component<ICommunityBarProps> = (
|
|||
<h2 class="text-sm font-bold">{props.name}</h2>
|
||||
<p class="text-xs">{props.description}</p>
|
||||
</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>
|
||||
{settingsIconHtml()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,119 +0,0 @@
|
|||
import { createSignal, type Component } from "solid-js";
|
||||
import { ICommunityModalProps } from "./types";
|
||||
import { dispatch } from "../../store/state";
|
||||
import { CommunityActionTypes } from "../../store/community";
|
||||
import { InviteActionTypes } from "../../store/invite";
|
||||
|
||||
const CommunityModal: Component<ICommunityModalProps> = (props) => {
|
||||
const [getCommunityName, setCommunityName] = createSignal("");
|
||||
const [getInviteId, setInviteId] = createSignal("");
|
||||
|
||||
const onCreateCommunity = () => {
|
||||
const communityName = getCommunityName();
|
||||
if (!communityName || communityName.trim().length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: CommunityActionTypes.CREATE_COMMUNITY_START,
|
||||
payload: {
|
||||
name: communityName,
|
||||
},
|
||||
});
|
||||
|
||||
setCommunityName("");
|
||||
|
||||
props.onClose?.();
|
||||
};
|
||||
|
||||
const onJoinCommunity = () => {
|
||||
const inviteId = getInviteId();
|
||||
if (!inviteId || inviteId.trim().length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: InviteActionTypes.ACCEPT_INVITE_START,
|
||||
payload: inviteId,
|
||||
});
|
||||
|
||||
setInviteId("");
|
||||
|
||||
props.onClose?.();
|
||||
};
|
||||
|
||||
const handleEnter = (e: KeyboardEvent, callback: () => void) => {
|
||||
if (e.key === "Enter") {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const createCommunityHtml = () => (
|
||||
<>
|
||||
<h3 class="text-lg font-bold text-center mb-6">
|
||||
Create a new Community
|
||||
</h3>
|
||||
<div class="bg-stone-800 h-16 p-2 flex flex-row gap-2 rounded-2xl">
|
||||
<label class="bg-stone-800 input w-full h-full rounded-xl focus:border-none outline-none">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter name of the new community"
|
||||
value={getCommunityName()}
|
||||
onInput={(e) => setCommunityName(e.currentTarget.value)}
|
||||
onKeyDown={(e) => handleEnter(e, onCreateCommunity)}
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
class="bg-stone-950 btn btn-neutral h-full rounded-xl"
|
||||
onClick={onCreateCommunity}
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const joinCommunityHtml = () => (
|
||||
<>
|
||||
<h3 class="text-lg font-bold text-center mb-6">
|
||||
Join an existing Community
|
||||
</h3>
|
||||
<div class="bg-stone-800 h-16 p-2 flex flex-row gap-2 rounded-2xl">
|
||||
<label class="bg-stone-800 input w-full h-full rounded-xl focus:border-none outline-none">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter invite ID"
|
||||
value={getInviteId()}
|
||||
onInput={(e) => setInviteId(e.currentTarget.value)}
|
||||
onKeyDown={(e) => handleEnter(e, onJoinCommunity)}
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
class="bg-stone-950 btn btn-neutral h-full rounded-xl"
|
||||
onClick={onJoinCommunity}
|
||||
>
|
||||
Join
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<dialog ref={props.dialogRef} class="modal bg-[#00000050]">
|
||||
<div class="modal-box bg-stone-950 rounded-3xl">
|
||||
{createCommunityHtml()}
|
||||
<div class="divider my-8"></div>
|
||||
{joinCommunityHtml()}
|
||||
</div>
|
||||
<form
|
||||
onClick={props.onClose}
|
||||
method="dialog"
|
||||
class="modal-backdrop"
|
||||
></form>
|
||||
</dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommunityModal;
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./CommunityModal";
|
||||
export * from "./types";
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
interface ICommunityModalProps {
|
||||
dialogRef?: (element: HTMLDialogElement) => void;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
export { type ICommunityModalProps };
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./CommunitySettingsModal";
|
||||
export * from "./types";
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
interface ICommunitySettingsModalProps {
|
||||
dialogRef?: (element: HTMLDialogElement) => void;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
export { type ICommunitySettingsModalProps };
|
||||
|
|
@ -5,7 +5,7 @@ import { Dynamic } from "solid-js/web";
|
|||
const HomeCard: Component<IHomeCardProps> = (props: IHomeCardProps) => {
|
||||
return (
|
||||
<a class="w-60 cursor-pointer" onClick={props.onClick}>
|
||||
<div class="card border-2 bg-stone-800 border-stone-500 hover:border-stone-100 w-60 h-60">
|
||||
<div class="card outline-2 bg-stone-800 outline-stone-500 hover:outline-stone-100 w-60 h-60">
|
||||
<div class="flex flex-col h-full gap-1 m-6">
|
||||
<div class="w-20">
|
||||
<Dynamic component={props.icon} />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { JSXElement } from "solid-js";
|
||||
import { IconParameters } from "../icons";
|
||||
import { IconParameters } from "../../icons";
|
||||
|
||||
interface IHomeCardProps {
|
||||
title: string;
|
||||
|
|
|
|||
40
src/components/Input/Input.tsx
Normal file
40
src/components/Input/Input.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { type Component, type JSXElement } from "solid-js";
|
||||
import { IInputProps } from "./types";
|
||||
|
||||
const Input: Component<IInputProps> = (props: IInputProps) => {
|
||||
const handleEnter = (e: KeyboardEvent) => {
|
||||
if (e.key === "Enter") {
|
||||
props.onSubmit?.();
|
||||
}
|
||||
};
|
||||
|
||||
const submitHtml = (): JSXElement => (
|
||||
<button
|
||||
class={`bg-stone-950 btn btn-neutral h-full ${props.rounded ? "rounded-full" : "rounded-xl"}`}
|
||||
onClick={props.onSubmit}
|
||||
>
|
||||
{props.submitText}
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
class={`bg-stone-800 h-16 p-2 flex flex-row gap-2 ${props.rounded ? "rounded-full" : "rounded-2xl"} ${props.outline ? "outline-2" : ""}`}
|
||||
>
|
||||
<label
|
||||
class={`bg-stone-800 input px-5 w-full h-full focus:border-none outline-none ${props.rounded ? "rounded-full" : "rounded-xl"}`}
|
||||
>
|
||||
<input
|
||||
type={props.type ? props.type : "text"}
|
||||
placeholder={props.placeholder}
|
||||
value={props.value}
|
||||
onInput={(e) => props.onChange?.(e.currentTarget.value)}
|
||||
onKeyDown={handleEnter}
|
||||
/>
|
||||
</label>
|
||||
{props.submitText ? submitHtml() : undefined}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { Input };
|
||||
2
src/components/Input/index.ts
Normal file
2
src/components/Input/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from "./Input";
|
||||
export * from "./types";
|
||||
12
src/components/Input/types.ts
Normal file
12
src/components/Input/types.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
interface IInputProps {
|
||||
value: string;
|
||||
type?: "text" | "password" | "email";
|
||||
outline?: boolean;
|
||||
rounded?: boolean;
|
||||
placeholder?: string;
|
||||
submitText?: string;
|
||||
onChange?: (value: string) => void;
|
||||
onSubmit?: () => void;
|
||||
}
|
||||
|
||||
export { type IInputProps };
|
||||
|
|
@ -13,8 +13,14 @@ const Message: Component<IMessageProps> = (props: IMessageProps) => {
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>{props.username}</div>
|
||||
<p class="list-col-wrap text-xs">{props.message}</p>
|
||||
<div class="font-bold">{props.username}</div>
|
||||
{props.decryptionStatus ? (
|
||||
<p class="list-col-wrap text-xs">{props.message}</p>
|
||||
) : (
|
||||
<p class="list-col-wrap text-xs italic">
|
||||
Decryption failed
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ interface IMessageProps {
|
|||
userId: string;
|
||||
username: string;
|
||||
avatar: string;
|
||||
decryptionStatus: boolean;
|
||||
onProfileClick?: (userId: string) => void;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { Component } from "solid-js";
|
||||
import { IMessageBarProps } from "./types";
|
||||
import { UpIcon } from "../../icons";
|
||||
|
||||
const MessageBar: Component<IMessageBarProps> = (props: IMessageBarProps) => {
|
||||
const handleEnter = (e: KeyboardEvent) => {
|
||||
|
|
@ -23,10 +24,12 @@ const MessageBar: Component<IMessageBarProps> = (props: IMessageBarProps) => {
|
|||
/>
|
||||
</label>
|
||||
<button
|
||||
class="bg-stone-950/50 backdrop-blur-lg btn btn-neutral h-full rounded-full"
|
||||
class="bg-stone-950/50 backdrop-blur-lg btn btn-neutral w-12 p-0 h-full rounded-full"
|
||||
onClick={props.onSend}
|
||||
>
|
||||
Send
|
||||
<div class="w-5">
|
||||
<UpIcon />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
64
src/components/RichSettingsItem/RichSettingsItem.tsx
Normal file
64
src/components/RichSettingsItem/RichSettingsItem.tsx
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import type { Component, JSXElement } from "solid-js";
|
||||
import { IRichSettingsItemProps } from "./types";
|
||||
import { Dynamic } from "solid-js/web";
|
||||
|
||||
const RichSettingsItem: Component<IRichSettingsItemProps> = (
|
||||
props: IRichSettingsItemProps,
|
||||
) => {
|
||||
const pictureHtml = (): JSXElement => {
|
||||
if (props.avatar) {
|
||||
return (
|
||||
<div class="avatar">
|
||||
<div class="w-12 rounded-lg">
|
||||
<img src={props.avatar} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (props.icon) {
|
||||
return (
|
||||
<div
|
||||
class={`bg-stone-700 w-12 rounded-lg p-${props.iconPadding ? props.iconPadding : 1}`}
|
||||
>
|
||||
<Dynamic component={props.icon} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const infoHtml = (): JSXElement | undefined => {
|
||||
if (!props.info) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="flex-1"></div>
|
||||
<div class="badge badge-primary badge-outline p-4 mr-11">
|
||||
{props.info}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
class={`collapse collapse-arrow rounded-xl transition-all border-2 border-stone-700 ${props.active ? "bg-stone-700 hover:bg-stone-700" : "bg-stone-900 hover:bg-stone-800"}`}
|
||||
>
|
||||
<input type="checkbox" />
|
||||
<div class="collapse-title font-semibold flex flex-row items-center gap-4 p-1">
|
||||
{pictureHtml()}
|
||||
{props.title}
|
||||
{infoHtml()}
|
||||
</div>
|
||||
<div class="collapse-content text-sm font-semibold">
|
||||
<div class="mt-2">{props.children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { RichSettingsItem };
|
||||
2
src/components/RichSettingsItem/index.ts
Normal file
2
src/components/RichSettingsItem/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from "./RichSettingsItem";
|
||||
export * from "./types";
|
||||
17
src/components/RichSettingsItem/types.ts
Normal file
17
src/components/RichSettingsItem/types.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { JSXElement } from "solid-js";
|
||||
import { IconParameters } from "../../icons";
|
||||
|
||||
interface IRichSettingsItemProps {
|
||||
id: string;
|
||||
title: string;
|
||||
text?: string;
|
||||
info?: string;
|
||||
avatar?: string;
|
||||
icon?: (props: IconParameters) => JSXElement;
|
||||
iconPadding?: number;
|
||||
active: boolean;
|
||||
children?: JSXElement;
|
||||
onClick?: (id: string) => void;
|
||||
}
|
||||
|
||||
export { type IRichSettingsItemProps };
|
||||
17
src/components/SettingsItem/SettingsItem.tsx
Normal file
17
src/components/SettingsItem/SettingsItem.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import type { Component } from "solid-js";
|
||||
import { ISettingsItemProps } from "./types";
|
||||
|
||||
const SettingsItem: Component<ISettingsItemProps> = (
|
||||
props: ISettingsItemProps,
|
||||
) => {
|
||||
return (
|
||||
<div
|
||||
class={`w-48 py-2 cursor-pointer rounded-xl text-center text-md font-semibold transition-all border-2 border-stone-700 ${props.active ? "bg-stone-700 hover:bg-stone-700" : "bg-stone-900 hover:bg-stone-800"}`}
|
||||
onClick={() => props.onClick?.(props.id)}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { SettingsItem };
|
||||
2
src/components/SettingsItem/index.ts
Normal file
2
src/components/SettingsItem/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from "./SettingsItem";
|
||||
export * from "./types";
|
||||
8
src/components/SettingsItem/types.ts
Normal file
8
src/components/SettingsItem/types.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
interface ISettingsItemProps {
|
||||
id: string;
|
||||
text: string;
|
||||
active: boolean;
|
||||
onClick?: (id: string) => void;
|
||||
}
|
||||
|
||||
export { type ISettingsItemProps };
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import type { Component } from "solid-js";
|
||||
import { ISettingsModalProps } from "./types";
|
||||
|
||||
const SettingsModal: Component<ISettingsModalProps> = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<dialog ref={props.dialogRef} class="modal bg-[#00000050]">
|
||||
<div class="modal-box bg-stone-950 rounded-3xl">
|
||||
<h3 class="text-lg font-bold text-center">Settings</h3>
|
||||
<p class="py-4 text-center">Not implemented yet</p>
|
||||
</div>
|
||||
<form
|
||||
onClick={props.onClose}
|
||||
method="dialog"
|
||||
class="modal-backdrop"
|
||||
></form>
|
||||
</dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsModal;
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./SettingsModal";
|
||||
export * from "./types";
|
||||
|
|
@ -5,9 +5,20 @@ import { Dynamic } from "solid-js/web";
|
|||
const SidebarItem: Component<ISidebarItemProps> = (
|
||||
props: ISidebarItemProps,
|
||||
) => {
|
||||
const rotateCss = (): string => {
|
||||
switch (props.hoverRotate) {
|
||||
case 30:
|
||||
return "hover:rotate-30";
|
||||
case 45:
|
||||
return "hover:rotate-45";
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
class={`bg-stone-800 w-full p-2 cursor-pointer transition-[border-radius] duration-300 outline-stone-300 ${props.active ? "rounded-lg outline-3 hover:outline-3" : "rounded-4xl hover:outline-2"}`}
|
||||
class={`bg-stone-800 w-full p-2 cursor-pointer transition-all duration-300 ${rotateCss()} hover:outline-stone-300 ${props.active ? "rounded-lg outline-3 outline-stone-300 hover:outline-3" : "outline-transparent rounded-4xl outline-2"}`}
|
||||
onClick={() => props.onClick?.()}
|
||||
>
|
||||
<Dynamic component={props.icon} />
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { JSXElement } from "solid-js";
|
||||
import { IconParameters } from "../icons";
|
||||
import { IconParameters } from "../../icons";
|
||||
|
||||
interface ISidebarItemProps {
|
||||
icon: (props: IconParameters) => JSXElement;
|
||||
active: boolean;
|
||||
hoverRotate?: 30 | 45;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
import HomeIcon from "./HomeIcon";
|
||||
import SettingsIcon from "./SettingsIcon";
|
||||
import PlusIcon from "./PlusIcon";
|
||||
|
||||
import type { IconParameters } from "./types";
|
||||
|
||||
export { IconParameters, HomeIcon, SettingsIcon, PlusIcon };
|
||||
34
src/icons/DeviceIcon.tsx
Normal file
34
src/icons/DeviceIcon.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import type { Component } from "solid-js";
|
||||
|
||||
import {
|
||||
IconParameters,
|
||||
defaultStrokeIconParameters as defaults,
|
||||
} from "./types";
|
||||
|
||||
const DeviceIcon: Component<IconParameters> = ({
|
||||
width,
|
||||
height,
|
||||
fill = defaults.fill,
|
||||
stroke = defaults.stroke,
|
||||
strokeWidth = defaults.strokeWidth,
|
||||
}: IconParameters) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={width}
|
||||
height={height}
|
||||
fill={fill}
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width={strokeWidth}
|
||||
stroke={stroke}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M10.5 1.5H8.25A2.25 2.25 0 0 0 6 3.75v16.5a2.25 2.25 0 0 0 2.25 2.25h7.5A2.25 2.25 0 0 0 18 20.25V3.75a2.25 2.25 0 0 0-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceIcon;
|
||||
34
src/icons/MinusIcon.tsx
Normal file
34
src/icons/MinusIcon.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import type { Component } from "solid-js";
|
||||
|
||||
import {
|
||||
IconParameters,
|
||||
defaultStrokeIconParameters as defaults,
|
||||
} from "./types";
|
||||
|
||||
const MinusIcon: Component<IconParameters> = ({
|
||||
width,
|
||||
height,
|
||||
fill = defaults.fill,
|
||||
stroke = defaults.stroke,
|
||||
strokeWidth = defaults.strokeWidth,
|
||||
}: IconParameters) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={width}
|
||||
height={height}
|
||||
fill={fill}
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width={strokeWidth}
|
||||
stroke={stroke}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default MinusIcon;
|
||||
31
src/icons/TrashIcon.tsx
Normal file
31
src/icons/TrashIcon.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import type { Component } from "solid-js";
|
||||
|
||||
import { IconParameters, defaultFillIconParameters as defaults } from "./types";
|
||||
|
||||
const TrashIcon: Component<IconParameters> = ({
|
||||
width,
|
||||
height,
|
||||
fill = defaults.fill,
|
||||
stroke = defaults.stroke,
|
||||
strokeWidth = defaults.strokeWidth,
|
||||
}: IconParameters) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={width}
|
||||
height={height}
|
||||
fill={fill}
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width={strokeWidth}
|
||||
stroke={stroke}
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M16.5 4.478v.227a48.816 48.816 0 0 1 3.878.512.75.75 0 1 1-.256 1.478l-.209-.035-1.005 13.07a3 3 0 0 1-2.991 2.77H8.084a3 3 0 0 1-2.991-2.77L4.087 6.66l-.209.035a.75.75 0 0 1-.256-1.478A48.567 48.567 0 0 1 7.5 4.705v-.227c0-1.564 1.213-2.9 2.816-2.951a52.662 52.662 0 0 1 3.369 0c1.603.051 2.815 1.387 2.815 2.951Zm-6.136-1.452a51.196 51.196 0 0 1 3.273 0C14.39 3.05 15 3.684 15 4.478v.113a49.488 49.488 0 0 0-6 0v-.113c0-.794.609-1.428 1.364-1.452Zm-.355 5.945a.75.75 0 1 0-1.5.058l.347 9a.75.75 0 1 0 1.499-.058l-.346-9Zm5.48.058a.75.75 0 1 0-1.498-.058l-.347 9a.75.75 0 0 0 1.5.058l.345-9Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default TrashIcon;
|
||||
34
src/icons/UpIcon.tsx
Normal file
34
src/icons/UpIcon.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import type { Component } from "solid-js";
|
||||
|
||||
import {
|
||||
IconParameters,
|
||||
defaultStrokeIconParameters as defaults,
|
||||
} from "./types";
|
||||
|
||||
const UpIcon: Component<IconParameters> = ({
|
||||
width,
|
||||
height,
|
||||
fill = defaults.fill,
|
||||
stroke = defaults.stroke,
|
||||
strokeWidth = defaults.strokeWidth,
|
||||
}: IconParameters) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={width}
|
||||
height={height}
|
||||
fill={fill}
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width={strokeWidth}
|
||||
stroke={stroke}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpIcon;
|
||||
20
src/icons/index.ts
Normal file
20
src/icons/index.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import HomeIcon from "./HomeIcon";
|
||||
import SettingsIcon from "./SettingsIcon";
|
||||
import PlusIcon from "./PlusIcon";
|
||||
import MinusIcon from "./MinusIcon";
|
||||
import DeviceIcon from "./DeviceIcon";
|
||||
import TrashIcon from "./TrashIcon";
|
||||
import UpIcon from "./UpIcon";
|
||||
|
||||
import type { IconParameters } from "./types";
|
||||
|
||||
export {
|
||||
IconParameters,
|
||||
HomeIcon,
|
||||
SettingsIcon,
|
||||
PlusIcon,
|
||||
MinusIcon,
|
||||
DeviceIcon,
|
||||
TrashIcon,
|
||||
UpIcon,
|
||||
};
|
||||
|
|
@ -1,6 +1,36 @@
|
|||
import { fetchLoginApi, fetchRefreshApi } from "../../api/auth";
|
||||
import { AuthActionTypes } from "../../store/auth";
|
||||
import { dispatch } from "../../store/state";
|
||||
import {
|
||||
fetchRegisterApi,
|
||||
fetchLoginApi,
|
||||
fetchRefreshApi,
|
||||
} from "../../api/auth";
|
||||
import {
|
||||
setLoggedIn,
|
||||
setRegisterSuccess,
|
||||
setAuthSession,
|
||||
setAuthSessionKey,
|
||||
setAuthSessionIV,
|
||||
} from "../../store/auth";
|
||||
import { state } from "../../store/state";
|
||||
import { hexToBytes } from "../crypto";
|
||||
|
||||
const fetchRegister = async (
|
||||
username: string,
|
||||
password: string,
|
||||
email: string,
|
||||
) => {
|
||||
const data = await fetchRegisterApi({
|
||||
username: username,
|
||||
password: password,
|
||||
email: email,
|
||||
});
|
||||
|
||||
if (typeof data.error === "string") {
|
||||
setRegisterSuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setRegisterSuccess(true);
|
||||
};
|
||||
|
||||
const fetchLogin = async (username: string, password: string) => {
|
||||
const data = await fetchLoginApi({
|
||||
|
|
@ -8,28 +38,42 @@ const fetchLogin = async (username: string, password: string) => {
|
|||
password: password,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: AuthActionTypes.FETCH_LOGIN_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: AuthActionTypes.FETCH_REFRESH_START,
|
||||
});
|
||||
setAuthSession(data);
|
||||
|
||||
fetchRefresh();
|
||||
};
|
||||
|
||||
const fetchRefresh = async () => {
|
||||
const data = await fetchRefreshApi();
|
||||
|
||||
dispatch({
|
||||
type: AuthActionTypes.FETCH_REFRESH_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
setLoggedIn(false);
|
||||
|
||||
dispatch({
|
||||
type: AuthActionTypes.SET_LOGGED_IN,
|
||||
payload: "id" in data,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setAuthSession(data);
|
||||
setLoggedIn(true);
|
||||
|
||||
if (
|
||||
!state.auth.session?.storageSecret ||
|
||||
state.auth.session.storageSecret.length !== 89
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [keyHex, ivHex] = state.auth.session.storageSecret.split(";");
|
||||
const key = hexToBytes(keyHex);
|
||||
const iv = hexToBytes(ivHex);
|
||||
|
||||
if (key && iv) {
|
||||
setAuthSessionKey(new Uint8Array(key));
|
||||
setAuthSessionIV(new Uint8Array(iv));
|
||||
}
|
||||
};
|
||||
|
||||
export { fetchLogin, fetchRefresh };
|
||||
export { fetchRegister, fetchLogin, fetchRefresh };
|
||||
|
|
|
|||
|
|
@ -5,18 +5,24 @@ import {
|
|||
removeChannelApi,
|
||||
fetchChannelMessagesApi,
|
||||
} from "../../api/channel";
|
||||
import { ChannelActionTypes } from "../../store/channel";
|
||||
import { dispatch } from "../../store/state";
|
||||
import {
|
||||
deleteChannel,
|
||||
setChannel,
|
||||
setChannelMessages,
|
||||
} from "../../store/channel";
|
||||
import { IMessage } from "../../store/message";
|
||||
import { decryptMessage } from "../message";
|
||||
|
||||
const fetchChannel = async (id: string) => {
|
||||
const data = await fetchChannelApi({
|
||||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ChannelActionTypes.FETCH_CHANNEL_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setChannel(data);
|
||||
};
|
||||
|
||||
const createChannel = async (name: string, communityId: string) => {
|
||||
|
|
@ -25,10 +31,11 @@ const createChannel = async (name: string, communityId: string) => {
|
|||
communityId: communityId,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ChannelActionTypes.CREATE_CHANNEL_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setChannel(data);
|
||||
};
|
||||
|
||||
const updateChannel = async (
|
||||
|
|
@ -42,10 +49,11 @@ const updateChannel = async (
|
|||
description: description,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ChannelActionTypes.UPDATE_CHANNEL_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setChannel(data);
|
||||
};
|
||||
|
||||
const removeChannel = async (id: string) => {
|
||||
|
|
@ -53,21 +61,52 @@ const removeChannel = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ChannelActionTypes.REMOVE_CHANNEL_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteChannel(data.id);
|
||||
};
|
||||
|
||||
const fetchChannelMessages = async (id: string) => {
|
||||
const fetchChannelMessages = async (id: string, communityId: string) => {
|
||||
const data = await fetchChannelMessagesApi({
|
||||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ChannelActionTypes.FETCH_CHANNEL_MESSAGES_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
const messages: IMessage[] = [];
|
||||
|
||||
for (const message of data.messages) {
|
||||
try {
|
||||
const decrypted = await decryptMessage(
|
||||
communityId,
|
||||
message.text,
|
||||
message.iv,
|
||||
);
|
||||
if (decrypted) {
|
||||
messages.push({
|
||||
...message,
|
||||
text: decrypted,
|
||||
decryptionStatus: true,
|
||||
});
|
||||
} else {
|
||||
messages.push({
|
||||
...message,
|
||||
decryptionStatus: false,
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
messages.push({
|
||||
...message,
|
||||
decryptionStatus: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setChannelMessages(data.id, messages);
|
||||
};
|
||||
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -8,22 +8,33 @@ import {
|
|||
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, state } from "../../store/state";
|
||||
import { InviteActionTypes } from "../../store/invite";
|
||||
import { setChannel } from "../../store/channel";
|
||||
import {
|
||||
deleteCommunity,
|
||||
setCommunity,
|
||||
setCommunityChannels,
|
||||
setCommunityEncryptionKey,
|
||||
setCommunityInvites,
|
||||
setCommunityMembers,
|
||||
setCommunityRoles,
|
||||
} from "../../store/community";
|
||||
import { setInvite } from "../../store/invite";
|
||||
import { setRole } from "../../store/role";
|
||||
import { state } from "../../store/state";
|
||||
import { setUser } from "../../store/user";
|
||||
import { DB_STORE, dbLoadEncrypted } from "../database";
|
||||
import { fetchUserCommunities } from "../user";
|
||||
|
||||
const fetchCommunity = async (id: string) => {
|
||||
const data = await fetchCommunityApi({
|
||||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: CommunityActionTypes.FETCH_COMMUNITY_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setCommunity(data);
|
||||
};
|
||||
|
||||
const createCommunity = async (name: string) => {
|
||||
|
|
@ -31,16 +42,14 @@ const createCommunity = async (name: string) => {
|
|||
name: name,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: CommunityActionTypes.CREATE_COMMUNITY_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setCommunity(data);
|
||||
|
||||
if (state.user.loggedUserId) {
|
||||
dispatch({
|
||||
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
|
||||
payload: state.user.loggedUserId,
|
||||
});
|
||||
fetchUserCommunities(state.user.loggedUserId);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -55,16 +64,14 @@ const updateCommunity = async (
|
|||
description: description,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: CommunityActionTypes.UPDATE_COMMUNITY_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setCommunity(data);
|
||||
|
||||
if (state.user.loggedUserId) {
|
||||
dispatch({
|
||||
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
|
||||
payload: state.user.loggedUserId,
|
||||
});
|
||||
fetchUserCommunities(state.user.loggedUserId);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -73,16 +80,14 @@ const removeCommunity = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: CommunityActionTypes.REMOVE_COMMUNITY_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteCommunity(data.id);
|
||||
|
||||
if (state.user.loggedUserId) {
|
||||
dispatch({
|
||||
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
|
||||
payload: state.user.loggedUserId,
|
||||
});
|
||||
fetchUserCommunities(state.user.loggedUserId);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -91,16 +96,14 @@ const fetchCommunityChannels = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: CommunityActionTypes.FETCH_COMMUNITY_CHANNELS_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setCommunityChannels(data.id, data.channels);
|
||||
|
||||
data.channels.forEach((channel) => {
|
||||
dispatch({
|
||||
type: ChannelActionTypes.SET_CHANNEL,
|
||||
payload: channel,
|
||||
});
|
||||
setChannel(channel);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -109,16 +112,14 @@ const fetchCommunityRoles = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: CommunityActionTypes.FETCH_COMMUNITY_ROLES_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setCommunityRoles(data.id, data.roles);
|
||||
|
||||
data.roles.forEach((role) => {
|
||||
dispatch({
|
||||
type: RoleActionTypes.SET_ROLE,
|
||||
payload: role,
|
||||
});
|
||||
setRole(role);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -127,16 +128,14 @@ const fetchCommunityMembers = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: CommunityActionTypes.FETCH_COMMUNITY_MEMBERS_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setCommunityMembers(data.id, data.members);
|
||||
|
||||
data.members.forEach((member) => {
|
||||
dispatch({
|
||||
type: UserActionTypes.SET_USER,
|
||||
payload: member,
|
||||
});
|
||||
setUser(member);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -145,15 +144,34 @@ const fetchCommunityInvites = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: CommunityActionTypes.FETCH_COMMUNITY_INVITES_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setCommunityInvites(data.id, data.invites);
|
||||
|
||||
data.invites.forEach((invite) => {
|
||||
dispatch({
|
||||
type: InviteActionTypes.SET_INVITE,
|
||||
payload: invite,
|
||||
setInvite(invite);
|
||||
});
|
||||
};
|
||||
|
||||
const loadCommunityCryptoStates = async () => {
|
||||
if (!state.user.loggedUserId) {
|
||||
return;
|
||||
}
|
||||
const communities = state.user.users[state.user.loggedUserId]?.communities;
|
||||
if (!communities) {
|
||||
return;
|
||||
}
|
||||
|
||||
communities.forEach((communityId) => {
|
||||
dbLoadEncrypted<string>(
|
||||
DB_STORE.COMMUNITY_ENCRYPTION_KEYS,
|
||||
communityId,
|
||||
).then((communityEncryptionKey) => {
|
||||
if (communityEncryptionKey) {
|
||||
setCommunityEncryptionKey(communityId, communityEncryptionKey);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -167,4 +185,5 @@ export {
|
|||
fetchCommunityRoles,
|
||||
fetchCommunityMembers,
|
||||
fetchCommunityInvites,
|
||||
loadCommunityCryptoStates,
|
||||
};
|
||||
|
|
|
|||
95
src/services/crypto/crypto.ts
Normal file
95
src/services/crypto/crypto.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import { ICryptoEncrypted, ICryptoData } from "./types";
|
||||
|
||||
const importKey = async (key: Uint8Array<ArrayBuffer>): Promise<CryptoKey> => {
|
||||
return await crypto.subtle.importKey(
|
||||
"raw",
|
||||
key,
|
||||
{ name: "AES-GCM" },
|
||||
false,
|
||||
["encrypt", "decrypt"],
|
||||
);
|
||||
};
|
||||
|
||||
const deriveKey = async (password: string): Promise<CryptoKey> => {
|
||||
const salt = "NEXLINK_FIXED_SALT";
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
const passwordKey = await crypto.subtle.importKey(
|
||||
"raw",
|
||||
encoder.encode(password),
|
||||
"PBKDF2",
|
||||
false,
|
||||
["deriveKey"],
|
||||
);
|
||||
|
||||
return crypto.subtle.deriveKey(
|
||||
{
|
||||
name: "PBKDF2",
|
||||
salt: encoder.encode(salt),
|
||||
iterations: 100000,
|
||||
hash: "SHA-256",
|
||||
},
|
||||
passwordKey,
|
||||
{ name: "AES-GCM", length: 256 },
|
||||
true,
|
||||
["encrypt", "decrypt"],
|
||||
);
|
||||
};
|
||||
|
||||
const encryptData = async <T>(
|
||||
cryptoData: ICryptoData<T>,
|
||||
): Promise<ArrayBuffer> => {
|
||||
const encoded = new TextEncoder().encode(JSON.stringify(cryptoData.data));
|
||||
const encrypted = await crypto.subtle.encrypt(
|
||||
{ name: "AES-GCM", iv: cryptoData.iv },
|
||||
cryptoData.key,
|
||||
encoded,
|
||||
);
|
||||
|
||||
return encrypted;
|
||||
};
|
||||
|
||||
const decryptData = async <T>(cryptoData: ICryptoEncrypted): Promise<T> => {
|
||||
const decrypted = await crypto.subtle.decrypt(
|
||||
{ name: "AES-GCM", iv: cryptoData.iv },
|
||||
cryptoData.key,
|
||||
cryptoData.encryptedData,
|
||||
);
|
||||
const decoded = JSON.parse(new TextDecoder().decode(decrypted));
|
||||
|
||||
return decoded as T;
|
||||
};
|
||||
|
||||
const generateIv = (): Uint8Array<ArrayBuffer> => {
|
||||
return crypto.getRandomValues(new Uint8Array(12));
|
||||
};
|
||||
|
||||
const hexToBytes = (hex: string): ArrayBuffer | undefined => {
|
||||
if (hex.length % 2 !== 0) {
|
||||
return;
|
||||
}
|
||||
const bytes = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < hex.length; i++) {
|
||||
bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
|
||||
}
|
||||
return bytes.buffer;
|
||||
};
|
||||
|
||||
const bytesToHex = (bytes: ArrayBuffer): string => {
|
||||
const bytesUint8 = new Uint8Array(bytes);
|
||||
let hex = "";
|
||||
for (const byte of bytesUint8) {
|
||||
hex += byte.toString(16).padStart(2, "0");
|
||||
}
|
||||
return hex;
|
||||
};
|
||||
|
||||
export {
|
||||
importKey,
|
||||
deriveKey,
|
||||
encryptData,
|
||||
decryptData,
|
||||
generateIv,
|
||||
hexToBytes,
|
||||
bytesToHex,
|
||||
};
|
||||
1
src/services/crypto/index.ts
Normal file
1
src/services/crypto/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from "./crypto";
|
||||
13
src/services/crypto/types.ts
Normal file
13
src/services/crypto/types.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
interface ICryptoEncrypted {
|
||||
key: CryptoKey;
|
||||
iv: Uint8Array<ArrayBuffer>;
|
||||
encryptedData: ArrayBuffer;
|
||||
}
|
||||
|
||||
interface ICryptoData<T> {
|
||||
key: CryptoKey;
|
||||
iv: Uint8Array<ArrayBuffer>;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export { type ICryptoEncrypted, type ICryptoData };
|
||||
114
src/services/database/database.ts
Normal file
114
src/services/database/database.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import { state } from "../../store/state";
|
||||
import { decryptData, encryptData, importKey } from "../crypto";
|
||||
import { DB_STORE, IDatabaseItem } from "./types";
|
||||
|
||||
const DB_VERSION = 1;
|
||||
|
||||
const openDB = async (name: string, store: string): Promise<IDBDatabase> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(name, DB_VERSION);
|
||||
|
||||
request.onupgradeneeded = () => {
|
||||
const db = request.result;
|
||||
|
||||
if (
|
||||
!db.objectStoreNames.contains(
|
||||
DB_STORE.COMMUNITY_ENCRYPTION_KEYS,
|
||||
)
|
||||
) {
|
||||
db.createObjectStore(DB_STORE.COMMUNITY_ENCRYPTION_KEYS, {
|
||||
keyPath: "id",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
};
|
||||
|
||||
const getDB = async (store: string): Promise<IDBDatabase> => {
|
||||
return await openDB("pulsardb", store);
|
||||
};
|
||||
|
||||
const dbSave = async (store: string, item: IDatabaseItem): Promise<void> => {
|
||||
const db = await getDB(store);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(store, "readwrite");
|
||||
tx.objectStore(store).put(item);
|
||||
|
||||
tx.oncomplete = () => resolve();
|
||||
tx.onerror = () => reject(tx.error);
|
||||
});
|
||||
};
|
||||
|
||||
const dbLoad = async (
|
||||
store: string,
|
||||
id: IDBValidKey,
|
||||
): Promise<IDatabaseItem | undefined> => {
|
||||
const db = await getDB(store);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(store, "readonly");
|
||||
const req = tx.objectStore(store).get(id);
|
||||
|
||||
req.onsuccess = () => resolve(req.result as IDatabaseItem | undefined);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
};
|
||||
|
||||
const dbSaveEncrypted = async <T>(store: string, id: IDBValidKey, item: T) => {
|
||||
const key = state.auth.session?.key;
|
||||
const iv = state.auth.session?.iv;
|
||||
if (!key || !iv) {
|
||||
return;
|
||||
}
|
||||
|
||||
const importedKey = await importKey(key);
|
||||
const encrypted = await encryptData<T>({
|
||||
key: importedKey,
|
||||
iv: iv,
|
||||
data: item,
|
||||
});
|
||||
|
||||
await dbSave(store, {
|
||||
id: id,
|
||||
data: encrypted,
|
||||
});
|
||||
};
|
||||
|
||||
const dbLoadEncrypted = async <T>(
|
||||
store: string,
|
||||
id: IDBValidKey,
|
||||
): Promise<T | undefined> => {
|
||||
const key = state.auth.session?.key;
|
||||
const iv = state.auth.session?.iv;
|
||||
if (!key || !iv) {
|
||||
return;
|
||||
}
|
||||
|
||||
const item = await dbLoad(store, id);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
const importedKey = await importKey(key);
|
||||
const decrypted = await decryptData<T>({
|
||||
key: importedKey,
|
||||
iv: iv,
|
||||
encryptedData: item.data,
|
||||
});
|
||||
|
||||
return decrypted;
|
||||
};
|
||||
|
||||
export {
|
||||
openDB,
|
||||
getDB,
|
||||
dbSave,
|
||||
dbLoad,
|
||||
importKey,
|
||||
dbSaveEncrypted,
|
||||
dbLoadEncrypted,
|
||||
};
|
||||
2
src/services/database/index.ts
Normal file
2
src/services/database/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from "./database";
|
||||
export * from "./types";
|
||||
10
src/services/database/types.ts
Normal file
10
src/services/database/types.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
enum DB_STORE {
|
||||
COMMUNITY_ENCRYPTION_KEYS = "COMMUNITY_ENCRYPTION_KEYS",
|
||||
}
|
||||
|
||||
interface IDatabaseItem {
|
||||
id: IDBValidKey;
|
||||
data: ArrayBuffer;
|
||||
}
|
||||
|
||||
export { DB_STORE, type IDatabaseItem };
|
||||
|
|
@ -3,19 +3,20 @@ import {
|
|||
removeInviteApi,
|
||||
acceptInviteApi,
|
||||
} from "../../api/invite";
|
||||
import { InviteActionTypes } from "../../store/invite";
|
||||
import { dispatch, state } from "../../store/state";
|
||||
import { UserActionTypes } from "../../store/user";
|
||||
import { deleteInvite, setInvite } from "../../store/invite";
|
||||
import { state } from "../../store/state";
|
||||
import { fetchUserCommunities } from "../user";
|
||||
|
||||
const fetchInvite = async (id: string) => {
|
||||
const data = await fetchInviteApi({
|
||||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: InviteActionTypes.FETCH_INVITE_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setInvite(data);
|
||||
};
|
||||
|
||||
const removeInvite = async (id: string) => {
|
||||
|
|
@ -23,10 +24,11 @@ const removeInvite = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: InviteActionTypes.REMOVE_INVITE_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteInvite(data.id);
|
||||
};
|
||||
|
||||
const acceptInvite = async (id: string) => {
|
||||
|
|
@ -34,16 +36,12 @@ const acceptInvite = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: InviteActionTypes.ACCEPT_INVITE_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.user.loggedUserId) {
|
||||
dispatch({
|
||||
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
|
||||
payload: state.user.loggedUserId,
|
||||
});
|
||||
fetchUserCommunities(state.user.loggedUserId);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,29 +4,62 @@ import {
|
|||
updateMessageApi,
|
||||
removeMessageApi,
|
||||
} from "../../api/message";
|
||||
import { MessageActionTypes } from "../../store/message";
|
||||
import { dispatch } from "../../store/state";
|
||||
import { deleteMessage, setMessage } from "../../store/message";
|
||||
import { state } from "../../store/state";
|
||||
import {
|
||||
bytesToHex,
|
||||
decryptData,
|
||||
encryptData,
|
||||
generateIv,
|
||||
hexToBytes,
|
||||
} from "../crypto";
|
||||
|
||||
const fetchMessage = async (id: string) => {
|
||||
const fetchMessage = async (id: string, communityId: string) => {
|
||||
const data = await fetchMessageApi({
|
||||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: MessageActionTypes.FETCH_MESSAGE_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const decrypted = await decryptMessage(communityId, data.text, data.iv);
|
||||
if (!decrypted) {
|
||||
return;
|
||||
}
|
||||
|
||||
setMessage({
|
||||
...data,
|
||||
text: decrypted,
|
||||
});
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const createMessage = async (text: string, channelId: string) => {
|
||||
const createMessage = async (
|
||||
communityId: string,
|
||||
channelId: string,
|
||||
text: string,
|
||||
) => {
|
||||
const encrypted = await encryptMessage(communityId, text);
|
||||
if (!encrypted) {
|
||||
return;
|
||||
}
|
||||
const [encryptedMessage, iv] = encrypted;
|
||||
|
||||
const data = await createMessageApi({
|
||||
text: text,
|
||||
channelId: channelId,
|
||||
iv: iv,
|
||||
text: encryptedMessage,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: MessageActionTypes.CREATE_MESSAGE_FINISH,
|
||||
payload: data,
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setMessage({
|
||||
...data,
|
||||
text: text,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -36,10 +69,11 @@ const updateMessage = async (id: string, text: string) => {
|
|||
text: text,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: MessageActionTypes.UPDATE_MESSAGE_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setMessage(data);
|
||||
};
|
||||
|
||||
const removeMessage = async (id: string) => {
|
||||
|
|
@ -47,10 +81,60 @@ const removeMessage = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: MessageActionTypes.REMOVE_MESSAGE_FINISH,
|
||||
payload: data,
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteMessage();
|
||||
};
|
||||
|
||||
const encryptMessage = async (
|
||||
communityId: string,
|
||||
text: string,
|
||||
): Promise<[string, string] | undefined> => {
|
||||
const key = state.community.communities[communityId]?.derivedKey;
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
|
||||
const iv = generateIv();
|
||||
const encrypted = await encryptData<string>({
|
||||
key: key,
|
||||
iv: iv,
|
||||
data: text,
|
||||
});
|
||||
|
||||
return [bytesToHex(encrypted), bytesToHex(iv.buffer)];
|
||||
};
|
||||
|
||||
const decryptMessage = async (
|
||||
communityId: string,
|
||||
text: string,
|
||||
iv: string,
|
||||
): Promise<string | undefined> => {
|
||||
const key = state.community.communities[communityId]?.derivedKey;
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ivBytes = hexToBytes(iv);
|
||||
const textBytes = hexToBytes(text);
|
||||
if (!ivBytes || !textBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
return await decryptData({
|
||||
key: key,
|
||||
iv: new Uint8Array(ivBytes),
|
||||
encryptedData: textBytes,
|
||||
});
|
||||
};
|
||||
|
||||
export { fetchMessage, createMessage, updateMessage, removeMessage };
|
||||
export {
|
||||
fetchMessage,
|
||||
createMessage,
|
||||
updateMessage,
|
||||
removeMessage,
|
||||
encryptMessage,
|
||||
decryptMessage,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@ import {
|
|||
updateRoleApi,
|
||||
removeRoleApi,
|
||||
} from "../../api/role";
|
||||
import { RoleActionTypes } from "../../store/role";
|
||||
import { dispatch } from "../../store/state";
|
||||
import { deleteRole, setRole } from "../../store/role";
|
||||
|
||||
const fetchRole = async (id: string) => {
|
||||
const data = await fetchRoleApi({
|
||||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: RoleActionTypes.FETCH_ROLE_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setRole(data);
|
||||
};
|
||||
|
||||
const createRole = async (name: string, communityId: string) => {
|
||||
|
|
@ -24,10 +24,11 @@ const createRole = async (name: string, communityId: string) => {
|
|||
communityId: communityId,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: RoleActionTypes.CREATE_ROLE_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setRole(data);
|
||||
};
|
||||
|
||||
const updateRole = async (id: string, name?: string) => {
|
||||
|
|
@ -36,10 +37,11 @@ const updateRole = async (id: string, name?: string) => {
|
|||
name: name,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: RoleActionTypes.UPDATE_ROLE_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setRole(data);
|
||||
};
|
||||
|
||||
const removeRole = async (id: string) => {
|
||||
|
|
@ -47,10 +49,11 @@ const removeRole = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: RoleActionTypes.REMOVE_ROLE_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteRole(data.id);
|
||||
};
|
||||
|
||||
export { fetchRole, createRole, updateRole, removeRole };
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import { fetchSessionApi, removeSessionApi } from "../../api/session";
|
||||
import { SessionActionTypes } from "../../store/session";
|
||||
import { dispatch } from "../../store/state";
|
||||
import { deleteSession, setSession } from "../../store/session";
|
||||
|
||||
const fetchSession = async (id: string) => {
|
||||
const data = await fetchSessionApi({
|
||||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: SessionActionTypes.FETCH_SESSION_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setSession(data);
|
||||
};
|
||||
|
||||
const removeSession = async (id: string) => {
|
||||
|
|
@ -18,10 +18,11 @@ const removeSession = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: SessionActionTypes.REMOVE_SESSION_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteSession(data.id);
|
||||
};
|
||||
|
||||
export { fetchSession, removeSession };
|
||||
|
|
|
|||
|
|
@ -4,18 +4,23 @@ import {
|
|||
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";
|
||||
import { setCommunity } from "../../store/community";
|
||||
import { setSession } from "../../store/session";
|
||||
import {
|
||||
setLoggedUserId,
|
||||
setUser,
|
||||
setUserCommunities,
|
||||
setUserSessions,
|
||||
} from "../../store/user";
|
||||
|
||||
const fetchLoggedUser = async () => {
|
||||
const data = await fetchLoggedUserApi();
|
||||
|
||||
dispatch({
|
||||
type: UserActionTypes.FETCH_LOGGED_USER_ID_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoggedUserId(data.id);
|
||||
};
|
||||
|
||||
const fetchUser = async (id: string) => {
|
||||
|
|
@ -23,10 +28,11 @@ const fetchUser = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: UserActionTypes.FETCH_USER_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setUser(data);
|
||||
};
|
||||
|
||||
const fetchUserSessions = async (id: string) => {
|
||||
|
|
@ -34,16 +40,14 @@ const fetchUserSessions = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: UserActionTypes.FETCH_USER_SESSIONS_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setUserSessions(data);
|
||||
|
||||
data.sessions.forEach((session) => {
|
||||
dispatch({
|
||||
type: SessionActionTypes.SET_SESSION,
|
||||
payload: session,
|
||||
});
|
||||
setSession(session);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -52,16 +56,14 @@ const fetchUserCommunities = async (id: string) => {
|
|||
id: id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: UserActionTypes.FETCH_USER_COMMUNITIES_FINISH,
|
||||
payload: data,
|
||||
});
|
||||
if (typeof data.error === "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
setUserCommunities(data);
|
||||
|
||||
data.communities.forEach((community) => {
|
||||
dispatch({
|
||||
type: CommunityActionTypes.SET_COMMUNITY,
|
||||
payload: community,
|
||||
});
|
||||
setCommunity(community);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,12 @@ enum SocketRequestTypes {
|
|||
}
|
||||
|
||||
enum SocketMessageTypes {
|
||||
NEW_ANNOUNCEMENT = "NEW_ANNOUNCEMENT",
|
||||
NEW_MESSAGE = "NEW_MESSAGE",
|
||||
NEW_CHANNEL = "NEW_CHANNEL",
|
||||
ANNOUNCEMENT = "ANNOUNCEMENT",
|
||||
SET_MESSAGE = "SET_MESSAGE",
|
||||
DELETE_MESSAGE = "DELETE_MESSAGE",
|
||||
UPDATE_CHANNELS = "UPDATE_CHANNELS",
|
||||
UPDATE_ROLES = "UPDATE_ROLES",
|
||||
UPDATE_MEMBERS = "UPDATE_MEMBERS",
|
||||
}
|
||||
|
||||
type SocketRequest = {
|
||||
|
|
@ -16,25 +19,42 @@ type SocketRequest = {
|
|||
|
||||
type SocketMessage =
|
||||
| {
|
||||
type: SocketMessageTypes.NEW_ANNOUNCEMENT;
|
||||
type: SocketMessageTypes.ANNOUNCEMENT;
|
||||
payload: {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: SocketMessageTypes.NEW_MESSAGE;
|
||||
type: SocketMessageTypes.SET_MESSAGE;
|
||||
payload: {
|
||||
channelId: string;
|
||||
message: IFetchChannelMessage;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: SocketMessageTypes.NEW_CHANNEL;
|
||||
type: SocketMessageTypes.DELETE_MESSAGE;
|
||||
payload: {
|
||||
channelId: string;
|
||||
messageId: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: SocketMessageTypes.UPDATE_CHANNELS;
|
||||
payload: {
|
||||
communityId: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: SocketMessageTypes.UPDATE_ROLES;
|
||||
payload: {
|
||||
communityId: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: SocketMessageTypes.UPDATE_MEMBERS;
|
||||
payload: {
|
||||
id: string;
|
||||
communityId: string;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { ChannelActionTypes } from "../../store/channel";
|
||||
import { dispatch } from "../../store/state";
|
||||
import { deleteChannelMessage, setChannelMessage } from "../../store/channel";
|
||||
import { state } from "../../store/state";
|
||||
import {
|
||||
fetchCommunityChannels,
|
||||
fetchCommunityMembers,
|
||||
fetchCommunityRoles,
|
||||
} from "../community";
|
||||
import { decryptMessage } from "../message";
|
||||
import config from "./config.json";
|
||||
import { SocketMessage, SocketMessageTypes } from "./types";
|
||||
|
||||
|
|
@ -38,18 +44,62 @@ const parseMessage = (data: string): SocketMessage | null => {
|
|||
|
||||
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.SET_MESSAGE: {
|
||||
try {
|
||||
const communityId =
|
||||
state.channel.channels[message.payload.channelId]
|
||||
?.communityId;
|
||||
if (!communityId) {
|
||||
break;
|
||||
}
|
||||
decryptMessage(
|
||||
communityId,
|
||||
message.payload.message.text,
|
||||
message.payload.message.iv,
|
||||
).then((decrypted) => {
|
||||
if (decrypted) {
|
||||
setChannelMessage(message.payload.channelId, {
|
||||
...message.payload.message,
|
||||
text: decrypted,
|
||||
decryptionStatus: true,
|
||||
});
|
||||
} else {
|
||||
setChannelMessage(message.payload.channelId, {
|
||||
...message.payload.message,
|
||||
decryptionStatus: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
setChannelMessage(message.payload.channelId, {
|
||||
...message.payload.message,
|
||||
decryptionStatus: false,
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SocketMessageTypes.NEW_CHANNEL: {
|
||||
case SocketMessageTypes.DELETE_MESSAGE: {
|
||||
deleteChannelMessage(
|
||||
message.payload.channelId,
|
||||
message.payload.messageId,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case SocketMessageTypes.NEW_ANNOUNCEMENT: {
|
||||
case SocketMessageTypes.UPDATE_CHANNELS: {
|
||||
fetchCommunityChannels(message.payload.communityId);
|
||||
break;
|
||||
}
|
||||
case SocketMessageTypes.UPDATE_ROLES: {
|
||||
fetchCommunityRoles(message.payload.communityId);
|
||||
break;
|
||||
}
|
||||
case SocketMessageTypes.UPDATE_MEMBERS: {
|
||||
fetchCommunityMembers(message.payload.communityId);
|
||||
break;
|
||||
}
|
||||
case SocketMessageTypes.ANNOUNCEMENT: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
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";
|
||||
import { MessageActionTypes, MessageAction } from "./message";
|
||||
|
||||
type ActionTypes =
|
||||
| AppActionTypes
|
||||
| AuthActionTypes
|
||||
| UserActionTypes
|
||||
| CommunityActionTypes
|
||||
| ChannelActionTypes
|
||||
| RoleActionTypes
|
||||
| SessionActionTypes
|
||||
| InviteActionTypes
|
||||
| MessageActionTypes;
|
||||
|
||||
type Action =
|
||||
| AppAction
|
||||
| AuthAction
|
||||
| UserAction
|
||||
| CommunityAction
|
||||
| ChannelAction
|
||||
| RoleAction
|
||||
| SessionAction
|
||||
| InviteAction
|
||||
| MessageAction;
|
||||
|
||||
export { type Action, type ActionTypes };
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
enum AppActionTypes {
|
||||
SET_HOME_OPEN = "SET_HOME_OPEN",
|
||||
SET_SETTINGS_OPEN = "SET_SETTINGS_OPEN",
|
||||
SET_ADD_COMMUNITY_OPEN = "SET_ADD_COMMUNITY_OPEN",
|
||||
SET_ADD_COMMUNITY_SETTINGS_OPEN = "SET_ADD_COMMUNITY_SETTINGS_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 }
|
||||
| {
|
||||
type: AppActionTypes.SET_ADD_COMMUNITY_SETTINGS_OPEN;
|
||||
payload: boolean;
|
||||
};
|
||||
|
||||
export { AppActionTypes, type AppAction };
|
||||
|
|
@ -1,30 +1,27 @@
|
|||
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;
|
||||
case AppActionTypes.SET_ADD_COMMUNITY_SETTINGS_OPEN:
|
||||
setState("app", "dialogsOpen", "communitySettingsOpen", false);
|
||||
setState(
|
||||
"app",
|
||||
"dialogsOpen",
|
||||
"communitySettingsOpen",
|
||||
action.payload,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const setHomeOpen = (value: boolean) => {
|
||||
setState("app", "homeOpen", value);
|
||||
};
|
||||
|
||||
export { appReducer };
|
||||
const setSettingsOpen = (value: boolean) => {
|
||||
setState("app", "dialogsOpen", "settingsOpen", false);
|
||||
setState("app", "dialogsOpen", "settingsOpen", value);
|
||||
};
|
||||
|
||||
const setAddCommunityOpen = (value: boolean) => {
|
||||
setState("app", "dialogsOpen", "addCommunityOpen", false);
|
||||
setState("app", "dialogsOpen", "addCommunityOpen", value);
|
||||
};
|
||||
|
||||
const setCommunitySettingsOpen = (value: boolean) => {
|
||||
setState("app", "dialogsOpen", "communitySettingsOpen", false);
|
||||
setState("app", "dialogsOpen", "communitySettingsOpen", value);
|
||||
};
|
||||
|
||||
export {
|
||||
setHomeOpen,
|
||||
setSettingsOpen,
|
||||
setAddCommunityOpen,
|
||||
setCommunitySettingsOpen,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,2 @@
|
|||
export * from "./app";
|
||||
export * from "./actions";
|
||||
export * from "./types";
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
import {
|
||||
IFetchLoginRequest,
|
||||
IFetchLoginResponse,
|
||||
IFetchRefreshResponseError,
|
||||
IFetchRefreshResponseSuccess,
|
||||
} from "../../api/auth";
|
||||
|
||||
enum AuthActionTypes {
|
||||
SET_LOGGED_IN = "SET_LOGGED_IN",
|
||||
FETCH_LOGIN_START = "FETCH_LOGIN_START",
|
||||
FETCH_LOGIN_FINISH = "FETCH_LOGIN_FINISH",
|
||||
FETCH_REFRESH_START = "FETCH_REFRESH_START",
|
||||
FETCH_REFRESH_FINISH = "FETCH_REFRESH_FINISH",
|
||||
}
|
||||
|
||||
type AuthAction =
|
||||
| { type: AuthActionTypes.SET_LOGGED_IN; payload: boolean }
|
||||
| { type: AuthActionTypes.FETCH_LOGIN_START; payload: IFetchLoginRequest }
|
||||
| {
|
||||
type: AuthActionTypes.FETCH_LOGIN_FINISH;
|
||||
payload: IFetchLoginResponse;
|
||||
}
|
||||
| { type: AuthActionTypes.FETCH_REFRESH_START }
|
||||
| {
|
||||
type: AuthActionTypes.FETCH_REFRESH_FINISH;
|
||||
payload: IFetchRefreshResponseError | IFetchRefreshResponseSuccess;
|
||||
};
|
||||
|
||||
export { AuthActionTypes, type AuthAction };
|
||||
|
|
@ -1,28 +1,45 @@
|
|||
import { fetchLogin, fetchRefresh } from "../../services/auth";
|
||||
import { setState } from "../state";
|
||||
import { AuthActionTypes, AuthAction } from "./actions";
|
||||
import { IAuthState } from "./types";
|
||||
import { IAuthSession } from "./types";
|
||||
|
||||
function authReducer(_state: IAuthState, action: AuthAction) {
|
||||
switch (action.type) {
|
||||
case AuthActionTypes.SET_LOGGED_IN:
|
||||
setState("auth", "loggedIn", action.payload);
|
||||
break;
|
||||
case AuthActionTypes.FETCH_LOGIN_START:
|
||||
fetchLogin(action.payload.username, action.payload.password);
|
||||
break;
|
||||
case AuthActionTypes.FETCH_LOGIN_FINISH:
|
||||
setState("auth", "session", action.payload);
|
||||
break;
|
||||
case AuthActionTypes.FETCH_REFRESH_START:
|
||||
fetchRefresh();
|
||||
break;
|
||||
case AuthActionTypes.FETCH_REFRESH_FINISH:
|
||||
if ("id" in action.payload) {
|
||||
setState("auth", "session", action.payload);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
const setRegisterSuccess = (value: boolean) => {
|
||||
setState("auth", "registerSuccess", value);
|
||||
};
|
||||
|
||||
export { authReducer };
|
||||
const resetRegisterSuccess = () => {
|
||||
setState("auth", "registerSuccess", undefined);
|
||||
};
|
||||
|
||||
const setLoggedIn = (value: boolean) => {
|
||||
setState("auth", "loggedIn", value);
|
||||
};
|
||||
|
||||
const resetLoggedIn = () => {
|
||||
setState("auth", "loggedIn", undefined);
|
||||
};
|
||||
|
||||
const setAuthSession = (session: IAuthSession) => {
|
||||
setState("auth", "session", session);
|
||||
};
|
||||
|
||||
const resetAuthSession = () => {
|
||||
setState("auth", "session", undefined);
|
||||
};
|
||||
|
||||
const setAuthSessionKey = (key: Uint8Array<ArrayBuffer>) => {
|
||||
setState("auth", "session", "key", key);
|
||||
};
|
||||
|
||||
const setAuthSessionIV = (iv: Uint8Array<ArrayBuffer>) => {
|
||||
setState("auth", "session", "iv", iv);
|
||||
};
|
||||
|
||||
export {
|
||||
setRegisterSuccess,
|
||||
resetRegisterSuccess,
|
||||
setLoggedIn,
|
||||
resetLoggedIn,
|
||||
setAuthSession,
|
||||
resetAuthSession,
|
||||
setAuthSessionKey,
|
||||
setAuthSessionIV,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,2 @@
|
|||
export * from "./auth";
|
||||
export * from "./actions";
|
||||
export * from "./types";
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
interface IAuthState {
|
||||
registerSuccess?: boolean;
|
||||
loggedIn?: boolean;
|
||||
session?: ISession;
|
||||
session?: IAuthSession;
|
||||
}
|
||||
|
||||
interface ISession {
|
||||
interface IAuthSession {
|
||||
id?: string;
|
||||
ownerId?: string;
|
||||
token?: string;
|
||||
storageSecret?: string;
|
||||
key?: Uint8Array<ArrayBuffer>;
|
||||
iv?: Uint8Array<ArrayBuffer>;
|
||||
}
|
||||
|
||||
export { type IAuthState, type ISession };
|
||||
export { type IAuthState, type IAuthSession };
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
import {
|
||||
ICreateChannelRequest,
|
||||
IUpdateChannelRequest,
|
||||
IFetchChannelResponse,
|
||||
ICreateChannelResponse,
|
||||
IUpdateChannelResponse,
|
||||
IRemoveChannelResponse,
|
||||
IFetchChannelMessagesResponse,
|
||||
IFetchChannelMessage,
|
||||
} from "../../api/channel";
|
||||
import { IFetchCommunityChannel } from "../../api/community";
|
||||
|
||||
enum ChannelActionTypes {
|
||||
SET_CHANNEL = "SET_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_FINISH = "FETCH_CHANNEL_FINISH",
|
||||
CREATE_CHANNEL_START = "CREATE_CHANNEL_START",
|
||||
CREATE_CHANNEL_FINISH = "CREATE_CHANNEL_FINISH",
|
||||
UPDATE_CHANNEL_START = "UPDATE_CHANNEL_START",
|
||||
UPDATE_CHANNEL_FINISH = "UPDATE_CHANNEL_FINISH",
|
||||
REMOVE_CHANNEL_START = "REMOVE_CHANNEL_START",
|
||||
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: ChannelActionTypes.SET_CHANNEL;
|
||||
payload: IFetchCommunityChannel;
|
||||
}
|
||||
| {
|
||||
type: ChannelActionTypes.SET_ACTIVE_CHANNEL;
|
||||
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_FINISH;
|
||||
payload: IFetchChannelResponse;
|
||||
}
|
||||
| {
|
||||
type: ChannelActionTypes.CREATE_CHANNEL_START;
|
||||
payload: ICreateChannelRequest;
|
||||
}
|
||||
| {
|
||||
type: ChannelActionTypes.CREATE_CHANNEL_FINISH;
|
||||
payload: ICreateChannelResponse;
|
||||
}
|
||||
| {
|
||||
type: ChannelActionTypes.UPDATE_CHANNEL_START;
|
||||
payload: IUpdateChannelRequest;
|
||||
}
|
||||
| {
|
||||
type: ChannelActionTypes.UPDATE_CHANNEL_FINISH;
|
||||
payload: IUpdateChannelResponse;
|
||||
}
|
||||
| { type: ChannelActionTypes.REMOVE_CHANNEL_START; payload: string }
|
||||
| {
|
||||
type: ChannelActionTypes.REMOVE_CHANNEL_FINISH;
|
||||
payload: IRemoveChannelResponse;
|
||||
}
|
||||
| { type: ChannelActionTypes.FETCH_CHANNEL_MESSAGES_START; payload: string }
|
||||
| {
|
||||
type: ChannelActionTypes.FETCH_CHANNEL_MESSAGES_FINISH;
|
||||
payload: IFetchChannelMessagesResponse;
|
||||
};
|
||||
|
||||
export { ChannelActionTypes, type ChannelAction };
|
||||
|
|
@ -1,93 +1,60 @@
|
|||
import {
|
||||
fetchChannel,
|
||||
createChannel,
|
||||
updateChannel,
|
||||
removeChannel,
|
||||
fetchChannelMessages,
|
||||
} from "../../services/channel";
|
||||
import { IMessage } from "../message";
|
||||
import { setState } from "../state";
|
||||
import { ChannelActionTypes, ChannelAction } from "./actions";
|
||||
import { IChannelState } from "./types";
|
||||
import { IChannel } from "./types";
|
||||
|
||||
function channelReducer(_state: IChannelState, action: ChannelAction) {
|
||||
switch (action.type) {
|
||||
case ChannelActionTypes.SET_CHANNEL:
|
||||
setState("channel", "channels", action.payload.id, action.payload);
|
||||
break;
|
||||
case ChannelActionTypes.SET_ACTIVE_CHANNEL:
|
||||
setState("channel", "active", action.payload);
|
||||
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:
|
||||
fetchChannel(action.payload);
|
||||
break;
|
||||
case ChannelActionTypes.FETCH_CHANNEL_FINISH:
|
||||
setState("channel", "channels", action.payload.id, action.payload);
|
||||
break;
|
||||
case ChannelActionTypes.CREATE_CHANNEL_START:
|
||||
createChannel(action.payload.name, action.payload.communityId);
|
||||
break;
|
||||
case ChannelActionTypes.CREATE_CHANNEL_FINISH:
|
||||
setState("channel", "channels", action.payload.id, action.payload);
|
||||
break;
|
||||
case ChannelActionTypes.UPDATE_CHANNEL_START:
|
||||
updateChannel(
|
||||
action.payload.id,
|
||||
action.payload.name,
|
||||
action.payload.description,
|
||||
);
|
||||
break;
|
||||
case ChannelActionTypes.UPDATE_CHANNEL_FINISH:
|
||||
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", (channels) => {
|
||||
const copy = { ...channels };
|
||||
delete copy[action.payload.id];
|
||||
return copy;
|
||||
});
|
||||
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 setChannel = (channel: IChannel) => {
|
||||
setState("channel", "channels", channel.id, channel);
|
||||
};
|
||||
|
||||
const copy = { ...messages, ...newMessages };
|
||||
return copy;
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const deleteChannel = (channelId: string) => {
|
||||
setState("channel", "channels", channelId, undefined);
|
||||
};
|
||||
|
||||
export { channelReducer };
|
||||
const setActiveChannel = (channelId: string) => {
|
||||
setState("channel", "active", channelId);
|
||||
};
|
||||
|
||||
const resetActiveChannel = () => {
|
||||
setState("channel", "active", undefined);
|
||||
};
|
||||
|
||||
const setText = (channelId: string, text: string) => {
|
||||
setState("channel", "channels", channelId, "text", text);
|
||||
};
|
||||
|
||||
const setChannelMessage = (channelId: string, message: IMessage) => {
|
||||
setState("channel", "channels", channelId, "messages", message.id, message);
|
||||
};
|
||||
|
||||
const setChannelMessages = (channelId: string, addMessages: IMessage[]) => {
|
||||
setState("channel", "channels", channelId, "messages", (messages) => {
|
||||
const newMessages = Object.fromEntries(
|
||||
addMessages.map((item) => [item.id, item]),
|
||||
);
|
||||
|
||||
const copy = { ...messages, ...newMessages };
|
||||
return copy;
|
||||
});
|
||||
};
|
||||
|
||||
const deleteChannelMessage = (channelId: string, messageId: string) => {
|
||||
setState(
|
||||
"channel",
|
||||
"channels",
|
||||
channelId,
|
||||
"messages",
|
||||
messageId,
|
||||
undefined,
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
setChannel,
|
||||
deleteChannel,
|
||||
setActiveChannel,
|
||||
resetActiveChannel,
|
||||
setText,
|
||||
setChannelMessage,
|
||||
setChannelMessages,
|
||||
deleteChannelMessage,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,2 @@
|
|||
export * from "./channel";
|
||||
export * from "./actions";
|
||||
export * from "./types";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { IMessage } from "../message";
|
|||
|
||||
interface IChannelState {
|
||||
active?: string;
|
||||
channels: Record<string, IChannel>;
|
||||
channels: Record<string, IChannel | undefined>;
|
||||
}
|
||||
|
||||
interface IChannel {
|
||||
|
|
@ -12,7 +12,7 @@ interface IChannel {
|
|||
communityId?: string;
|
||||
creationDate?: number;
|
||||
text?: string;
|
||||
messages?: Record<string, IMessage>;
|
||||
messages?: Record<string, IMessage | undefined>;
|
||||
}
|
||||
|
||||
export { type IChannelState, type IChannel };
|
||||
|
|
|
|||
|
|
@ -1,104 +0,0 @@
|
|||
import {
|
||||
ICreateCommunityRequest,
|
||||
IUpdateCommunityRequest,
|
||||
IFetchCommunityResponse,
|
||||
ICreateCommunityResponse,
|
||||
IUpdateCommunityResponse,
|
||||
IRemoveCommunityResponse,
|
||||
IFetchCommunityChannelsResponse,
|
||||
IFetchCommunityRolesResponse,
|
||||
IFetchCommunityMembersResponse,
|
||||
IFetchCommunityInvitesResponse,
|
||||
} from "../../api/community";
|
||||
import { IFetchUserCommunity } from "../../api/user";
|
||||
|
||||
enum CommunityActionTypes {
|
||||
SET_COMMUNITY = "SET_COMMUNITY",
|
||||
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 =
|
||||
| {
|
||||
type: CommunityActionTypes.SET_COMMUNITY;
|
||||
payload: IFetchUserCommunity;
|
||||
}
|
||||
| {
|
||||
type: CommunityActionTypes.SET_ACTIVE_COMMUNITY;
|
||||
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;
|
||||
}
|
||||
| {
|
||||
type: CommunityActionTypes.FETCH_COMMUNITY_CHANNELS_FINISH;
|
||||
payload: IFetchCommunityChannelsResponse;
|
||||
}
|
||||
| {
|
||||
type: CommunityActionTypes.FETCH_COMMUNITY_ROLES_START;
|
||||
payload: string;
|
||||
}
|
||||
| {
|
||||
type: CommunityActionTypes.FETCH_COMMUNITY_ROLES_FINISH;
|
||||
payload: IFetchCommunityRolesResponse;
|
||||
}
|
||||
| {
|
||||
type: CommunityActionTypes.FETCH_COMMUNITY_MEMBERS_START;
|
||||
payload: string;
|
||||
}
|
||||
| {
|
||||
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 };
|
||||
|
|
@ -1,110 +1,81 @@
|
|||
import {
|
||||
fetchCommunity,
|
||||
createCommunity,
|
||||
updateCommunity,
|
||||
removeCommunity,
|
||||
fetchCommunityChannels,
|
||||
fetchCommunityRoles,
|
||||
fetchCommunityMembers,
|
||||
fetchCommunityInvites,
|
||||
} from "../../services/community";
|
||||
import { deriveKey } from "../../services/crypto";
|
||||
import { IChannel } from "../channel";
|
||||
import { IInvite } from "../invite";
|
||||
import { IRole } from "../role";
|
||||
import { setState } from "../state";
|
||||
import { CommunityActionTypes, CommunityAction } from "./actions";
|
||||
import { ICommunityState } from "./types";
|
||||
import { IUser } from "../user";
|
||||
import { ICommunity } from "./types";
|
||||
|
||||
function communityReducer(_state: ICommunityState, action: CommunityAction) {
|
||||
switch (action.type) {
|
||||
case CommunityActionTypes.SET_COMMUNITY:
|
||||
setState(
|
||||
"community",
|
||||
"communities",
|
||||
action.payload.id,
|
||||
action.payload,
|
||||
);
|
||||
break;
|
||||
case CommunityActionTypes.SET_ACTIVE_COMMUNITY:
|
||||
setState("community", "active", action.payload);
|
||||
break;
|
||||
case CommunityActionTypes.FETCH_COMMUNITY_START:
|
||||
fetchCommunity(action.payload);
|
||||
break;
|
||||
case CommunityActionTypes.FETCH_COMMUNITY_FINISH:
|
||||
setState(
|
||||
"community",
|
||||
"communities",
|
||||
action.payload.id,
|
||||
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;
|
||||
case CommunityActionTypes.FETCH_COMMUNITY_CHANNELS_FINISH:
|
||||
setState("community", "communities", action.payload.id, {
|
||||
channels: action.payload.channels.map((channel) => channel.id),
|
||||
});
|
||||
break;
|
||||
case CommunityActionTypes.FETCH_COMMUNITY_ROLES_START:
|
||||
fetchCommunityRoles(action.payload);
|
||||
break;
|
||||
case CommunityActionTypes.FETCH_COMMUNITY_ROLES_FINISH:
|
||||
setState("community", "communities", action.payload.id, {
|
||||
roles: action.payload.roles.map((role) => role.id),
|
||||
});
|
||||
break;
|
||||
case CommunityActionTypes.FETCH_COMMUNITY_MEMBERS_START:
|
||||
fetchCommunityMembers(action.payload);
|
||||
break;
|
||||
case CommunityActionTypes.FETCH_COMMUNITY_MEMBERS_FINISH:
|
||||
setState("community", "communities", action.payload.id, {
|
||||
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;
|
||||
}
|
||||
}
|
||||
const setCommunity = (community: ICommunity) => {
|
||||
setState("community", "communities", community.id, community);
|
||||
};
|
||||
|
||||
export { communityReducer };
|
||||
const deleteCommunity = (communityId: string) => {
|
||||
setState("community", "communities", communityId, undefined);
|
||||
};
|
||||
|
||||
const setActiveCommunity = (communityId: string) => {
|
||||
setState("community", "active", communityId);
|
||||
};
|
||||
|
||||
const resetActiveCommunity = () => {
|
||||
setState("community", "active", undefined);
|
||||
};
|
||||
|
||||
const setCommunityChannels = (communityId: string, channels: IChannel[]) => {
|
||||
setState("community", "communities", communityId, {
|
||||
channels: channels.map((channel) => channel.id),
|
||||
});
|
||||
};
|
||||
|
||||
const setCommunityRoles = (communityId: string, roles: IRole[]) => {
|
||||
setState("community", "communities", communityId, {
|
||||
roles: roles.map((role) => role.id),
|
||||
});
|
||||
};
|
||||
|
||||
const setCommunityMembers = (communityId: string, members: IUser[]) => {
|
||||
setState("community", "communities", communityId, {
|
||||
members: members.map((member) => member.id),
|
||||
});
|
||||
};
|
||||
|
||||
const setCommunityInvites = (communityId: string, invites: IInvite[]) => {
|
||||
setState("community", "communities", communityId, {
|
||||
invites: invites.map((invite) => invite.id),
|
||||
});
|
||||
};
|
||||
|
||||
const setCommunityEncryptionKey = (
|
||||
communityId: string,
|
||||
encryptionKey: string,
|
||||
) => {
|
||||
setState(
|
||||
"community",
|
||||
"communities",
|
||||
communityId,
|
||||
"encryptionKey",
|
||||
encryptionKey,
|
||||
);
|
||||
|
||||
deriveKey(encryptionKey).then((derivedKey) =>
|
||||
setCommunityDerivedKey(communityId, derivedKey),
|
||||
);
|
||||
};
|
||||
|
||||
const setCommunityDerivedKey = (communityId: string, derivedKey: CryptoKey) => {
|
||||
setState("community", "communities", communityId, "derivedKey", derivedKey);
|
||||
};
|
||||
|
||||
export {
|
||||
setCommunity,
|
||||
deleteCommunity,
|
||||
setActiveCommunity,
|
||||
resetActiveCommunity,
|
||||
setCommunityChannels,
|
||||
setCommunityRoles,
|
||||
setCommunityMembers,
|
||||
setCommunityInvites,
|
||||
setCommunityEncryptionKey,
|
||||
setCommunityDerivedKey,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,2 @@
|
|||
export * from "./community";
|
||||
export * from "./actions";
|
||||
export * from "./types";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
interface ICommunityState {
|
||||
active?: string;
|
||||
communities: Record<string, ICommunity>;
|
||||
communities: Record<string, ICommunity | undefined>;
|
||||
}
|
||||
|
||||
interface ICommunity {
|
||||
|
|
@ -13,6 +13,8 @@ interface ICommunity {
|
|||
roles?: string[];
|
||||
members?: string[];
|
||||
invites?: string[];
|
||||
encryptionKey?: string;
|
||||
derivedKey?: CryptoKey;
|
||||
}
|
||||
|
||||
export { type ICommunityState, type ICommunity };
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
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 };
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
export * from "./invite";
|
||||
export * from "./actions";
|
||||
export * from "./types";
|
||||
|
|
|
|||
|
|
@ -1,35 +1,12 @@
|
|||
import { fetchInvite, removeInvite, acceptInvite } from "../../services/invite";
|
||||
import { setState } from "../state";
|
||||
import { InviteActionTypes, InviteAction } from "./actions";
|
||||
import { IInviteState } from "./types";
|
||||
import { IInvite } 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;
|
||||
}
|
||||
}
|
||||
const setInvite = (invite: IInvite) => {
|
||||
setState("invite", "invites", invite.id, invite);
|
||||
};
|
||||
|
||||
export { inviteReducer };
|
||||
const deleteInvite = (inviteId: string) => {
|
||||
setState("invite", "invites", inviteId, undefined);
|
||||
};
|
||||
|
||||
export { setInvite, deleteInvite };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
interface IInviteState {
|
||||
invites: Record<string, IInvite>;
|
||||
invites: Record<string, IInvite | undefined>;
|
||||
}
|
||||
|
||||
interface IInvite {
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
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 };
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
export * from "./message";
|
||||
export * from "./actions";
|
||||
export * from "./types";
|
||||
|
|
|
|||
|
|
@ -1,59 +1,22 @@
|
|||
import {
|
||||
fetchMessage,
|
||||
createMessage,
|
||||
updateMessage,
|
||||
removeMessage,
|
||||
} from "../../services/message";
|
||||
import { setState } from "../state";
|
||||
import { MessageActionTypes, MessageAction } from "./actions";
|
||||
import { IMessageState } from "./types";
|
||||
import { IMessage } 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;
|
||||
const setMessage = (message: IMessage) => {
|
||||
setState("message", "message", message);
|
||||
if (message.channelId) {
|
||||
setState(
|
||||
"channel",
|
||||
"channels",
|
||||
message.channelId,
|
||||
"messages",
|
||||
message.id,
|
||||
message,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { messageReducer };
|
||||
const deleteMessage = () => {
|
||||
setState("message", "message", undefined);
|
||||
};
|
||||
|
||||
export { setMessage, deleteMessage };
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ interface IMessageState {
|
|||
interface IMessage {
|
||||
id: string;
|
||||
text: string;
|
||||
iv: string;
|
||||
editHistory?: string[];
|
||||
edited: boolean;
|
||||
ownerId: string;
|
||||
channelId?: string;
|
||||
creationDate: number;
|
||||
decryptionStatus?: boolean;
|
||||
}
|
||||
|
||||
export { type IMessageState, type IMessage };
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
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";
|
||||
import { MessageAction, messageReducer } from "./message";
|
||||
|
||||
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);
|
||||
messageReducer(state.message, action as MessageAction);
|
||||
}
|
||||
|
||||
export { reducer };
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import { IFetchCommunityRole } from "../../api/community";
|
||||
import {
|
||||
ICreateRoleRequest,
|
||||
IUpdateRoleRequest,
|
||||
IFetchRoleResponse,
|
||||
ICreateRoleResponse,
|
||||
IUpdateRoleResponse,
|
||||
IRemoveRoleResponse,
|
||||
} from "../../api/role";
|
||||
|
||||
enum RoleActionTypes {
|
||||
SET_ROLE = "SET_ROLE",
|
||||
FETCH_ROLE_START = "FETCH_ROLE_START",
|
||||
FETCH_ROLE_FINISH = "FETCH_ROLE_FINISH",
|
||||
CREATE_ROLE_START = "CREATE_ROLE_START",
|
||||
CREATE_ROLE_FINISH = "CREATE_ROLE_FINISH",
|
||||
UPDATE_ROLE_START = "UPDATE_ROLE_START",
|
||||
UPDATE_ROLE_FINISH = "UPDATE_ROLE_FINISH",
|
||||
REMOVE_ROLE_START = "REMOVE_ROLE_START",
|
||||
REMOVE_ROLE_FINISH = "REMOVE_ROLE_FINISH",
|
||||
}
|
||||
|
||||
type RoleAction =
|
||||
| {
|
||||
type: RoleActionTypes.SET_ROLE;
|
||||
payload: IFetchCommunityRole;
|
||||
}
|
||||
| { type: RoleActionTypes.FETCH_ROLE_START; payload: string }
|
||||
| {
|
||||
type: RoleActionTypes.FETCH_ROLE_FINISH;
|
||||
payload: IFetchRoleResponse;
|
||||
}
|
||||
| { type: RoleActionTypes.CREATE_ROLE_START; payload: ICreateRoleRequest }
|
||||
| {
|
||||
type: RoleActionTypes.CREATE_ROLE_FINISH;
|
||||
payload: ICreateRoleResponse;
|
||||
}
|
||||
| { type: RoleActionTypes.UPDATE_ROLE_START; payload: IUpdateRoleRequest }
|
||||
| {
|
||||
type: RoleActionTypes.UPDATE_ROLE_FINISH;
|
||||
payload: IUpdateRoleResponse;
|
||||
}
|
||||
| { type: RoleActionTypes.REMOVE_ROLE_START; payload: string }
|
||||
| {
|
||||
type: RoleActionTypes.REMOVE_ROLE_FINISH;
|
||||
payload: IRemoveRoleResponse;
|
||||
};
|
||||
|
||||
export { RoleActionTypes, type RoleAction };
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
export * from "./role";
|
||||
export * from "./actions";
|
||||
export * from "./types";
|
||||
|
|
|
|||
|
|
@ -1,47 +1,12 @@
|
|||
import {
|
||||
fetchRole,
|
||||
createRole,
|
||||
updateRole,
|
||||
removeRole,
|
||||
} from "../../services/role";
|
||||
import { setState } from "../state";
|
||||
import { RoleActionTypes, RoleAction } from "./actions";
|
||||
import { IRoleState } from "./types";
|
||||
import { IRole } from "./types";
|
||||
|
||||
function roleReducer(_state: IRoleState, action: RoleAction) {
|
||||
switch (action.type) {
|
||||
case RoleActionTypes.SET_ROLE:
|
||||
setState("role", "roles", action.payload.id, action.payload);
|
||||
break;
|
||||
case RoleActionTypes.FETCH_ROLE_START:
|
||||
fetchRole(action.payload);
|
||||
break;
|
||||
case RoleActionTypes.FETCH_ROLE_FINISH:
|
||||
setState("role", "roles", action.payload.id, action.payload);
|
||||
break;
|
||||
case RoleActionTypes.CREATE_ROLE_START:
|
||||
createRole(action.payload.name, action.payload.communityId);
|
||||
break;
|
||||
case RoleActionTypes.CREATE_ROLE_FINISH:
|
||||
setState("role", "roles", action.payload.id, action.payload);
|
||||
break;
|
||||
case RoleActionTypes.UPDATE_ROLE_START:
|
||||
updateRole(action.payload.id, action.payload.name);
|
||||
break;
|
||||
case RoleActionTypes.UPDATE_ROLE_FINISH:
|
||||
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", (roles) => {
|
||||
const copy = { ...roles };
|
||||
delete copy[action.payload.id];
|
||||
return copy;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
const setRole = (role: IRole) => {
|
||||
setState("role", "roles", role.id, role);
|
||||
};
|
||||
|
||||
export { roleReducer };
|
||||
const deleteRole = (roleId: string) => {
|
||||
setState("role", "roles", roleId, undefined);
|
||||
};
|
||||
|
||||
export { setRole, deleteRole };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
interface IRoleState {
|
||||
roles: Record<string, IRole>;
|
||||
roles: Record<string, IRole | undefined>;
|
||||
}
|
||||
|
||||
interface IRole {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue