Skip to content

Commit

Permalink
Merge pull request #6 from dcSpark/nico/move-latest-updates
Browse files Browse the repository at this point in the history
moving latest updates
  • Loading branch information
nicarq authored Sep 9, 2023
2 parents c1c2100 + 1ad0328 commit 864abc1
Show file tree
Hide file tree
Showing 21 changed files with 1,095 additions and 884 deletions.
191 changes: 191 additions & 0 deletions apps/shinkai-app/src/components/ChatMessages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {
getLastMessagesFromInbox,
getLastUnreadMessagesFromInbox,
} from '@shinkai/shinkai-message-ts/api/methods';
import { ShinkaiMessage } from '@shinkai/shinkai-message-ts/models';
import { IonList, IonItem, IonButton } from '@ionic/react';
import Avatar from '../components/ui/Avatar';
import { cn } from '../theme/lib/utils';
import { IonContentCustom } from './ui/Layout';
import { calculateMessageHash } from '@shinkai/shinkai-message-ts/utils/shinkai_message_handler';
import { RootState } from '../store';
import { receiveLastMessagesFromInbox } from '../store/actions';

interface ChatMessagesProps {
deserializedId: string;
}

const ChatMessages: React.FC<ChatMessagesProps> = ({ deserializedId }) => {
console.log('Loading ChatMessages.tsx');
const dispatch = useDispatch();
const setupDetailsState = useSelector(
(state: RootState) => state.setupDetails
);
const reduxMessages = useSelector(
(state: RootState) => state.messages.inboxes[deserializedId]
);

const [lastKey, setLastKey] = useState<string | undefined>(undefined);
const [mostRecentKey, setMostRecentKey] = useState<string | undefined>(
undefined
);
const [prevMessagesLength, setPrevMessagesLength] = useState(0);
const [hasMoreMessages, setHasMoreMessages] = useState(true);
const [messages, setMessages] = useState<ShinkaiMessage[]>([]);

useEffect(() => {
console.log('deserializedId:', deserializedId);
getLastMessagesFromInbox(
deserializedId,
10,
lastKey,
setupDetailsState
).then((messages) => {
dispatch(receiveLastMessagesFromInbox(deserializedId, messages));
});
}, [dispatch, setupDetailsState, deserializedId, lastKey]);

useEffect(() => {
const interval = setInterval(() => {
getLastUnreadMessagesFromInbox(
deserializedId,
10,
mostRecentKey,
setupDetailsState
).then((messages) => {
dispatch(receiveLastMessagesFromInbox(deserializedId, messages));
});
}, 5000); // 2000 milliseconds = 2 seconds
return () => clearInterval(interval);
}, [
dispatch,
deserializedId,
mostRecentKey,
setupDetailsState,
]);

useEffect(() => {
if (reduxMessages && reduxMessages.length > 0) {
// console.log("Redux Messages:", reduxMessages);
const lastMessage = reduxMessages[reduxMessages.length - 1];
console.log('Last Message:', lastMessage);
const timeKey = lastMessage.external_metadata.scheduled_time;
const hashKey = calculateMessageHash(lastMessage);
const lastMessageKey = `${timeKey}:::${hashKey}`;
setLastKey(lastMessageKey);

const mostRecentMessage = reduxMessages[0];
const mostRecentTimeKey =
mostRecentMessage.external_metadata.scheduled_time;
const mostRecentHashKey = calculateMessageHash(mostRecentMessage);
const mostRecentMessageKey = `${mostRecentTimeKey}:::${mostRecentHashKey}`;
setMostRecentKey(mostRecentMessageKey);

setMessages(reduxMessages);

if (reduxMessages.length - prevMessagesLength < 10) {
setHasMoreMessages(false);
}
setPrevMessagesLength(reduxMessages.length);
}
}, [reduxMessages, prevMessagesLength]);

const extractContent = (messageBody: any) => {
// TODO: extend it so it can be re-used by JobChat or normal Chat
if (messageBody && 'unencrypted' in messageBody) {
if ('unencrypted' in messageBody.unencrypted.message_data) {
return JSON.parse(
messageBody.unencrypted.message_data.unencrypted.message_raw_content
).content;
} else {
return JSON.parse(
messageBody.unencrypted.message_data.encrypted.content
).content;
}
} else if (messageBody?.encrypted) {
return JSON.parse(messageBody.encrypted.content).content;
}
return '';
};

return (
<IonContentCustom>
<div className="py-10 md:rounded-[1.25rem] bg-white dark:bg-slate-800">
{hasMoreMessages && (
<IonButton
onClick={() =>
dispatch(
getLastMessagesFromInbox(
deserializedId,
10,
lastKey,
setupDetailsState
// true
)
)
}
>
Load More
</IonButton>
)}
<IonList class="ion-list-chat p-0 divide-y divide-slate-200 dark:divide-slate-500/50 md:rounded=[1.25rem] ">
{messages &&
messages.slice().map((message, index) => {
const { shinkai_identity, profile, registration_name } =
setupDetailsState;

const localIdentity = `${profile}/device/${registration_name}`;
// console.log("Message:", message);
let isLocalMessage = false;
if (message.body && 'unencrypted' in message.body) {
isLocalMessage =
message.body.unencrypted.internal_metadata
.sender_subidentity === localIdentity;
}

return (
<IonItem
key={index}
lines="none"
className={cn(
'ion-item-chat relative w-full shadow',
isLocalMessage && 'isLocalMessage'
)}
>
<div className="px-2 py-4 flex gap-4 pb-10 w-full">
<Avatar
className="shrink-0 mr-4"
url={
isLocalMessage
? 'https://ui-avatars.com/api/?name=Me&background=FE6162&color=fff'
: 'https://ui-avatars.com/api/?name=O&background=363636&color=fff'
}
/>

<p>{extractContent(message.body)}</p>
{message?.external_metadata?.scheduled_time && (
<span className="absolute bottom-[5px] right-5 text-muted text-sm">
{new Date(
message.external_metadata.scheduled_time
).toLocaleString(undefined, {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})}
</span>
)}
</div>
</IonItem>
);
})}
</IonList>
</div>
</IonContentCustom>
);
};

