Skip to content

Commit

Permalink
integrate smart inboxes in shinkai web/mobile
Browse files Browse the repository at this point in the history
  • Loading branch information
paulclindo committed Nov 6, 2023
1 parent 44894ef commit fd8499e
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 37 deletions.
143 changes: 114 additions & 29 deletions apps/shinkai-app/src/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,141 @@
import './Home.css';

import { zodResolver } from '@hookform/resolvers/zod';
import {
IonActionSheet,
IonAlert,
IonButton,
IonButtons,
IonIcon,
IonInput,
IonItem,
IonLabel,
IonPage,
IonText,
IonTitle,
} from '@ionic/react';
import { SmartInbox } from '@shinkai_network/shinkai-message-ts/models';
import { useUpdateInboxName } from '@shinkai_network/shinkai-node-state/lib/mutations/updateInboxName/useUpdateInboxName';
import { useGetInboxes } from '@shinkai_network/shinkai-node-state/lib/queries/getInboxes/useGetInboxes';
import { addOutline, arrowForward } from 'ionicons/icons';
import { Edit3Icon } from 'lucide-react';
import React, { useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useHistory } from 'react-router-dom';
import { z } from 'zod';

import Avatar from '../components/ui/Avatar';
import Button from '../components/ui/Button';
import { IonContentCustom, IonHeaderCustom } from '../components/ui/Layout';
import { useAuth } from '../store/auth';

const updateInboxNameSchema = z.object({
inboxName: z.string(),
});

const MessageButton = ({ inbox }: { inbox: SmartInbox }) => {
const history = useHistory();
const auth = useAuth((state) => state.auth);
const [isEditable, setIsEditable] = useState(false);
const updateInboxNameForm = useForm<z.infer<typeof updateInboxNameSchema>>({
resolver: zodResolver(updateInboxNameSchema),
});

const { inboxName: inboxNameValue } = updateInboxNameForm.watch();
const { mutateAsync: updateInboxName } = useUpdateInboxName();

const onSubmit = async (data: z.infer<typeof updateInboxNameSchema>) => {
if (!auth) return;
console.log('data', data);
await updateInboxName({
sender: auth.shinkai_identity,
senderSubidentity: auth.profile,
receiver: `${auth.shinkai_identity}`,
receiverSubidentity: '',
my_device_encryption_sk: auth.my_device_encryption_sk,
my_device_identity_sk: auth.my_device_identity_sk,
node_encryption_pk: auth.node_encryption_pk,
profile_encryption_sk: auth.profile_encryption_sk,
profile_identity_sk: auth.profile_identity_sk,

inboxId: decodeURIComponent(inbox.inbox_id),
inboxName: data.inboxName,
});
setIsEditable(false);
};

if (isEditable) {
return (
<form
className="space-y-10"
onSubmit={updateInboxNameForm.handleSubmit(onSubmit)}
>
<Controller
control={updateInboxNameForm.control}
name="inboxName"
render={({ field }) => (
<div>
<IonLabel>Enter Shinkai Identity</IonLabel>
<IonInput
onIonChange={(e) =>
updateInboxNameForm.setValue(
'inboxName',
e.detail.value as string
)
}
placeholder="@@name.shinkai or @@name.shinkai/profile"
value={field.value}
/>
</div>
)}
/>
<Button isLoading={false} type="submit">
Save
</Button>
</form>
);
}

return (
<div className="flex items-center gap-2" key={inbox.inbox_id}>
<IonItem
button
className="ion-item-home"
onClick={() => {
const encodedInboxId = inbox.inbox_id.toString().replace(/\./g, '~');
if (encodedInboxId.startsWith('inbox')) {
history.push(`/chat/${encodeURIComponent(encodedInboxId)}`);
} else if (encodedInboxId.startsWith('job_inbox')) {
history.push(`/job-chat/${encodeURIComponent(encodedInboxId)}`);
}
}}
>
<Avatar
className="shrink-0"
url={`https://ui-avatars.com/api/?name=${inbox.custom_name}&background=FE6162&color=fff`}
/>
<IonText className="ml-4 font-medium md:text-lg">
{JSON.stringify(inbox.custom_name)}
</IonText>
<IonIcon className="hidden md:ml-auto md:block" icon={arrowForward} />
</IonItem>
<button
onClick={() => {
console.log(inbox.inbox_id);
setIsEditable(true);
}}
>
<Edit3Icon />
</button>
</div>
);
};

