Skip to content

Commit

Permalink
Integrate Redux into front end
Browse files Browse the repository at this point in the history
  • Loading branch information
hpeebles committed Jan 5, 2021
1 parent 18abaa8 commit 7b06e0a
Show file tree
Hide file tree
Showing 28 changed files with 1,029 additions and 24 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
},
"dependencies": {
"@dfinity/agent": "0.6.14",
"@reduxjs/toolkit": "1.5.0",
"@types/react": "17.0.0",
"@types/react-dom": "17.0.0",
"@types/react-redux": "^7.1.15",
"css-loader": "5.0.1",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-redux": "^7.2.2",
"redux-devtools-extension": "^2.13.8",
"redux-thunk": "^2.3.0",
"terser-webpack-plugin": "2.2.2",
"ts-loader": "8.0.12",
"typescript": "4.1.3",
Expand Down
29 changes: 18 additions & 11 deletions src/open_chat_assets/public/App.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import React, { Component } from "react";
import React from "react";

import { setupBackgroundTasks } from "./backgroundTasks";

import Main from "./components/Main";
import Side from "./components/Side";

const App = () => {
setupBackgroundTasks();

class App extends Component {
render() {
return (
<div>
<div>
Hello!
<div style={{ display:"flex", width:"100%", height:"100%" }}>
<div style={{ flex:"40%" }}>
<Side />
</div>
<div style={{ flex:"60%" }}>
<Main />
</div>
</div>
</div>
);
}
}
)
};

export default App;

45 changes: 45 additions & 0 deletions src/open_chat_assets/public/actions/chats/getAllChats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import chatService from "../../services/chats/service";
import { Chat } from "../../model/chats";

export const GET_ALL_CHATS_REQUESTED = "GET_ALL_CHATS_REQUESTED";
export const GET_ALL_CHATS_SUCCEEDED = "GET_ALL_CHATS_SUCCEEDED";
export const GET_ALL_CHATS_FAILED = "GET_ALL_CHATS_FAILED";

export default function() {
return async (dispatch: any) => {
const requestEvent: GetAllChatsRequestedEvent = {
type: GET_ALL_CHATS_REQUESTED
};

dispatch(requestEvent);

const result = await chatService.listChats(false);

let outcomeEvent;
if (result.kind === "success") {
outcomeEvent = {
type: GET_ALL_CHATS_SUCCEEDED,
payload: result.chats
} as GetAllChatsSucceededEvent;
} else {
outcomeEvent = {
type: GET_ALL_CHATS_FAILED
} as GetAllChatsFailedEvent;
}

dispatch(outcomeEvent);
}
}

export type GetAllChatsRequestedEvent = {
type: typeof GET_ALL_CHATS_REQUESTED
}

export type GetAllChatsSucceededEvent = {
type: typeof GET_ALL_CHATS_SUCCEEDED,
payload: Chat[]
}

export type GetAllChatsFailedEvent = {
type: typeof GET_ALL_CHATS_FAILED
}
13 changes: 13 additions & 0 deletions src/open_chat_assets/public/actions/chats/selectChat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const CHAT_SELECTED = "CHAT_SELECTED";

export default function(index: number) {
return {
type: CHAT_SELECTED,
payload: index
};
}

export type ChatSelectedEvent = {
type: typeof CHAT_SELECTED,
payload: number
}
144 changes: 144 additions & 0 deletions src/open_chat_assets/public/actions/chats/sendMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import chatService from "../../services/chats/service";
import { ChatId } from "../../model/chats";
import { Option, Timestamp } from "../../model/common";
import { UserId } from "../../model/users";

export const SEND_MESSAGE_REQUESTED = "SEND_MESSAGE_REQUESTED";
export const SEND_MESSAGE_SUCCEEDED = "SEND_MESSAGE_SUCCEEDED";
export const SEND_MESSAGE_FAILED = "SEND_MESSAGE_FAILED";