export default ChatMessages;
27 changes: 0 additions & 27 deletions apps/shinkai-app/src/features/chat/chatSlice.ts

This file was deleted.

15 changes: 9 additions & 6 deletions apps/shinkai-app/src/hooks/usetSetup.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// hooks/useSetup.ts
import { useEffect } from "react";
import { useSelector } from "react-redux";
import { shallowEqual, useSelector } from "react-redux";
import { RootState } from "../store";
import { ApiConfig } from "@shinkai/shinkai-message-ts/api";

export const useSetup = () => {
const { setupDetailsState } = useSelector((state: RootState) => state);
const setupDetails = useSelector(
(state: RootState) => state.setupDetails,
shallowEqual
);

useEffect(() => {
console.log("Redux State:", setupDetailsState);
ApiConfig.getInstance().setEndpoint(setupDetailsState.node_address);
}, [setupDetailsState]);
};
console.log("Redux State:", setupDetails);
ApiConfig.getInstance().setEndpoint(setupDetails.node_address);
}, [setupDetails]);
};
15 changes: 10 additions & 5 deletions apps/shinkai-app/src/pages/AddAgent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ import {
import { useEffect, useState } from "react";
import { IonContentCustom, IonHeaderCustom } from "../components/ui/Layout";
import Button from "../components/ui/Button";
import Input from "../components/ui/Input";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "../store";
import { SerializedAgent, AgentAPIModel } from "@shinkai/shinkai-message-ts/models";
import { addAgent } from "@shinkai/shinkai-message-ts/api";
import { useSetup } from "../hooks/usetSetup";
import { useHistory } from 'react-router-dom';

const AddAgent: React.FC = () => {
useSetup();
const dispatch = useDispatch();
const setupDetailsState = useSelector(
(state: RootState) => state.setupDetailsState
(state: RootState) => state.setupDetails
);
const [agent, setAgent] = useState<Partial<SerializedAgent>>({
perform_locally: false,
Expand Down Expand Up @@ -70,7 +70,7 @@ const AddAgent: React.FC = () => {
});
};

const handleSubmit = () => {
const handleSubmit = async () => {
const { shinkai_identity, profile } = setupDetailsState;
const node_name = shinkai_identity;

Expand All @@ -82,13 +82,18 @@ const AddAgent: React.FC = () => {
}

console.log("Submitting agent:", agent);
addAgent(

const resp = await addAgent(
profile,
node_name,
agent as SerializedAgent,
setupDetailsState
);
if (resp) {
// TODO: show a success toast
// eslint-disable-next-line no-restricted-globals
history.back();
}
};

return (
Expand Down
4 changes: 2 additions & 2 deletions apps/shinkai-app/src/pages/AdminCommands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { useSetup } from "../hooks/usetSetup";
const AdminCommands: React.FC = () => {
useSetup();
const setupDetailsState = useSelector(
(state: RootState) => state.setupDetailsState
(state: RootState) => state.setupDetails
);
const [showCodeRegistrationActionSheet, setShowCodeRegistrationActionSheet] =
useState(false);
Expand All @@ -36,7 +36,7 @@ const AdminCommands: React.FC = () => {
const [profileName, setProfileName] = useState("");
const dispatch = useDispatch();
const registrationCode = useSelector(
(state: RootState) => state.registrationCode
(state: RootState) => state.other.registrationCode
);
const commands = [
"Get Peers",
Expand Down
10 changes: 5 additions & 5 deletions apps/shinkai-app/src/pages/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import React, { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
getLastMessagesFromInbox,
createChatWithMessage,
sendTextMessageWithInbox,
} from '@shinkai/shinkai-message-ts/api';
import { RootState } from '../store';
Expand All @@ -42,6 +41,7 @@ import {
IonHeaderCustom,
} from '../components/ui/Layout';
import { addMessageToInbox, receiveLastMessagesFromInbox, receiveLoadMoreMessagesFromInbox } from '../store/actions';
import { RECEIVE_LAST_MESSAGES_FROM_INBOX } from '../store/types';

const parseDate = (dateString: string) => {
return new Date(dateString);
Expand All @@ -53,7 +53,7 @@ const Chat: React.FC = () => {

const dispatch = useDispatch();
const setupDetailsState = useSelector(
(state: RootState) => state.setupDetailsState
(state: RootState) => state.setupDetails
);

const { id } = useParams<{ id: string }>();
Expand All @@ -64,7 +64,7 @@ const Chat: React.FC = () => {
const [prevMessagesLength, setPrevMessagesLength] = useState(0);

const reduxMessages = useSelector(
(state: RootState) => state.inboxes[deserializedId]
(state: RootState) => state.messages.inboxes[deserializedId]
);

const [messages, setMessages] = useState<ShinkaiMessage[]>([]);
Expand All @@ -78,9 +78,9 @@ const Chat: React.FC = () => {
console.log('deserializedId:', deserializedId);
getLastMessagesFromInbox(deserializedId, 10, lastKey, setupDetailsState).then((messages) => {
console.log("receiveLastMessagesFromInbox Response:", messages);
dispatch(receiveLastMessagesFromInbox(deserializedId, messages));
dispatch({ type: RECEIVE_LAST_MESSAGES_FROM_INBOX, payload: messages });
});
}, [id, dispatch, setupDetailsState]);
}, [id, dispatch, setupDetailsState, deserializedId, lastKey]);

useEffect(() => {
if (reduxMessages && reduxMessages.length > 0) {
Expand Down
7 changes: 2 additions & 5 deletions apps/shinkai-app/src/pages/Connect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@ import {
generateSignatureKeys,
} from "@shinkai/shinkai-message-ts/utils";
import { QRSetupData } from "../models/QRSetupData";
import { SetupDetailsState } from "../store/reducers";
import { InputCustomEvent } from "@ionic/core/dist/types/components/input/input-interface";
import { cn } from "../theme/lib/utils";
import Button from "../components/ui/Button";
import { IonHeaderCustom } from "../components/ui/Layout";
import Input from "../components/ui/Input";
import { scan, cloudUpload, checkmarkSharp } from "ionicons/icons";
import { useRegistrationCode } from "../store/actions";
import { SetupDetailsState } from "../store/reducers/setupDetailsReducer";

export type MergedSetupType = SetupDetailsState & QRSetupData;

Expand Down Expand Up @@ -58,7 +55,7 @@ const Connect: React.FC = () => {
const [error, setError] = useState<string | null>(null);
const dispatch = useDispatch<AppDispatch>();
const history = useHistory();
const errorFromState = useSelector((state: RootState) => state.error);
const errorFromState = useSelector((state: RootState) => state.other.error);

// Generate keys when the component mounts
useEffect(() => {
Expand Down
Loading

0 comments on commit 864abc1

Please sign in to comment.