const Home: React.FC = () => {
const auth = useAuth((state) => state.auth);
const setLogout = useAuth((state) => state.setLogout);

const { inboxIds } = useGetInboxes({
const { inboxes } = useGetInboxes({
sender: auth?.shinkai_identity ?? '',
senderSubidentity: `${auth?.profile}/device/${auth?.registration_name}`,
// Assuming receiver and target_shinkai_name_profile are the same as sender
Expand Down Expand Up @@ -74,34 +185,8 @@ const Home: React.FC = () => {
<IonContentCustom>
<div className="h-full flex flex-col mt-4">
<div className="flex-1 md:rounded-lg space-y-2 md:space-y-4">
{inboxIds?.map((inboxId) => (
<IonItem
button
className="ion-item-home"
key={inboxId}
onClick={() => {
const encodedInboxId = inboxId.toString().replace(/\./g, '~');
if (encodedInboxId.startsWith('inbox')) {
history.push(`/chat/${encodeURIComponent(encodedInboxId)}`);
} else if (encodedInboxId.startsWith('job_inbox')) {
history.push(
`/job-chat/${encodeURIComponent(encodedInboxId)}`
);
}
}}
>
<Avatar
className="shrink-0"
url={`https://ui-avatars.com/api/?name=${inboxId}&background=FE6162&color=fff`}
/>
<IonText className="ml-4 font-medium md:text-lg">
{JSON.stringify(inboxId)}
</IonText>
<IonIcon
className="hidden md:ml-auto md:block"
icon={arrowForward}
/>
</IonItem>
{inboxes?.map((inbox) => (
<MessageButton inbox={inbox} key={inbox.inbox_id} />
))}
</div>
</div>
Expand Down
12 changes: 7 additions & 5 deletions libs/shinkai-message-ts/src/wasm/ShinkaiMessageBuilderWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,9 @@ export class ShinkaiMessageBuilderWrapper {
'None'
);
builder.external_metadata(receiver, sender);
// TODO: fix encryption
builder.body_encryption(
'DiffieHellmanChaChaPoly1305'
'None'
);

const message = builder.build_to_string();
Expand Down Expand Up @@ -464,7 +465,7 @@ export class ShinkaiMessageBuilderWrapper {
'None'
);
builder.external_metadata_with_intra(receiver, sender, sender_subidentity);
builder.body_encryption('DiffieHellmanChaChaPoly1305');
builder.body_encryption('None');

const message = builder.build_to_string();

Expand Down Expand Up @@ -498,7 +499,7 @@ export class ShinkaiMessageBuilderWrapper {
);
builder.external_metadata_with_intra(receiver, sender, sender_subidentity);

builder.body_encryption('DiffieHellmanChaChaPoly1305');
builder.body_encryption('None');

const message = builder.build_to_string();

Expand Down Expand Up @@ -559,10 +560,11 @@ export class ShinkaiMessageBuilderWrapper {
sender_subidentity,
receiver_subidentity,
inbox,
"None"
'None'
);
builder.external_metadata_with_intra(receiver, sender, sender_subidentity);
builder.body_encryption('DiffieHellmanChaChaPoly1305');
// TODO: fix encryption
builder.body_encryption('None');

const message = builder.build_to_string();

Expand Down
98 changes: 98 additions & 0 deletions libs/shinkai-node-state/src/lib/mutations/updateInboxName/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { ApiConfig, handleHttpError } from "@shinkai_network/shinkai-message-ts/api";
import {
type CredentialsPayload,
type ShinkaiMessage,
} from "@shinkai_network/shinkai-message-ts/models";
import {
ShinkaiMessageBuilderWrapper,
} from "@shinkai_network/shinkai-message-ts/wasm";

export type SmartInbox = {
custom_name: string;
inbox_id: string;
last_message: ShinkaiMessage;
};

export const updateInboxNameApi = async (
sender: string,
sender_subidentity: string,
receiver: string,
receiver_subidentity: string,
setupDetailsState: CredentialsPayload,
inboxName: string,
inboxId: string
): Promise<SmartInbox[]> => {
try {
const messageString = ShinkaiMessageBuilderWrapper.update_shinkai_inbox_name(
setupDetailsState.my_device_encryption_sk,
setupDetailsState.my_device_identity_sk,
setupDetailsState.node_encryption_pk,
sender,
sender_subidentity,
receiver,
receiver_subidentity,
inboxId,
inboxName
);

const message = JSON.parse(messageString);

const apiEndpoint = ApiConfig.getInstance().getEndpoint();
const response = await fetch(`${apiEndpoint}/v1/update_smart_inbox_name`, {
method: "POST",
body: JSON.stringify(message),
headers: { "Content-Type": "application/json" },
});
const data = await response.json();
await handleHttpError(response);
return data.data;
} catch (error) {
console.error("Error updating inbox name:", error);
throw error;
}
};

export const updateInboxName = async ({
senderSubidentity,
sender,
receiver,
receiverSubidentity,
my_device_encryption_sk,
my_device_identity_sk,
node_encryption_pk,
profile_encryption_sk,
profile_identity_sk, // eslint-disable-next-line @typescript-eslint/no-explicit-any
inboxName,
inboxId, // eslint-disable-next-line @typescript-eslint/no-explicit-any
}: {
senderSubidentity: string;
sender: string;
receiver: string;
receiverSubidentity: string;
my_device_encryption_sk: string;
my_device_identity_sk: string;
node_encryption_pk: string;
profile_encryption_sk: string;
profile_identity_sk: string;
inboxName: string;
inboxId: string;
}) => {
const response = await updateInboxNameApi(
sender,
senderSubidentity,
receiver,
receiverSubidentity,
{
my_device_encryption_sk,
my_device_identity_sk,
node_encryption_pk,
profile_encryption_sk,
profile_identity_sk,
},
inboxName,
inboxId
);
console.log("updateInboxName response:", response);

return response;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

export type UpdateInboxNamebInput = any;

export type UpdateInboxNameOutput = any;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { UseMutationOptions } from "@tanstack/react-query";
import { useMutation } from "@tanstack/react-query";

import { updateInboxName } from ".";
import type { UpdateInboxNamebInput,UpdateInboxNameOutput } from "./types";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Options = UseMutationOptions<UpdateInboxNameOutput, Error, UpdateInboxNamebInput>;

export const useUpdateInboxName = (options?: Options) => {
return useMutation({
mutationFn: updateInboxName,
...options,
});
};
7 changes: 5 additions & 2 deletions libs/shinkai-node-state/src/lib/queries/getInboxes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@ export const getInboxes = async ({
profile_identity_sk,
}
);
return inboxes.map((inbox) => encodeURIComponent(inbox));
};
return inboxes.map((inbox) => ({
...inbox,
inbox_id: encodeURIComponent(inbox.inbox_id),
custom_name: encodeURIComponent(inbox.custom_name),
}));};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export const useGetInboxes = (input: GetInboxesInput) => {
});
return {
...response,
inboxIds: response.data ?? [],
inboxes: response.data ?? [],
};
};

0 comments on commit fd8499e

Please sign in to comment.