export default function(userId: UserId, chatId: Option<ChatId>, message: string) {
return async (dispatch: any) => {
const id = Symbol("id");

const requestEvent: SendMessageRequestedEvent = {
type: SEND_MESSAGE_REQUESTED,
payload: {
kind: "direct",
userId: userId,
chatId: chatId,
message: message,
unconfirmedMessageId: id
}
};

dispatch(requestEvent);

const response = chatId
? await chatService.sendMessage(chatId, message)
: await chatService.sendDirectMessage(userId, message);

let outcomeEvent;
if (response.kind === "success") {
outcomeEvent = {
type: SEND_MESSAGE_SUCCEEDED,
payload: {
kind: "direct",
userId: userId,
chatId: chatId,
message: message,
unconfirmedMessageId: id,
confirmedMessageId: response.result.messageId,
confirmedMessageTimestamp: response.result.timestamp
}
} as SendMessageSucceededEvent;
} else {
outcomeEvent = {
type: SEND_MESSAGE_FAILED
} as SendMessageFailedEvent;
}

dispatch(outcomeEvent);
}
}

export const sendGroupMessage = (chatId: ChatId, message: string) => async (dispatch: any) => {
const id = Symbol("id");

const requestEvent: SendMessageRequestedEvent = {
type: SEND_MESSAGE_REQUESTED,
payload: {
kind: "group",
chatId: chatId,
message: message,
unconfirmedMessageId: id
}
};

dispatch(requestEvent);

const response = await chatService.sendMessage(chatId, message);

let outcomeEvent;
if (response.kind === "success") {
outcomeEvent = {
type: SEND_MESSAGE_SUCCEEDED,
payload: {
kind: "group",
chatId: chatId,
message: message,
unconfirmedMessageId: id,
confirmedMessageId: response.result.messageId,
confirmedMessageTimestamp: response.result.timestamp
}
} as SendMessageSucceededEvent;
} else {
outcomeEvent = {
type: SEND_MESSAGE_FAILED
} as SendMessageFailedEvent;
}

dispatch(outcomeEvent);
}

export type SendMessageRequestedEvent = {
type: typeof SEND_MESSAGE_REQUESTED,
payload: SendMessageRequest
}

export type SendMessageSucceededEvent = {
type: typeof SEND_MESSAGE_SUCCEEDED,
payload: SendMessageSuccess
}

export type SendMessageFailedEvent = {
type: typeof SEND_MESSAGE_FAILED
}

export type SendMessageRequest = SendDirectMessageRequest | SendGroupMessageRequest;

export type SendDirectMessageRequest = {
kind: "direct",
userId: UserId,
chatId: Option<ChatId>,
message: string,
unconfirmedMessageId: Symbol
}

export type SendGroupMessageRequest = {
kind: "group",
chatId: ChatId,
message: string,
unconfirmedMessageId: Symbol
}

export type SendMessageSuccess = SendDirectMessageSuccess | SendGroupMessageSuccess;

export type SendDirectMessageSuccess = {
kind: "direct",
userId: UserId,
chatId: Option<ChatId>,
message: string,
unconfirmedMessageId: Symbol,
confirmedMessageId: number,
confirmedMessageTimestamp: Timestamp
}

export type SendGroupMessageSuccess = {
kind: "group",
chatId: ChatId,
message: string,
unconfirmedMessageId: Symbol,
confirmedMessageId: number,
confirmedMessageTimestamp: Timestamp
}
103 changes: 103 additions & 0 deletions src/open_chat_assets/public/actions/chats/setupNewDirectChat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import getUserId from "../../services/userMgmt/getUserId";
import { RootState } from "../../reducers";
import { Chat } from "../../model/chats";
import { Option } from "../../model/common";
import { UserId, UserSummary } from "../../model/users";
import { userIdsEqual } from "../../utils";

