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",
|
"name": "pulsar-web",
|
||||||
"version": "0.3.0",
|
"version": "0.5.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pulsar-web",
|
"name": "pulsar-web",
|
||||||
"version": "0.3.0",
|
"version": "0.5.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@solidjs/router": "^0.15.4",
|
"@solidjs/router": "^0.15.4",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "pulsar-web",
|
"name": "pulsar-web",
|
||||||
"version": "0.4.0",
|
"version": "0.5.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,29 @@
|
||||||
import { callApi, HTTP } from "../tools";
|
import { callApi, HTTP } from "../tools";
|
||||||
|
import { IResponseError } from "../types";
|
||||||
import {
|
import {
|
||||||
|
IFetchRegisterRequest,
|
||||||
|
IFetchRegisterResponse,
|
||||||
IFetchLoginRequest,
|
IFetchLoginRequest,
|
||||||
IFetchLoginResponse,
|
IFetchLoginResponse,
|
||||||
IFetchRefreshResponseError,
|
IFetchRefreshResponse,
|
||||||
IFetchRefreshResponseSuccess,
|
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
|
const fetchRegisterApi = async (
|
||||||
|
request: IFetchRegisterRequest,
|
||||||
|
): Promise<IFetchRegisterResponse | IResponseError> => {
|
||||||
|
return await callApi(HTTP.POST, `auth/register`, request, true);
|
||||||
|
};
|
||||||
|
|
||||||
const fetchLoginApi = async (
|
const fetchLoginApi = async (
|
||||||
request: IFetchLoginRequest,
|
request: IFetchLoginRequest,
|
||||||
): Promise<IFetchLoginResponse> => {
|
): Promise<IFetchLoginResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.POST, `auth/login`, request, true);
|
return await callApi(HTTP.POST, `auth/login`, request, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchRefreshApi = async (): Promise<
|
const fetchRefreshApi = async (): Promise<
|
||||||
IFetchRefreshResponseError | IFetchRefreshResponseSuccess
|
IFetchRefreshResponse | IResponseError
|
||||||
> => {
|
> => {
|
||||||
return await callApi(HTTP.GET, `auth/refresh`, undefined, true);
|
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 {
|
interface IFetchLoginRequest {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchLoginResponse {
|
interface IFetchLoginResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchRefreshResponseError {
|
interface IFetchRefreshResponse extends IResponseSuccess {
|
||||||
error: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IFetchRefreshResponseSuccess {
|
|
||||||
id: string;
|
id: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
token: string;
|
token: string;
|
||||||
|
storageSecret: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
type IFetchRegisterRequest,
|
||||||
|
type IFetchRegisterResponse,
|
||||||
type IFetchLoginRequest,
|
type IFetchLoginRequest,
|
||||||
type IFetchLoginResponse,
|
type IFetchLoginResponse,
|
||||||
type IFetchRefreshResponseError,
|
type IFetchRefreshResponse,
|
||||||
type IFetchRefreshResponseSuccess,
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { callApi, HTTP } from "../tools";
|
import { callApi, HTTP } from "../tools";
|
||||||
|
import { IResponseError } from "../types";
|
||||||
import {
|
import {
|
||||||
IFetchChannelRequest,
|
IFetchChannelRequest,
|
||||||
IFetchChannelResponse,
|
IFetchChannelResponse,
|
||||||
|
|
@ -14,31 +15,31 @@ import {
|
||||||
|
|
||||||
const fetchChannelApi = async (
|
const fetchChannelApi = async (
|
||||||
request: IFetchChannelRequest,
|
request: IFetchChannelRequest,
|
||||||
): Promise<IFetchChannelResponse> => {
|
): Promise<IFetchChannelResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `channel/${request.id}`);
|
return await callApi(HTTP.GET, `channel/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createChannelApi = async (
|
const createChannelApi = async (
|
||||||
request: ICreateChannelRequest,
|
request: ICreateChannelRequest,
|
||||||
): Promise<ICreateChannelResponse> => {
|
): Promise<ICreateChannelResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.POST, `channel`, request);
|
return await callApi(HTTP.POST, `channel`, request);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateChannelApi = async (
|
const updateChannelApi = async (
|
||||||
request: IUpdateChannelRequest,
|
request: IUpdateChannelRequest,
|
||||||
): Promise<IUpdateChannelResponse> => {
|
): Promise<IUpdateChannelResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.PATCH, `channel/${request.id}`, request);
|
return await callApi(HTTP.PATCH, `channel/${request.id}`, request);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeChannelApi = async (
|
const removeChannelApi = async (
|
||||||
request: IRemoveChannelRequest,
|
request: IRemoveChannelRequest,
|
||||||
): Promise<IRemoveChannelResponse> => {
|
): Promise<IRemoveChannelResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.DELETE, `channel/${request.id}`);
|
return await callApi(HTTP.DELETE, `channel/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchChannelMessagesApi = async (
|
const fetchChannelMessagesApi = async (
|
||||||
request: IFetchChannelMessagesRequest,
|
request: IFetchChannelMessagesRequest,
|
||||||
): Promise<IFetchChannelMessagesResponse> => {
|
): Promise<IFetchChannelMessagesResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `channel/${request.id}/messages`);
|
return await callApi(HTTP.GET, `channel/${request.id}/messages`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { IResponseSuccess } from "../types";
|
||||||
|
|
||||||
interface IFetchChannel {
|
interface IFetchChannel {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -10,14 +12,14 @@ interface IFetchChannelRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchChannelResponse extends IFetchChannel {}
|
interface IFetchChannelResponse extends IResponseSuccess, IFetchChannel {}
|
||||||
|
|
||||||
interface ICreateChannelRequest {
|
interface ICreateChannelRequest {
|
||||||
name: string;
|
name: string;
|
||||||
communityId: string;
|
communityId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ICreateChannelResponse extends IFetchChannel {}
|
interface ICreateChannelResponse extends IResponseSuccess, IFetchChannel {}
|
||||||
|
|
||||||
interface IUpdateChannelRequest {
|
interface IUpdateChannelRequest {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -25,13 +27,13 @@ interface IUpdateChannelRequest {
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IUpdateChannelResponse extends IFetchChannel {}
|
interface IUpdateChannelResponse extends IResponseSuccess, IFetchChannel {}
|
||||||
|
|
||||||
interface IRemoveChannelRequest {
|
interface IRemoveChannelRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRemoveChannelResponse {
|
interface IRemoveChannelResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
communityId: string;
|
communityId: string;
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +42,7 @@ interface IFetchChannelMessagesRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchChannelMessagesResponse {
|
interface IFetchChannelMessagesResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
messages: IFetchChannelMessage[];
|
messages: IFetchChannelMessage[];
|
||||||
}
|
}
|
||||||
|
|
@ -48,6 +50,7 @@ interface IFetchChannelMessagesResponse {
|
||||||
interface IFetchChannelMessage {
|
interface IFetchChannelMessage {
|
||||||
id: string;
|
id: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
iv: string;
|
||||||
edited: boolean;
|
edited: boolean;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
creationDate: number;
|
creationDate: number;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { callApi, HTTP } from "../tools";
|
import { callApi, HTTP } from "../tools";
|
||||||
|
import { IResponseError } from "../types";
|
||||||
import {
|
import {
|
||||||
IFetchCommunityRequest,
|
IFetchCommunityRequest,
|
||||||
IFetchCommunityResponse,
|
IFetchCommunityResponse,
|
||||||
|
|
@ -20,49 +21,49 @@ import {
|
||||||
|
|
||||||
const fetchCommunityApi = async (
|
const fetchCommunityApi = async (
|
||||||
request: IFetchCommunityRequest,
|
request: IFetchCommunityRequest,
|
||||||
): Promise<IFetchCommunityResponse> => {
|
): Promise<IFetchCommunityResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `community/${request.id}`);
|
return await callApi(HTTP.GET, `community/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCommunityApi = async (
|
const createCommunityApi = async (
|
||||||
request: ICreateCommunityRequest,
|
request: ICreateCommunityRequest,
|
||||||
): Promise<ICreateCommunityResponse> => {
|
): Promise<ICreateCommunityResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.POST, `community`, request);
|
return await callApi(HTTP.POST, `community`, request);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateCommunityApi = async (
|
const updateCommunityApi = async (
|
||||||
request: IUpdateCommunityRequest,
|
request: IUpdateCommunityRequest,
|
||||||
): Promise<IUpdateCommunityResponse> => {
|
): Promise<IUpdateCommunityResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.PATCH, `community/${request.id}`, request);
|
return await callApi(HTTP.PATCH, `community/${request.id}`, request);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeCommunityApi = async (
|
const removeCommunityApi = async (
|
||||||
request: IRemoveCommunityRequest,
|
request: IRemoveCommunityRequest,
|
||||||
): Promise<IRemoveCommunityResponse> => {
|
): Promise<IRemoveCommunityResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.DELETE, `community/${request.id}`);
|
return await callApi(HTTP.DELETE, `community/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchCommunityChannelsApi = async (
|
const fetchCommunityChannelsApi = async (
|
||||||
request: IFetchCommunityChannelsRequest,
|
request: IFetchCommunityChannelsRequest,
|
||||||
): Promise<IFetchCommunityChannelsResponse> => {
|
): Promise<IFetchCommunityChannelsResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `community/${request.id}/channels`);
|
return await callApi(HTTP.GET, `community/${request.id}/channels`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchCommunityRolesApi = async (
|
const fetchCommunityRolesApi = async (
|
||||||
request: IFetchCommunityRolesRequest,
|
request: IFetchCommunityRolesRequest,
|
||||||
): Promise<IFetchCommunityRolesResponse> => {
|
): Promise<IFetchCommunityRolesResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `community/${request.id}/roles`);
|
return await callApi(HTTP.GET, `community/${request.id}/roles`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchCommunityMembersApi = async (
|
const fetchCommunityMembersApi = async (
|
||||||
request: IFetchCommunityMembersRequest,
|
request: IFetchCommunityMembersRequest,
|
||||||
): Promise<IFetchCommunityMembersResponse> => {
|
): Promise<IFetchCommunityMembersResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `community/${request.id}/members`);
|
return await callApi(HTTP.GET, `community/${request.id}/members`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchCommunityInvitesApi = async (
|
const fetchCommunityInvitesApi = async (
|
||||||
request: IFetchCommunityInvitesRequest,
|
request: IFetchCommunityInvitesRequest,
|
||||||
): Promise<IFetchCommunityInvitesResponse> => {
|
): Promise<IFetchCommunityInvitesResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `community/${request.id}/invites`);
|
return await callApi(HTTP.GET, `community/${request.id}/invites`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { IResponseSuccess } from "../types";
|
||||||
|
|
||||||
interface IFetchCommunity {
|
interface IFetchCommunity {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -10,13 +12,13 @@ interface IFetchCommunityRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchCommunityResponse extends IFetchCommunity {}
|
interface IFetchCommunityResponse extends IResponseSuccess, IFetchCommunity {}
|
||||||
|
|
||||||
interface ICreateCommunityRequest {
|
interface ICreateCommunityRequest {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ICreateCommunityResponse extends IFetchCommunity {}
|
interface ICreateCommunityResponse extends IResponseSuccess, IFetchCommunity {}
|
||||||
|
|
||||||
interface IUpdateCommunityRequest {
|
interface IUpdateCommunityRequest {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -24,13 +26,13 @@ interface IUpdateCommunityRequest {
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IUpdateCommunityResponse extends IFetchCommunity {}
|
interface IUpdateCommunityResponse extends IResponseSuccess, IFetchCommunity {}
|
||||||
|
|
||||||
interface IRemoveCommunityRequest {
|
interface IRemoveCommunityRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRemoveCommunityResponse {
|
interface IRemoveCommunityResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +40,7 @@ interface IFetchCommunityChannelsRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchCommunityChannelsResponse {
|
interface IFetchCommunityChannelsResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
channels: IFetchCommunityChannel[];
|
channels: IFetchCommunityChannel[];
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +54,7 @@ interface IFetchCommunityRolesRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchCommunityRolesResponse {
|
interface IFetchCommunityRolesResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
roles: IFetchCommunityRole[];
|
roles: IFetchCommunityRole[];
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +68,7 @@ interface IFetchCommunityMembersRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchCommunityMembersResponse {
|
interface IFetchCommunityMembersResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
members: IFetchCommunityMember[];
|
members: IFetchCommunityMember[];
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +82,7 @@ interface IFetchCommunityInvitesRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchCommunityInvitesResponse {
|
interface IFetchCommunityInvitesResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
invites: IFetchCommunityInvite[];
|
invites: IFetchCommunityInvite[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { callApi, HTTP } from "../tools";
|
import { callApi, HTTP } from "../tools";
|
||||||
|
import { IResponseError } from "../types";
|
||||||
import {
|
import {
|
||||||
IFetchInviteRequest,
|
IFetchInviteRequest,
|
||||||
IFetchInviteResponse,
|
IFetchInviteResponse,
|
||||||
|
|
@ -10,19 +11,19 @@ import {
|
||||||
|
|
||||||
const fetchInviteApi = async (
|
const fetchInviteApi = async (
|
||||||
request: IFetchInviteRequest,
|
request: IFetchInviteRequest,
|
||||||
): Promise<IFetchInviteResponse> => {
|
): Promise<IFetchInviteResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `invite/${request.id}`);
|
return await callApi(HTTP.GET, `invite/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeInviteApi = async (
|
const removeInviteApi = async (
|
||||||
request: IRemoveInviteRequest,
|
request: IRemoveInviteRequest,
|
||||||
): Promise<IRemoveInviteResponse> => {
|
): Promise<IRemoveInviteResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.DELETE, `invite/${request.id}`);
|
return await callApi(HTTP.DELETE, `invite/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const acceptInviteApi = async (
|
const acceptInviteApi = async (
|
||||||
request: IAcceptInviteRequest,
|
request: IAcceptInviteRequest,
|
||||||
): Promise<IAcceptInviteResponse> => {
|
): Promise<IAcceptInviteResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `invite/${request.id}/accept`);
|
return await callApi(HTTP.GET, `invite/${request.id}/accept`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { IResponseSuccess } from "../types";
|
||||||
|
|
||||||
interface IFetchInvite {
|
interface IFetchInvite {
|
||||||
id: string;
|
id: string;
|
||||||
communityId: string;
|
communityId: string;
|
||||||
|
|
@ -14,13 +16,13 @@ interface IFetchInviteRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchInviteResponse extends IFetchInvite {}
|
interface IFetchInviteResponse extends IResponseSuccess, IFetchInvite {}
|
||||||
|
|
||||||
interface IRemoveInviteRequest {
|
interface IRemoveInviteRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRemoveInviteResponse {
|
interface IRemoveInviteResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +31,7 @@ interface IAcceptInviteRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IAcceptInviteResponse {
|
interface IAcceptInviteResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
userName: string;
|
userName: string;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { callApi, HTTP } from "../tools";
|
import { callApi, HTTP } from "../tools";
|
||||||
|
import { IResponseError } from "../types";
|
||||||
import {
|
import {
|
||||||
IFetchMessageRequest,
|
IFetchMessageRequest,
|
||||||
IFetchMessageResponse,
|
IFetchMessageResponse,
|
||||||
|
|
@ -12,25 +13,25 @@ import {
|
||||||
|
|
||||||
const fetchMessageApi = async (
|
const fetchMessageApi = async (
|
||||||
request: IFetchMessageRequest,
|
request: IFetchMessageRequest,
|
||||||
): Promise<IFetchMessageResponse> => {
|
): Promise<IFetchMessageResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `message/${request.id}`);
|
return await callApi(HTTP.GET, `message/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createMessageApi = async (
|
const createMessageApi = async (
|
||||||
request: ICreateMessageRequest,
|
request: ICreateMessageRequest,
|
||||||
): Promise<ICreateMessageResponse> => {
|
): Promise<ICreateMessageResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.POST, `message`, request);
|
return await callApi(HTTP.POST, `message`, request);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateMessageApi = async (
|
const updateMessageApi = async (
|
||||||
request: IUpdateMessageRequest,
|
request: IUpdateMessageRequest,
|
||||||
): Promise<IUpdateMessageResponse> => {
|
): Promise<IUpdateMessageResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.PATCH, `message/${request.id}`, request);
|
return await callApi(HTTP.PATCH, `message/${request.id}`, request);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeMessageApi = async (
|
const removeMessageApi = async (
|
||||||
request: IRemoveMessageRequest,
|
request: IRemoveMessageRequest,
|
||||||
): Promise<IRemoveMessageResponse> => {
|
): Promise<IRemoveMessageResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.DELETE, `message/${request.id}`);
|
return await callApi(HTTP.DELETE, `message/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
|
import { IResponseSuccess } from "../types";
|
||||||
|
|
||||||
interface IFetchMessage {
|
interface IFetchMessage {
|
||||||
id: string;
|
id: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
iv: string;
|
||||||
editHistory: string[];
|
editHistory: string[];
|
||||||
edited: boolean;
|
edited: boolean;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
|
|
@ -12,27 +15,28 @@ interface IFetchMessageRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchMessageResponse extends IFetchMessage {}
|
interface IFetchMessageResponse extends IResponseSuccess, IFetchMessage {}
|
||||||
|
|
||||||
interface ICreateMessageRequest {
|
interface ICreateMessageRequest {
|
||||||
text: string;
|
text: string;
|
||||||
|
iv: string;
|
||||||
channelId: string;
|
channelId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ICreateMessageResponse extends IFetchMessage {}
|
interface ICreateMessageResponse extends IResponseSuccess, IFetchMessage {}
|
||||||
|
|
||||||
interface IUpdateMessageRequest {
|
interface IUpdateMessageRequest {
|
||||||
id: string;
|
id: string;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IUpdateMessageResponse extends IFetchMessage {}
|
interface IUpdateMessageResponse extends IResponseSuccess, IFetchMessage {}
|
||||||
|
|
||||||
interface IRemoveMessageRequest {
|
interface IRemoveMessageRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRemoveMessageResponse {
|
interface IRemoveMessageResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
channelId: string;
|
channelId: string;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { callApi, HTTP } from "../tools";
|
import { callApi, HTTP } from "../tools";
|
||||||
|
import { IResponseError } from "../types";
|
||||||
import {
|
import {
|
||||||
IFetchRoleRequest,
|
IFetchRoleRequest,
|
||||||
IFetchRoleResponse,
|
IFetchRoleResponse,
|
||||||
|
|
@ -12,25 +13,25 @@ import {
|
||||||
|
|
||||||
const fetchRoleApi = async (
|
const fetchRoleApi = async (
|
||||||
request: IFetchRoleRequest,
|
request: IFetchRoleRequest,
|
||||||
): Promise<IFetchRoleResponse> => {
|
): Promise<IFetchRoleResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `role/${request.id}`);
|
return await callApi(HTTP.GET, `role/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createRoleApi = async (
|
const createRoleApi = async (
|
||||||
request: ICreateRoleRequest,
|
request: ICreateRoleRequest,
|
||||||
): Promise<ICreateRoleResponse> => {
|
): Promise<ICreateRoleResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.POST, `role`, request);
|
return await callApi(HTTP.POST, `role`, request);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateRoleApi = async (
|
const updateRoleApi = async (
|
||||||
request: IUpdateRoleRequest,
|
request: IUpdateRoleRequest,
|
||||||
): Promise<IUpdateRoleResponse> => {
|
): Promise<IUpdateRoleResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.PATCH, `role/${request.id}`, request);
|
return await callApi(HTTP.PATCH, `role/${request.id}`, request);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeRoleApi = async (
|
const removeRoleApi = async (
|
||||||
request: IRemoveRoleRequest,
|
request: IRemoveRoleRequest,
|
||||||
): Promise<IRemoveRoleResponse> => {
|
): Promise<IRemoveRoleResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.DELETE, `role/${request.id}`);
|
return await callApi(HTTP.DELETE, `role/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { IResponseSuccess } from "../types";
|
||||||
|
|
||||||
interface IFetchRole {
|
interface IFetchRole {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -11,27 +13,27 @@ interface IFetchRoleRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchRoleResponse extends IFetchRole {}
|
interface IFetchRoleResponse extends IResponseSuccess, IFetchRole {}
|
||||||
|
|
||||||
interface ICreateRoleRequest {
|
interface ICreateRoleRequest {
|
||||||
name: string;
|
name: string;
|
||||||
communityId: string;
|
communityId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ICreateRoleResponse extends IFetchRole {}
|
interface ICreateRoleResponse extends IResponseSuccess, IFetchRole {}
|
||||||
|
|
||||||
interface IUpdateRoleRequest {
|
interface IUpdateRoleRequest {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IUpdateRoleResponse extends IFetchRole {}
|
interface IUpdateRoleResponse extends IResponseSuccess, IFetchRole {}
|
||||||
|
|
||||||
interface IRemoveRoleRequest {
|
interface IRemoveRoleRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRemoveRoleResponse {
|
interface IRemoveRoleResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
communityId: string;
|
communityId: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { callApi, HTTP } from "../tools";
|
import { callApi, HTTP } from "../tools";
|
||||||
|
import { IResponseError } from "../types";
|
||||||
import {
|
import {
|
||||||
IFetchSessionRequest,
|
IFetchSessionRequest,
|
||||||
IFetchSessionResponse,
|
IFetchSessionResponse,
|
||||||
|
|
@ -8,13 +9,13 @@ import {
|
||||||
|
|
||||||
const fetchSessionApi = async (
|
const fetchSessionApi = async (
|
||||||
request: IFetchSessionRequest,
|
request: IFetchSessionRequest,
|
||||||
): Promise<IFetchSessionResponse> => {
|
): Promise<IFetchSessionResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `session/${request.id}`);
|
return await callApi(HTTP.GET, `session/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeSessionApi = async (
|
const removeSessionApi = async (
|
||||||
request: IRemoveSessionRequest,
|
request: IRemoveSessionRequest,
|
||||||
): Promise<IRemoveSessionResponse> => {
|
): Promise<IRemoveSessionResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.DELETE, `session/${request.id}`);
|
return await callApi(HTTP.DELETE, `session/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,25 @@
|
||||||
|
import { IResponseSuccess } from "../types";
|
||||||
|
|
||||||
interface IFetchSession {
|
interface IFetchSession {
|
||||||
id: string;
|
id: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
name: string;
|
||||||
|
userAgent: string;
|
||||||
creationDate: number;
|
creationDate: number;
|
||||||
|
refreshDate: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchSessionRequest {
|
interface IFetchSessionRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchSessionResponse extends IFetchSession {}
|
interface IFetchSessionResponse extends IResponseSuccess, IFetchSession {}
|
||||||
|
|
||||||
interface IRemoveSessionRequest {
|
interface IRemoveSessionRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRemoveSessionResponse {
|
interface IRemoveSessionResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
userId: 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 {
|
interface IFetchUser {
|
||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
|
|
@ -8,7 +10,7 @@ interface IFetchUser {
|
||||||
lastLogin: number;
|
lastLogin: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchLoggedUserResponse {
|
interface IFetchLoggedUserResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,13 +18,13 @@ interface IFetchUserRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchUserResponse extends IFetchUser {}
|
interface IFetchUserResponse extends IResponseSuccess, IFetchUser {}
|
||||||
|
|
||||||
interface IFetchUserSessionsRequest {
|
interface IFetchUserSessionsRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchUserSessionsResponse {
|
interface IFetchUserSessionsResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
sessions: IFetchUserSession[];
|
sessions: IFetchUserSession[];
|
||||||
}
|
}
|
||||||
|
|
@ -30,13 +32,17 @@ interface IFetchUserSessionsResponse {
|
||||||
interface IFetchUserSession {
|
interface IFetchUserSession {
|
||||||
id: string;
|
id: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
name: string;
|
||||||
|
userAgent: string;
|
||||||
|
creationDate: number;
|
||||||
|
refreshDate: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchUserCommunitiesRequest {
|
interface IFetchUserCommunitiesRequest {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFetchUserCommunitiesResponse {
|
interface IFetchUserCommunitiesResponse extends IResponseSuccess {
|
||||||
id: string;
|
id: string;
|
||||||
communities: IFetchUserCommunity[];
|
communities: IFetchUserCommunity[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { callApi, HTTP } from "../tools";
|
import { callApi, HTTP } from "../tools";
|
||||||
|
import { IResponseError } from "../types";
|
||||||
import {
|
import {
|
||||||
IFetchLoggedUserResponse,
|
IFetchLoggedUserResponse,
|
||||||
IFetchUserRequest,
|
IFetchUserRequest,
|
||||||
|
|
@ -9,25 +10,27 @@ import {
|
||||||
IFetchUserCommunitiesResponse,
|
IFetchUserCommunitiesResponse,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
const fetchLoggedUserApi = async (): Promise<IFetchLoggedUserResponse> => {
|
const fetchLoggedUserApi = async (): Promise<
|
||||||
|
IFetchLoggedUserResponse | IResponseError
|
||||||
|
> => {
|
||||||
return await callApi(HTTP.GET, `user/logged`);
|
return await callApi(HTTP.GET, `user/logged`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchUserApi = async (
|
const fetchUserApi = async (
|
||||||
request: IFetchUserRequest,
|
request: IFetchUserRequest,
|
||||||
): Promise<IFetchUserResponse> => {
|
): Promise<IFetchUserResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `user/${request.id}`);
|
return await callApi(HTTP.GET, `user/${request.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchUserSessionsApi = async (
|
const fetchUserSessionsApi = async (
|
||||||
request: IFetchUserSessionsRequest,
|
request: IFetchUserSessionsRequest,
|
||||||
): Promise<IFetchUserSessionsResponse> => {
|
): Promise<IFetchUserSessionsResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `user/${request.id}/sessions`);
|
return await callApi(HTTP.GET, `user/${request.id}/sessions`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchUserCommunitiesApi = async (
|
const fetchUserCommunitiesApi = async (
|
||||||
request: IFetchUserCommunitiesRequest,
|
request: IFetchUserCommunitiesRequest,
|
||||||
): Promise<IFetchUserCommunitiesResponse> => {
|
): Promise<IFetchUserCommunitiesResponse | IResponseError> => {
|
||||||
return await callApi(HTTP.GET, `user/${request.id}/communities`);
|
return await callApi(HTTP.GET, `user/${request.id}/communities`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ const Community: Component<ICommunityProps> = (props: ICommunityProps) => {
|
||||||
onClick={() => props.onCommunityClick?.(props.id)}
|
onClick={() => props.onCommunityClick?.(props.id)}
|
||||||
>
|
>
|
||||||
<div
|
<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} />
|
<img src={props.avatar} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,36 @@
|
||||||
import type { Component } from "solid-js";
|
import { type Component, type JSXElement } from "solid-js";
|
||||||
import { ICommunityBarProps } from "./types";
|
import { ICommunityBarProps } from "./types";
|
||||||
import { SettingsIcon } from "../icons";
|
import { SettingsIcon } from "../../icons";
|
||||||
|
import { state } from "../../store/state";
|
||||||
|
|
||||||
const CommunityBar: Component<ICommunityBarProps> = (
|
const CommunityBar: Component<ICommunityBarProps> = (
|
||||||
props: 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 (
|
return (
|
||||||
<div
|
<div
|
||||||
class={`absolute w-full top-0 z-10 bg-cover bg-top bg-no-repeat bg-[url('${props.avatar}')]`}
|
class={`absolute w-full top-0 z-10 bg-cover bg-top bg-no-repeat bg-[url('${props.avatar}')]`}
|
||||||
|
|
@ -14,12 +40,7 @@ const CommunityBar: Component<ICommunityBarProps> = (
|
||||||
<h2 class="text-sm font-bold">{props.name}</h2>
|
<h2 class="text-sm font-bold">{props.name}</h2>
|
||||||
<p class="text-xs">{props.description}</p>
|
<p class="text-xs">{props.description}</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
{settingsIconHtml()}
|
||||||
class="bg-stone-950/25 cursor-pointer rounded-full w-10 h-10 p-2 hover:bg-stone-950/75"
|
|
||||||
onClick={props.onSettingsClick}
|
|
||||||
>
|
|
||||||
<SettingsIcon />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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) => {
|
const HomeCard: Component<IHomeCardProps> = (props: IHomeCardProps) => {
|
||||||
return (
|
return (
|
||||||
<a class="w-60 cursor-pointer" onClick={props.onClick}>
|
<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="flex flex-col h-full gap-1 m-6">
|
||||||
<div class="w-20">
|
<div class="w-20">
|
||||||
<Dynamic component={props.icon} />
|
<Dynamic component={props.icon} />
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { JSXElement } from "solid-js";
|
import { JSXElement } from "solid-js";
|
||||||
import { IconParameters } from "../icons";
|
import { IconParameters } from "../../icons";
|
||||||
|
|
||||||
interface IHomeCardProps {
|
interface IHomeCardProps {
|
||||||
title: string;
|
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>
|
||||||
<div>
|
<div>
|
||||||
<div>{props.username}</div>
|
<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">{props.message}</p>
|
||||||
|
) : (
|
||||||
|
<p class="list-col-wrap text-xs italic">
|
||||||
|
Decryption failed
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ interface IMessageProps {
|
||||||
userId: string;
|
userId: string;
|
||||||
username: string;
|
username: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
|
decryptionStatus: boolean;
|
||||||
onProfileClick?: (userId: string) => void;
|
onProfileClick?: (userId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Component } from "solid-js";
|
import type { Component } from "solid-js";
|
||||||
import { IMessageBarProps } from "./types";
|
import { IMessageBarProps } from "./types";
|
||||||
|
import { UpIcon } from "../../icons";
|
||||||
|
|
||||||
const MessageBar: Component<IMessageBarProps> = (props: IMessageBarProps) => {
|
const MessageBar: Component<IMessageBarProps> = (props: IMessageBarProps) => {
|
||||||
const handleEnter = (e: KeyboardEvent) => {
|
const handleEnter = (e: KeyboardEvent) => {
|
||||||
|
|
@ -23,10 +24,12 @@ const MessageBar: Component<IMessageBarProps> = (props: IMessageBarProps) => {
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<button
|
<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}
|
onClick={props.onSend}
|
||||||
>
|
>
|
||||||
Send
|
<div class="w-5">
|
||||||
|
<UpIcon />
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</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> = (
|
const SidebarItem: Component<ISidebarItemProps> = (
|
||||||
props: ISidebarItemProps,
|
props: ISidebarItemProps,
|
||||||
) => {
|
) => {
|
||||||
|
const rotateCss = (): string => {
|
||||||
|
switch (props.hoverRotate) {
|
||||||
|
case 30:
|
||||||
|
return "hover:rotate-30";
|
||||||
|
case 45:
|
||||||
|
return "hover:rotate-45";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<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?.()}
|
onClick={() => props.onClick?.()}
|
||||||
>
|
>
|
||||||
<Dynamic component={props.icon} />
|
<Dynamic component={props.icon} />
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { JSXElement } from "solid-js";
|
import { JSXElement } from "solid-js";
|
||||||
import { IconParameters } from "../icons";
|
import { IconParameters } from "../../icons";
|
||||||
|
|
||||||
interface ISidebarItemProps {
|
interface ISidebarItemProps {
|
||||||
icon: (props: IconParameters) => JSXElement;
|
icon: (props: IconParameters) => JSXElement;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
hoverRotate?: 30 | 45;
|
||||||
onClick?: () => void;
|
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 {
|
||||||
import { AuthActionTypes } from "../../store/auth";
|
fetchRegisterApi,
|
||||||
import { dispatch } from "../../store/state";
|
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 fetchLogin = async (username: string, password: string) => {
|
||||||
const data = await fetchLoginApi({
|
const data = await fetchLoginApi({
|
||||||
|
|
@ -8,28 +38,42 @@ const fetchLogin = async (username: string, password: string) => {
|
||||||
password: password,
|
password: password,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: AuthActionTypes.FETCH_LOGIN_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
dispatch({
|
setAuthSession(data);
|
||||||
type: AuthActionTypes.FETCH_REFRESH_START,
|
|
||||||
});
|
fetchRefresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchRefresh = async () => {
|
const fetchRefresh = async () => {
|
||||||
const data = await fetchRefreshApi();
|
const data = await fetchRefreshApi();
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: AuthActionTypes.FETCH_REFRESH_FINISH,
|
setLoggedIn(false);
|
||||||
payload: data,
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch({
|
return;
|
||||||
type: AuthActionTypes.SET_LOGGED_IN,
|
}
|
||||||
payload: "id" in data,
|
|
||||||
});
|
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,
|
removeChannelApi,
|
||||||
fetchChannelMessagesApi,
|
fetchChannelMessagesApi,
|
||||||
} from "../../api/channel";
|
} from "../../api/channel";
|
||||||
import { ChannelActionTypes } from "../../store/channel";
|
import {
|
||||||
import { dispatch } from "../../store/state";
|
deleteChannel,
|
||||||
|
setChannel,
|
||||||
|
setChannelMessages,
|
||||||
|
} from "../../store/channel";
|
||||||
|
import { IMessage } from "../../store/message";
|
||||||
|
import { decryptMessage } from "../message";
|
||||||
|
|
||||||
const fetchChannel = async (id: string) => {
|
const fetchChannel = async (id: string) => {
|
||||||
const data = await fetchChannelApi({
|
const data = await fetchChannelApi({
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: ChannelActionTypes.FETCH_CHANNEL_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setChannel(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createChannel = async (name: string, communityId: string) => {
|
const createChannel = async (name: string, communityId: string) => {
|
||||||
|
|
@ -25,10 +31,11 @@ const createChannel = async (name: string, communityId: string) => {
|
||||||
communityId: communityId,
|
communityId: communityId,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: ChannelActionTypes.CREATE_CHANNEL_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setChannel(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateChannel = async (
|
const updateChannel = async (
|
||||||
|
|
@ -42,10 +49,11 @@ const updateChannel = async (
|
||||||
description: description,
|
description: description,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: ChannelActionTypes.UPDATE_CHANNEL_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setChannel(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeChannel = async (id: string) => {
|
const removeChannel = async (id: string) => {
|
||||||
|
|
@ -53,21 +61,52 @@ const removeChannel = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: ChannelActionTypes.REMOVE_CHANNEL_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
deleteChannel(data.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchChannelMessages = async (id: string) => {
|
const fetchChannelMessages = async (id: string, communityId: string) => {
|
||||||
const data = await fetchChannelMessagesApi({
|
const data = await fetchChannelMessagesApi({
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: ChannelActionTypes.FETCH_CHANNEL_MESSAGES_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
|
|
||||||
|
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 {
|
export {
|
||||||
|
|
|
||||||
|
|
@ -8,22 +8,33 @@ import {
|
||||||
fetchCommunityMembersApi,
|
fetchCommunityMembersApi,
|
||||||
fetchCommunityInvitesApi,
|
fetchCommunityInvitesApi,
|
||||||
} from "../../api/community";
|
} from "../../api/community";
|
||||||
import { CommunityActionTypes } from "../../store/community";
|
import { setChannel } from "../../store/channel";
|
||||||
import { ChannelActionTypes } from "../../store/channel";
|
import {
|
||||||
import { RoleActionTypes } from "../../store/role";
|
deleteCommunity,
|
||||||
import { UserActionTypes } from "../../store/user";
|
setCommunity,
|
||||||
import { dispatch, state } from "../../store/state";
|
setCommunityChannels,
|
||||||
import { InviteActionTypes } from "../../store/invite";
|
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 fetchCommunity = async (id: string) => {
|
||||||
const data = await fetchCommunityApi({
|
const data = await fetchCommunityApi({
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: CommunityActionTypes.FETCH_COMMUNITY_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setCommunity(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCommunity = async (name: string) => {
|
const createCommunity = async (name: string) => {
|
||||||
|
|
@ -31,16 +42,14 @@ const createCommunity = async (name: string) => {
|
||||||
name: name,
|
name: name,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: CommunityActionTypes.CREATE_COMMUNITY_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setCommunity(data);
|
||||||
|
|
||||||
if (state.user.loggedUserId) {
|
if (state.user.loggedUserId) {
|
||||||
dispatch({
|
fetchUserCommunities(state.user.loggedUserId);
|
||||||
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
|
|
||||||
payload: state.user.loggedUserId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -55,16 +64,14 @@ const updateCommunity = async (
|
||||||
description: description,
|
description: description,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: CommunityActionTypes.UPDATE_COMMUNITY_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setCommunity(data);
|
||||||
|
|
||||||
if (state.user.loggedUserId) {
|
if (state.user.loggedUserId) {
|
||||||
dispatch({
|
fetchUserCommunities(state.user.loggedUserId);
|
||||||
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
|
|
||||||
payload: state.user.loggedUserId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -73,16 +80,14 @@ const removeCommunity = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: CommunityActionTypes.REMOVE_COMMUNITY_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
deleteCommunity(data.id);
|
||||||
|
|
||||||
if (state.user.loggedUserId) {
|
if (state.user.loggedUserId) {
|
||||||
dispatch({
|
fetchUserCommunities(state.user.loggedUserId);
|
||||||
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
|
|
||||||
payload: state.user.loggedUserId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -91,16 +96,14 @@ const fetchCommunityChannels = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: CommunityActionTypes.FETCH_COMMUNITY_CHANNELS_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setCommunityChannels(data.id, data.channels);
|
||||||
|
|
||||||
data.channels.forEach((channel) => {
|
data.channels.forEach((channel) => {
|
||||||
dispatch({
|
setChannel(channel);
|
||||||
type: ChannelActionTypes.SET_CHANNEL,
|
|
||||||
payload: channel,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -109,16 +112,14 @@ const fetchCommunityRoles = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: CommunityActionTypes.FETCH_COMMUNITY_ROLES_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setCommunityRoles(data.id, data.roles);
|
||||||
|
|
||||||
data.roles.forEach((role) => {
|
data.roles.forEach((role) => {
|
||||||
dispatch({
|
setRole(role);
|
||||||
type: RoleActionTypes.SET_ROLE,
|
|
||||||
payload: role,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -127,16 +128,14 @@ const fetchCommunityMembers = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: CommunityActionTypes.FETCH_COMMUNITY_MEMBERS_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setCommunityMembers(data.id, data.members);
|
||||||
|
|
||||||
data.members.forEach((member) => {
|
data.members.forEach((member) => {
|
||||||
dispatch({
|
setUser(member);
|
||||||
type: UserActionTypes.SET_USER,
|
|
||||||
payload: member,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -145,15 +144,34 @@ const fetchCommunityInvites = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: CommunityActionTypes.FETCH_COMMUNITY_INVITES_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setCommunityInvites(data.id, data.invites);
|
||||||
|
|
||||||
data.invites.forEach((invite) => {
|
data.invites.forEach((invite) => {
|
||||||
dispatch({
|
setInvite(invite);
|
||||||
type: InviteActionTypes.SET_INVITE,
|
});
|
||||||
payload: 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,
|
fetchCommunityRoles,
|
||||||
fetchCommunityMembers,
|
fetchCommunityMembers,
|
||||||
fetchCommunityInvites,
|
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,
|
removeInviteApi,
|
||||||
acceptInviteApi,
|
acceptInviteApi,
|
||||||
} from "../../api/invite";
|
} from "../../api/invite";
|
||||||
import { InviteActionTypes } from "../../store/invite";
|
import { deleteInvite, setInvite } from "../../store/invite";
|
||||||
import { dispatch, state } from "../../store/state";
|
import { state } from "../../store/state";
|
||||||
import { UserActionTypes } from "../../store/user";
|
import { fetchUserCommunities } from "../user";
|
||||||
|
|
||||||
const fetchInvite = async (id: string) => {
|
const fetchInvite = async (id: string) => {
|
||||||
const data = await fetchInviteApi({
|
const data = await fetchInviteApi({
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: InviteActionTypes.FETCH_INVITE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setInvite(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeInvite = async (id: string) => {
|
const removeInvite = async (id: string) => {
|
||||||
|
|
@ -23,10 +24,11 @@ const removeInvite = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: InviteActionTypes.REMOVE_INVITE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
deleteInvite(data.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const acceptInvite = async (id: string) => {
|
const acceptInvite = async (id: string) => {
|
||||||
|
|
@ -34,16 +36,12 @@ const acceptInvite = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: InviteActionTypes.ACCEPT_INVITE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (state.user.loggedUserId) {
|
if (state.user.loggedUserId) {
|
||||||
dispatch({
|
fetchUserCommunities(state.user.loggedUserId);
|
||||||
type: UserActionTypes.FETCH_USER_COMMUNITIES_START,
|
|
||||||
payload: state.user.loggedUserId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,29 +4,62 @@ import {
|
||||||
updateMessageApi,
|
updateMessageApi,
|
||||||
removeMessageApi,
|
removeMessageApi,
|
||||||
} from "../../api/message";
|
} from "../../api/message";
|
||||||
import { MessageActionTypes } from "../../store/message";
|
import { deleteMessage, setMessage } from "../../store/message";
|
||||||
import { dispatch } from "../../store/state";
|
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({
|
const data = await fetchMessageApi({
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: MessageActionTypes.FETCH_MESSAGE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
|
|
||||||
|
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({
|
const data = await createMessageApi({
|
||||||
text: text,
|
|
||||||
channelId: channelId,
|
channelId: channelId,
|
||||||
|
iv: iv,
|
||||||
|
text: encryptedMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: MessageActionTypes.CREATE_MESSAGE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
|
|
||||||
|
setMessage({
|
||||||
|
...data,
|
||||||
|
text: text,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -36,10 +69,11 @@ const updateMessage = async (id: string, text: string) => {
|
||||||
text: text,
|
text: text,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: MessageActionTypes.UPDATE_MESSAGE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setMessage(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeMessage = async (id: string) => {
|
const removeMessage = async (id: string) => {
|
||||||
|
|
@ -47,10 +81,60 @@ const removeMessage = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: MessageActionTypes.REMOVE_MESSAGE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
|
|
||||||
|
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,
|
updateRoleApi,
|
||||||
removeRoleApi,
|
removeRoleApi,
|
||||||
} from "../../api/role";
|
} from "../../api/role";
|
||||||
import { RoleActionTypes } from "../../store/role";
|
import { deleteRole, setRole } from "../../store/role";
|
||||||
import { dispatch } from "../../store/state";
|
|
||||||
|
|
||||||
const fetchRole = async (id: string) => {
|
const fetchRole = async (id: string) => {
|
||||||
const data = await fetchRoleApi({
|
const data = await fetchRoleApi({
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: RoleActionTypes.FETCH_ROLE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setRole(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createRole = async (name: string, communityId: string) => {
|
const createRole = async (name: string, communityId: string) => {
|
||||||
|
|
@ -24,10 +24,11 @@ const createRole = async (name: string, communityId: string) => {
|
||||||
communityId: communityId,
|
communityId: communityId,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: RoleActionTypes.CREATE_ROLE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setRole(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateRole = async (id: string, name?: string) => {
|
const updateRole = async (id: string, name?: string) => {
|
||||||
|
|
@ -36,10 +37,11 @@ const updateRole = async (id: string, name?: string) => {
|
||||||
name: name,
|
name: name,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: RoleActionTypes.UPDATE_ROLE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setRole(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeRole = async (id: string) => {
|
const removeRole = async (id: string) => {
|
||||||
|
|
@ -47,10 +49,11 @@ const removeRole = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: RoleActionTypes.REMOVE_ROLE_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
deleteRole(data.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { fetchRole, createRole, updateRole, removeRole };
|
export { fetchRole, createRole, updateRole, removeRole };
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
import { fetchSessionApi, removeSessionApi } from "../../api/session";
|
import { fetchSessionApi, removeSessionApi } from "../../api/session";
|
||||||
import { SessionActionTypes } from "../../store/session";
|
import { deleteSession, setSession } from "../../store/session";
|
||||||
import { dispatch } from "../../store/state";
|
|
||||||
|
|
||||||
const fetchSession = async (id: string) => {
|
const fetchSession = async (id: string) => {
|
||||||
const data = await fetchSessionApi({
|
const data = await fetchSessionApi({
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: SessionActionTypes.FETCH_SESSION_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setSession(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeSession = async (id: string) => {
|
const removeSession = async (id: string) => {
|
||||||
|
|
@ -18,10 +18,11 @@ const removeSession = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: SessionActionTypes.REMOVE_SESSION_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
deleteSession(data.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { fetchSession, removeSession };
|
export { fetchSession, removeSession };
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,23 @@ import {
|
||||||
fetchUserSessionsApi,
|
fetchUserSessionsApi,
|
||||||
fetchUserCommunitiesApi,
|
fetchUserCommunitiesApi,
|
||||||
} from "../../api/user";
|
} from "../../api/user";
|
||||||
import { UserActionTypes } from "../../store/user";
|
import { setCommunity } from "../../store/community";
|
||||||
import { CommunityActionTypes } from "../../store/community";
|
import { setSession } from "../../store/session";
|
||||||
import { dispatch } from "../../store/state";
|
import {
|
||||||
import { SessionActionTypes } from "../../store/session";
|
setLoggedUserId,
|
||||||
|
setUser,
|
||||||
|
setUserCommunities,
|
||||||
|
setUserSessions,
|
||||||
|
} from "../../store/user";
|
||||||
|
|
||||||
const fetchLoggedUser = async () => {
|
const fetchLoggedUser = async () => {
|
||||||
const data = await fetchLoggedUserApi();
|
const data = await fetchLoggedUserApi();
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: UserActionTypes.FETCH_LOGGED_USER_ID_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setLoggedUserId(data.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchUser = async (id: string) => {
|
const fetchUser = async (id: string) => {
|
||||||
|
|
@ -23,10 +28,11 @@ const fetchUser = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: UserActionTypes.FETCH_USER_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setUser(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchUserSessions = async (id: string) => {
|
const fetchUserSessions = async (id: string) => {
|
||||||
|
|
@ -34,16 +40,14 @@ const fetchUserSessions = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: UserActionTypes.FETCH_USER_SESSIONS_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setUserSessions(data);
|
||||||
|
|
||||||
data.sessions.forEach((session) => {
|
data.sessions.forEach((session) => {
|
||||||
dispatch({
|
setSession(session);
|
||||||
type: SessionActionTypes.SET_SESSION,
|
|
||||||
payload: session,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -52,16 +56,14 @@ const fetchUserCommunities = async (id: string) => {
|
||||||
id: id,
|
id: id,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
if (typeof data.error === "string") {
|
||||||
type: UserActionTypes.FETCH_USER_COMMUNITIES_FINISH,
|
return;
|
||||||
payload: data,
|
}
|
||||||
});
|
|
||||||
|
setUserCommunities(data);
|
||||||
|
|
||||||
data.communities.forEach((community) => {
|
data.communities.forEach((community) => {
|
||||||
dispatch({
|
setCommunity(community);
|
||||||
type: CommunityActionTypes.SET_COMMUNITY,
|
|
||||||
payload: community,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,12 @@ enum SocketRequestTypes {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SocketMessageTypes {
|
enum SocketMessageTypes {
|
||||||
NEW_ANNOUNCEMENT = "NEW_ANNOUNCEMENT",
|
ANNOUNCEMENT = "ANNOUNCEMENT",
|
||||||
NEW_MESSAGE = "NEW_MESSAGE",
|
SET_MESSAGE = "SET_MESSAGE",
|
||||||
NEW_CHANNEL = "NEW_CHANNEL",
|
DELETE_MESSAGE = "DELETE_MESSAGE",
|
||||||
|
UPDATE_CHANNELS = "UPDATE_CHANNELS",
|
||||||
|
UPDATE_ROLES = "UPDATE_ROLES",
|
||||||
|
UPDATE_MEMBERS = "UPDATE_MEMBERS",
|
||||||
}
|
}
|
||||||
|
|
||||||
type SocketRequest = {
|
type SocketRequest = {
|
||||||
|
|
@ -16,25 +19,42 @@ type SocketRequest = {
|
||||||
|
|
||||||
type SocketMessage =
|
type SocketMessage =
|
||||||
| {
|
| {
|
||||||
type: SocketMessageTypes.NEW_ANNOUNCEMENT;
|
type: SocketMessageTypes.ANNOUNCEMENT;
|
||||||
payload: {
|
payload: {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: SocketMessageTypes.NEW_MESSAGE;
|
type: SocketMessageTypes.SET_MESSAGE;
|
||||||
payload: {
|
payload: {
|
||||||
channelId: string;
|
channelId: string;
|
||||||
message: IFetchChannelMessage;
|
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: {
|
payload: {
|
||||||
id: string;
|
|
||||||
communityId: string;
|
communityId: string;
|
||||||
name: string;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
import { ChannelActionTypes } from "../../store/channel";
|
import { deleteChannelMessage, setChannelMessage } from "../../store/channel";
|
||||||
import { dispatch } from "../../store/state";
|
import { state } from "../../store/state";
|
||||||
|
import {
|
||||||
|
fetchCommunityChannels,
|
||||||
|
fetchCommunityMembers,
|
||||||
|
fetchCommunityRoles,
|
||||||
|
} from "../community";
|
||||||
|
import { decryptMessage } from "../message";
|
||||||
import config from "./config.json";
|
import config from "./config.json";
|
||||||
import { SocketMessage, SocketMessageTypes } from "./types";
|
import { SocketMessage, SocketMessageTypes } from "./types";
|
||||||
|
|
||||||
|
|
@ -38,18 +44,62 @@ const parseMessage = (data: string): SocketMessage | null => {
|
||||||
|
|
||||||
const handleMessage = (message: SocketMessage) => {
|
const handleMessage = (message: SocketMessage) => {
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case SocketMessageTypes.NEW_MESSAGE: {
|
case SocketMessageTypes.SET_MESSAGE: {
|
||||||
dispatch({
|
try {
|
||||||
type: ChannelActionTypes.SET_CHANNEL_MESSAGE,
|
const communityId =
|
||||||
payload: {
|
state.channel.channels[message.payload.channelId]
|
||||||
id: message.payload.channelId,
|
?.communityId;
|
||||||
message: message.payload.message,
|
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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
case SocketMessageTypes.NEW_CHANNEL: {
|
});
|
||||||
|
} catch {
|
||||||
|
setChannelMessage(message.payload.channelId, {
|
||||||
|
...message.payload.message,
|
||||||
|
decryptionStatus: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
case SocketMessageTypes.NEW_ANNOUNCEMENT: {
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SocketMessageTypes.DELETE_MESSAGE: {
|
||||||
|
deleteChannelMessage(
|
||||||
|
message.payload.channelId,
|
||||||
|
message.payload.messageId,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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 { setState } from "../state";
|
||||||
import { AppActionTypes, AppAction } from "./actions";
|
|
||||||
import { IAppState } from "./types";
|
|
||||||
|
|
||||||
function appReducer(_state: IAppState, action: AppAction) {
|
const setHomeOpen = (value: boolean) => {
|
||||||
switch (action.type) {
|
setState("app", "homeOpen", value);
|
||||||
case AppActionTypes.SET_HOME_OPEN:
|
};
|
||||||
setState("app", "homeOpen", action.payload);
|
|
||||||
break;
|
const setSettingsOpen = (value: boolean) => {
|
||||||
case AppActionTypes.SET_SETTINGS_OPEN:
|
|
||||||
setState("app", "dialogsOpen", "settingsOpen", false);
|
setState("app", "dialogsOpen", "settingsOpen", false);
|
||||||
setState("app", "dialogsOpen", "settingsOpen", action.payload);
|
setState("app", "dialogsOpen", "settingsOpen", value);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { appReducer };
|
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 "./app";
|
||||||
export * from "./actions";
|
|
||||||
export * from "./types";
|
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 { setState } from "../state";
|
||||||
import { AuthActionTypes, AuthAction } from "./actions";
|
import { IAuthSession } from "./types";
|
||||||
import { IAuthState } from "./types";
|
|
||||||
|
|
||||||
function authReducer(_state: IAuthState, action: AuthAction) {
|
const setRegisterSuccess = (value: boolean) => {
|
||||||
switch (action.type) {
|
setState("auth", "registerSuccess", value);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 "./auth";
|
||||||
export * from "./actions";
|
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
interface IAuthState {
|
interface IAuthState {
|
||||||
|
registerSuccess?: boolean;
|
||||||
loggedIn?: boolean;
|
loggedIn?: boolean;
|
||||||
session?: ISession;
|
session?: IAuthSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ISession {
|
interface IAuthSession {
|
||||||
id?: string;
|
id?: string;
|
||||||
ownerId?: string;
|
ownerId?: string;
|
||||||
token?: 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 {
|
import { IMessage } from "../message";
|
||||||
fetchChannel,
|
|
||||||
createChannel,
|
|
||||||
updateChannel,
|
|
||||||
removeChannel,
|
|
||||||
fetchChannelMessages,
|
|
||||||
} from "../../services/channel";
|
|
||||||
import { setState } from "../state";
|
import { setState } from "../state";
|
||||||
import { ChannelActionTypes, ChannelAction } from "./actions";
|
import { IChannel } from "./types";
|
||||||
import { IChannelState } from "./types";
|
|
||||||
|
|
||||||
function channelReducer(_state: IChannelState, action: ChannelAction) {
|
const setChannel = (channel: IChannel) => {
|
||||||
switch (action.type) {
|
setState("channel", "channels", channel.id, channel);
|
||||||
case ChannelActionTypes.SET_CHANNEL:
|
};
|
||||||
setState("channel", "channels", action.payload.id, action.payload);
|
|
||||||
break;
|
const deleteChannel = (channelId: string) => {
|
||||||
case ChannelActionTypes.SET_ACTIVE_CHANNEL:
|
setState("channel", "channels", channelId, undefined);
|
||||||
setState("channel", "active", action.payload);
|
};
|
||||||
break;
|
|
||||||
case ChannelActionTypes.SET_TEXT:
|
const setActiveChannel = (channelId: string) => {
|
||||||
setState(
|
setState("channel", "active", channelId);
|
||||||
"channel",
|
};
|
||||||
"channels",
|
|
||||||
action.payload.id,
|
const resetActiveChannel = () => {
|
||||||
"text",
|
setState("channel", "active", undefined);
|
||||||
action.payload.text,
|
};
|
||||||
);
|
|
||||||
break;
|
const setText = (channelId: string, text: string) => {
|
||||||
case ChannelActionTypes.SET_CHANNEL_MESSAGE:
|
setState("channel", "channels", channelId, "text", text);
|
||||||
setState(
|
};
|
||||||
"channel",
|
|
||||||
"channels",
|
const setChannelMessage = (channelId: string, message: IMessage) => {
|
||||||
action.payload.id,
|
setState("channel", "channels", channelId, "messages", message.id, message);
|
||||||
"messages",
|
};
|
||||||
action.payload.message.id,
|
|
||||||
action.payload.message,
|
const setChannelMessages = (channelId: string, addMessages: IMessage[]) => {
|
||||||
);
|
setState("channel", "channels", channelId, "messages", (messages) => {
|
||||||
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(
|
const newMessages = Object.fromEntries(
|
||||||
action.payload.messages.map((item) => [item.id, item]),
|
addMessages.map((item) => [item.id, item]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const copy = { ...messages, ...newMessages };
|
const copy = { ...messages, ...newMessages };
|
||||||
return copy;
|
return copy;
|
||||||
},
|
});
|
||||||
);
|
};
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { channelReducer };
|
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 "./channel";
|
||||||
export * from "./actions";
|
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { IMessage } from "../message";
|
||||||
|
|
||||||
interface IChannelState {
|
interface IChannelState {
|
||||||
active?: string;
|
active?: string;
|
||||||
channels: Record<string, IChannel>;
|
channels: Record<string, IChannel | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IChannel {
|
interface IChannel {
|
||||||
|
|
@ -12,7 +12,7 @@ interface IChannel {
|
||||||
communityId?: string;
|
communityId?: string;
|
||||||
creationDate?: number;
|
creationDate?: number;
|
||||||
text?: string;
|
text?: string;
|
||||||
messages?: Record<string, IMessage>;
|
messages?: Record<string, IMessage | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { type IChannelState, type IChannel };
|
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 {
|
import { deriveKey } from "../../services/crypto";
|
||||||
fetchCommunity,
|
import { IChannel } from "../channel";
|
||||||
createCommunity,
|
import { IInvite } from "../invite";
|
||||||
updateCommunity,
|
import { IRole } from "../role";
|
||||||
removeCommunity,
|
|
||||||
fetchCommunityChannels,
|
|
||||||
fetchCommunityRoles,
|
|
||||||
fetchCommunityMembers,
|
|
||||||
fetchCommunityInvites,
|
|
||||||
} from "../../services/community";
|
|
||||||
import { setState } from "../state";
|
import { setState } from "../state";
|
||||||
import { CommunityActionTypes, CommunityAction } from "./actions";
|
import { IUser } from "../user";
|
||||||
import { ICommunityState } from "./types";
|
import { ICommunity } from "./types";
|
||||||
|
|
||||||
function communityReducer(_state: ICommunityState, action: CommunityAction) {
|
const setCommunity = (community: ICommunity) => {
|
||||||
switch (action.type) {
|
setState("community", "communities", community.id, community);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 "./community";
|
||||||
export * from "./actions";
|
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
interface ICommunityState {
|
interface ICommunityState {
|
||||||
active?: string;
|
active?: string;
|
||||||
communities: Record<string, ICommunity>;
|
communities: Record<string, ICommunity | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ICommunity {
|
interface ICommunity {
|
||||||
|
|
@ -13,6 +13,8 @@ interface ICommunity {
|
||||||
roles?: string[];
|
roles?: string[];
|
||||||
members?: string[];
|
members?: string[];
|
||||||
invites?: string[];
|
invites?: string[];
|
||||||
|
encryptionKey?: string;
|
||||||
|
derivedKey?: CryptoKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { type ICommunityState, type ICommunity };
|
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 "./invite";
|
||||||
export * from "./actions";
|
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,12 @@
|
||||||
import { fetchInvite, removeInvite, acceptInvite } from "../../services/invite";
|
|
||||||
import { setState } from "../state";
|
import { setState } from "../state";
|
||||||
import { InviteActionTypes, InviteAction } from "./actions";
|
import { IInvite } from "./types";
|
||||||
import { IInviteState } from "./types";
|
|
||||||
|
|
||||||
function inviteReducer(_state: IInviteState, action: InviteAction) {
|
const setInvite = (invite: IInvite) => {
|
||||||
switch (action.type) {
|
setState("invite", "invites", invite.id, invite);
|
||||||
case InviteActionTypes.SET_INVITE:
|
};
|
||||||
setState("invite", "invites", action.payload.id, action.payload);
|
|
||||||
break;
|
|
||||||
case InviteActionTypes.FETCH_INVITE_START:
|
|
||||||
fetchInvite(action.payload);
|
|
||||||
break;
|
|
||||||
case InviteActionTypes.FETCH_INVITE_FINISH:
|
|
||||||
setState("invite", "invites", action.payload.id, action.payload);
|
|
||||||
break;
|
|
||||||
case InviteActionTypes.REMOVE_INVITE_START:
|
|
||||||
removeInvite(action.payload);
|
|
||||||
break;
|
|
||||||
case InviteActionTypes.REMOVE_INVITE_FINISH:
|
|
||||||
setState("invite", "invites", (invites) => {
|
|
||||||
const copy = { ...invites };
|
|
||||||
delete copy[action.payload.id];
|
|
||||||
return copy;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case InviteActionTypes.ACCEPT_INVITE_START:
|
|
||||||
acceptInvite(action.payload);
|
|
||||||
break;
|
|
||||||
case InviteActionTypes.ACCEPT_INVITE_FINISH:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { inviteReducer };
|
const deleteInvite = (inviteId: string) => {
|
||||||
|
setState("invite", "invites", inviteId, undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { setInvite, deleteInvite };
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
interface IInviteState {
|
interface IInviteState {
|
||||||
invites: Record<string, IInvite>;
|
invites: Record<string, IInvite | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IInvite {
|
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 "./message";
|
||||||
export * from "./actions";
|
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
|
|
|
||||||
|
|
@ -1,59 +1,22 @@
|
||||||
import {
|
|
||||||
fetchMessage,
|
|
||||||
createMessage,
|
|
||||||
updateMessage,
|
|
||||||
removeMessage,
|
|
||||||
} from "../../services/message";
|
|
||||||
import { setState } from "../state";
|
import { setState } from "../state";
|
||||||
import { MessageActionTypes, MessageAction } from "./actions";
|
import { IMessage } from "./types";
|
||||||
import { IMessageState } from "./types";
|
|
||||||
|
|
||||||
function messageReducer(_state: IMessageState, action: MessageAction) {
|
const setMessage = (message: IMessage) => {
|
||||||
switch (action.type) {
|
setState("message", "message", message);
|
||||||
case MessageActionTypes.FETCH_MESSAGE_START:
|
if (message.channelId) {
|
||||||
fetchMessage(action.payload);
|
|
||||||
break;
|
|
||||||
case MessageActionTypes.FETCH_MESSAGE_FINISH:
|
|
||||||
setState("message", "message", action.payload);
|
|
||||||
setState(
|
setState(
|
||||||
"channel",
|
"channel",
|
||||||
"channels",
|
"channels",
|
||||||
action.payload.channelId,
|
message.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",
|
"messages",
|
||||||
action.payload.id,
|
message.id,
|
||||||
action.payload,
|
message,
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
case MessageActionTypes.UPDATE_MESSAGE_START:
|
|
||||||
updateMessage(action.payload.id, action.payload.text);
|
|
||||||
break;
|
|
||||||
case MessageActionTypes.UPDATE_MESSAGE_FINISH:
|
|
||||||
setState("message", "message", action.payload);
|
|
||||||
setState(
|
|
||||||
"channel",
|
|
||||||
"channels",
|
|
||||||
action.payload.channelId,
|
|
||||||
action.payload,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case MessageActionTypes.REMOVE_MESSAGE_START:
|
|
||||||
removeMessage(action.payload);
|
|
||||||
break;
|
|
||||||
case MessageActionTypes.REMOVE_MESSAGE_FINISH:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export { messageReducer };
|
const deleteMessage = () => {
|
||||||
|
setState("message", "message", undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { setMessage, deleteMessage };
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@ interface IMessageState {
|
||||||
interface IMessage {
|
interface IMessage {
|
||||||
id: string;
|
id: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
iv: string;
|
||||||
editHistory?: string[];
|
editHistory?: string[];
|
||||||
edited: boolean;
|
edited: boolean;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
channelId?: string;
|
channelId?: string;
|
||||||
creationDate: number;
|
creationDate: number;
|
||||||
|
decryptionStatus?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { type IMessageState, type IMessage };
|
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 "./role";
|
||||||
export * from "./actions";
|
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,12 @@
|
||||||
import {
|
|
||||||
fetchRole,
|
|
||||||
createRole,
|
|
||||||
updateRole,
|
|
||||||
removeRole,
|
|
||||||
} from "../../services/role";
|
|
||||||
import { setState } from "../state";
|
import { setState } from "../state";
|
||||||
import { RoleActionTypes, RoleAction } from "./actions";
|
import { IRole } from "./types";
|
||||||
import { IRoleState } from "./types";
|
|
||||||
|
|
||||||
function roleReducer(_state: IRoleState, action: RoleAction) {
|
const setRole = (role: IRole) => {
|
||||||
switch (action.type) {
|
setState("role", "roles", role.id, role);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { roleReducer };
|
const deleteRole = (roleId: string) => {
|
||||||
|
setState("role", "roles", roleId, undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { setRole, deleteRole };
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
interface IRoleState {
|
interface IRoleState {
|
||||||
roles: Record<string, IRole>;
|
roles: Record<string, IRole | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRole {
|
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