Skip to content

Commit

Permalink
574 chat (#589)
Browse files Browse the repository at this point in the history
* stashed files added

* signin page ui

* renamed file

* fixed linting issues

* integrated rainbow wallet

* integrated metamask wallet & fixed home image strech issue

* fixed error message

* removed provider check

* removed fontawesome library & added svg image

* added user profile page

* integrated user contact list

* changed color of contact background on hover

* created hamburger menu ui

* removed unused import

* fixed prettier issue

* created contacts action menu

* added contact menu list

* UI for user contact page

* contact info page

* add conversation UI

* integrated add conversation modal

* integrated preferences modal

* optimized code

* removed unused code & fixed linting

* changed let to const

* UI for profile configuration

* added error messages

* Configure user profile

* removed unused parameter

* fixed prettier issue

* env test

* hide contact integrated

* modified condition check

* profile not configured info box

* env test

* chat screen

* chat message UI

* library function integration for chat

* removed unused code

* resolved conflicts

* fixed conflict

---------

Co-authored-by: Bhupesh-MS <[email protected]>
Co-authored-by: Heiko Burkhardt <[email protected]>
  • Loading branch information
3 people authored Aug 29, 2023
1 parent 1943a6d commit 065f15b
Show file tree
Hide file tree
Showing 21 changed files with 782 additions and 29 deletions.
6 changes: 1 addition & 5 deletions packages/messenger-widget/src/adapters/contacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,7 @@ export async function addContact(
userDb: UserDB,
createEmptyConversationEntry: (id: string) => void,
) {
if (
!createEmptyConversation(ensName, userDb, createEmptyConversationEntry)
) {
throw Error('Contact exists already.');
}
createEmptyConversation(ensName, userDb, createEmptyConversationEntry);
}

function fetchDeliveryServiceProfile(connection: Connection) {
Expand Down
146 changes: 143 additions & 3 deletions packages/messenger-widget/src/adapters/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@ import {
buildEnvelop,
EncryptionEnvelop,
} from 'dm3-lib-messaging';
import { getDeliveryServiceClient } from 'dm3-lib-profile';
import {
Account,
getDeliveryServiceClient,
getDeliveryServiceProfile,
normalizeEnsName,
} from 'dm3-lib-profile';
import { log } from 'dm3-lib-shared';
import { StorageEnvelopContainer } from 'dm3-lib-storage';
import {
StorageEnvelopContainer,
UserDB,
getConversation,
} from 'dm3-lib-storage';
import { Connection } from '../interfaces/web3';
import { withAuthHeader } from './auth';
import { decryptAsymmetric } from 'dm3-lib-crypto';

export async function fetchPendingConversations(
connection: Connection,
Expand Down Expand Up @@ -158,7 +168,137 @@ export async function sendMessage(
);
}

export async function fetchAndStoreMessages(
connection: Connection,
deliveryServiceToken: string,
contact: string,
userDb: UserDB,
storeMessages: (envelops: StorageEnvelopContainer[]) => void,
contacts: Account[],
): Promise<StorageEnvelopContainer[]> {
const profile = connection.account?.profile;

if (!profile) {
throw Error('Account has no profile');
}
//Fetch evey delivery service's profie
const deliveryServices = await Promise.all(
profile.deliveryServices.map(async (ds) => {
const deliveryServiceProfile = await getDeliveryServiceProfile(
ds,
connection.provider!,
async (url) => (await axios.get(url)).data,
);
return deliveryServiceProfile?.url;
}),
);

//Filter every deliveryService without an url
const deliveryServiceUrls = deliveryServices.filter(
(ds): ds is string => !!ds,
);

//Fetch messages from each deliveryService
const messages = await Promise.all(
deliveryServiceUrls.map(async (baseUrl) => {
return await fetchNewMessages(
connection,
deliveryServiceToken,
contact,
);
}),
);

//Flatten the message arrays of each delivery service to one message array
const allMessages = messages.reduce((agg, cur) => [...agg, ...cur], []);
const isFulfilled = <T>(
p: PromiseSettledResult<T>,
): p is PromiseFulfilledResult<T> => p.status === 'fulfilled';

const envelops = (
await Promise.allSettled(
/**
* Decrypts every message using the receivers encryptionKey
*/
allMessages.map(
async (envelop: any): Promise<StorageEnvelopContainer> => {
const decryptedEnvelop = await decryptMessages(
[envelop],
userDb,
);

return {
envelop: decryptedEnvelop[0],
messageState: MessageState.Send,
deliveryServiceIncommingTimestamp:
decryptedEnvelop[0].postmark?.incommingTimestamp,
};
},
),
)
)
.filter(isFulfilled)
.map((settledResult) => settledResult.value);

//Storing the newly fetched messages in the userDb
storeMessages(envelops);

try {
//Return all messages from the conversation between the user and their contact
return getConversation(contact, contacts, userDb);
} catch (error) {
return [];
}
}

async function decryptMessages(
envelops: EncryptionEnvelop[],
userDb: UserDB,
): Promise<Envelop[]> {
return Promise.all(
envelops.map(
async (envelop): Promise<Envelop> => ({
message: JSON.parse(
await decryptAsymmetric(
userDb.keys.encryptionKeyPair,
JSON.parse(envelop.message),
),
),
postmark: JSON.parse(
await decryptAsymmetric(
userDb.keys.encryptionKeyPair,
JSON.parse(envelop.postmark!),
),
),
metadata: envelop.metadata,
}),
),
);
}

export async function fetchNewMessages(
connection: Connection,
token: string,
contactAddress: string,
): Promise<EncryptionEnvelop[]> {
const { account } = connection;
const deliveryPath = process.env.REACT_APP_BACKEND + '/delivery';
const url = `${deliveryPath}/messages/${normalizeEnsName(
account!.ensName,
)}/contact/${contactAddress}`;

const { data } = await getDeliveryServiceClient(
account!.profile!,
connection.provider!,
async (url: string) => (await axios.get(url)).data,
).get(url, withAuthHeader(token));

return data;
}

export type SendMessage = typeof sendMessage;
export type CreatePendingEntry = typeof createPendingEntry;
export type SubmitMessageType = typeof submitMessage;
export type GetNewMessages = typeof fetchNewMessages;
export type CreatePendingEntry = typeof createPendingEntry;
export type FetchAndStoreMessages = typeof fetchAndStoreMessages;
export type FetchPendingConversations = typeof fetchPendingConversations;
9 changes: 9 additions & 0 deletions packages/messenger-widget/src/assets/images/emoji.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions packages/messenger-widget/src/assets/images/file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/messenger-widget/src/assets/images/send-btn.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/messenger-widget/src/assets/images/tick.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions packages/messenger-widget/src/components/Chat/Chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,20 @@
.highlight-chat-border-none {
border-top: none;
}

.chat-container {
min-height: 86vh;
}

.chat-items {
overflow-y: scroll;
margin-top: 0.5rem;
}

.chat-height-small {
height: 63vh;
}

.chat-height-high {
height: 75vh;
}
Loading

0 comments on commit 065f15b

Please sign in to comment.