export const SETUP_NEW_DIRECT_CHAT_REQUESTED = "SETUP_NEW_DIRECT_CHAT_REQUESTED";
export const SETUP_NEW_DIRECT_CHAT_SUCCEEDED = "SETUP_NEW_DIRECT_CHAT_SUCCEEDED";
export const SETUP_NEW_DIRECT_CHAT_FAILED_USER_NOT_FOUND = "SETUP_NEW_DIRECT_CHAT_FAILED_USER_NOT_FOUND";
export const SETUP_NEW_DIRECT_CHAT_FAILED_CHAT_ALREADY_EXISTS = "SETUP_NEW_DIRECT_CHAT_FAILED_CHAT_ALREADY_EXISTS";
export const SETUP_NEW_DIRECT_CHAT_FAILED_CANT_CREATE_CHAT_WITH_SELF = "SETUP_NEW_DIRECT_CHAT_FAILED_CANT_CREATE_CHAT_WITH_SELF";

export default function(username: string) {
return async (dispatch: any, getState: () => RootState) => {
const requestEvent: SetupNewDirectChatRequestedEvent = {
type: SETUP_NEW_DIRECT_CHAT_REQUESTED,
payload: username
};

dispatch(requestEvent);

const outcomeEvent = await getOutcomeEvent();

dispatch(outcomeEvent);

async function getOutcomeEvent() {
const state = getState();

if (state.usersState.me!.username === username) {
return {
type: SETUP_NEW_DIRECT_CHAT_FAILED_CANT_CREATE_CHAT_WITH_SELF
} as SetupNewDirectChatFailedCantCreateChatWithSelfEvent;
}

let userId = findUserId(state.usersState.userDictionary, username);
if (!userId) {
const getUserResponse = await getUserId(username);

if (getUserResponse.kind === "success") {
userId = getUserResponse.userId;
} else {
return {
type: SETUP_NEW_DIRECT_CHAT_FAILED_USER_NOT_FOUND,
payload: username
} as SetupNewDirectChatFailedUserNotFoundEvent;
}
}

if (!chatAlreadyExists(state.chatsState.chats, userId)) {
return {
type: SETUP_NEW_DIRECT_CHAT_SUCCEEDED,
payload: {
userId,
username,
version: 0
}
} as SetupNewDirectChatSucceededEvent;
} else {
return {
type: SETUP_NEW_DIRECT_CHAT_FAILED_CHAT_ALREADY_EXISTS,
payload: username
} as SetupNewDirectChatFailedChatAlreadyExistsEvent;
}
}
}
}

function chatAlreadyExists(chats: Chat[], userId: UserId) : boolean {
const chat = chats.find(c => c.kind === "direct" && userIdsEqual(c.them, userId));

return Boolean(chat);
}

function findUserId(userDictionary: any, username: string) : Option<UserId> {
const key = Object.keys(userDictionary).find(k => userDictionary[k].username === username);

return key ? userDictionary[key].userId : null;
}

export type SetupNewDirectChatRequestedEvent = {
type: typeof SETUP_NEW_DIRECT_CHAT_REQUESTED,
payload: string
}

export type SetupNewDirectChatSucceededEvent = {
type: typeof SETUP_NEW_DIRECT_CHAT_SUCCEEDED,
payload: UserSummary
}

export type SetupNewDirectChatFailedUserNotFoundEvent = {
type: typeof SETUP_NEW_DIRECT_CHAT_FAILED_USER_NOT_FOUND,
payload: string
}

export type SetupNewDirectChatFailedChatAlreadyExistsEvent = {
type: typeof SETUP_NEW_DIRECT_CHAT_FAILED_CHAT_ALREADY_EXISTS,
payload: string
}

export type SetupNewDirectChatFailedCantCreateChatWithSelfEvent = {
type: typeof SETUP_NEW_DIRECT_CHAT_FAILED_CANT_CREATE_CHAT_WITH_SELF
}
Loading

0 comments on commit 7b06e0a

Please sign in to comment.