New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/1239 message wrapper for render library #1297
Fix/1239 message wrapper for render library #1297
Conversation
…nder library creating sharedServices
lib/typescript/render/shared.ts
Outdated
return { | ||
fromContact, | ||
contact: props.contact ?? null, | ||
commandCallback: props.commandCallback, | ||
sentAt: props.lastInGroup ? formatTime(props.message.sentAt) : null, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the logic exclusive to rendering messages is moved to the wrapper <InfoMessageWrapper />
, which displays the Avatar
and sentAt
time along with the message's content.
This wrapper is also responsible for determining is which side the messages are displayed. The components inside the render library are stripped down from this logic and are just pure components.
-
RenderProps contains the base props common to all the content that the library needs to render (i.e. we need the
source
and therenderedContent
for both messages and templates). It's the base type fromMessageRenderProps
andTemplateRenderProps
.<InfoMessageWrapper />
takes inMessageRenderProps
to get the necessary data for the message rendering logic. -
DefaultRenderingProps are needed by some components that the library needs to render, for all content (for all the components that use
<Text />
, as this props will determine the backgroundColor of the message)
|
||
export function isFromContact(message: RenderedContentUnion) { | ||
if (message && 'senderType' in message) return message?.senderType === SenderType.sourceContact; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typing for render library: RenderedContentUnion represents the type for the content that the library is rendering
@@ -22,7 +23,8 @@ export default function templatesReducer(state = defaultState, action: Action): | |||
case getType(actions.listTemplatesAction): | |||
return { | |||
...state, | |||
all: action.payload, | |||
all: action.payload.templates, | |||
source: action.payload.source, | |||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This keeps track of the source of the templates that are listed. This is needed because we fetch the list of templates only when the conversation's source channel changes (as the api lists templates based on the source).
} | ||
|
||
event.currentTarget.alt = 'fallback image'; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With @thorstenairy we had the idea of creating SharedServices that contains helper functions needed by both the frontend and our libraries. Here fallbackImage serves for images in both the frontend and the render library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah so this was brought up many times and I should probably write down the answer eheh
the tl;dr is "shared" "util" is always bad as then we mix things that have nothing to do with each other. (I think last I talked to @thorstenairy exactly about this because he introduced a shared lib with was a date lib)
A better approach is to have small libraries that solve a specific problem.
to me, it feels like here we're introducting an avatar library
. So best is 1) call it what it does 2) that way we can move more "avatar related things" there 3) by far themost important point: we do not open the door to catch all libraries
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lucapette the idea was to also use this function for the fallback images in the render library.
I totally get the point of not falling in 'catch all libraries' so should we create 2 libraries: one avatar library
used for the channel avatars (and possibly also the Avatar component), and another library (fallback image library
) for the fallback images in the render library? (but then we would duplicate code)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplicating a little of code is better than the wrong abstraction for sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok we talked offline about it: we do not need to introduce libraries as the abstraction is not worth it. //c @thorstenairy
chatplugin = 'chatplugin', | ||
smsTwilio = 'twilio.sms', | ||
whatsappTwilio = 'twilio.whatsapp', | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the enum SourceType was duplicated in both Message and Channels in the httpClient library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
source was a better name :D (sorry... I suggested source type somewhere else.... without realising we call the field source everywhere in the api)
@@ -18,5 +19,6 @@ export const Media = ({height, contentInfo: {altText, fileUrl}}: MediaRenderProp | |||
className={`${styles.mediaImage} ${ | |||
height === MediaHeight.tall ? styles.tall : height === MediaHeight.medium ? styles.medium : styles.short | |||
}`} | |||
onError={(event: React.SyntheticEvent<HTMLImageElement, Event>) => fallbackImage(event, 'mediaImage')} | |||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added fallbackImage for all the images inside the render library. This fallback does not work for video, I've created an issue for this #1299
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First pass. Amazing work already!
@@ -47,6 +47,9 @@ const MessageInput = (props: MessageInputProps & ConnectedProps<typeof connector | |||
const sendButtonRef = useRef(null); | |||
const emojiDiv = useRef<HTMLDivElement>(null); | |||
|
|||
const conversationIdParams = useParams(); | |||
const currentConversationId: string = conversationIdParams[Object.keys(conversationIdParams)[0]]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible I would suggest to always use the getCurrentConversation
selector since it also uses the routing params but in a declarative and typed way :)
lib/typescript/render/shared.ts
Outdated
message: RenderedContent; | ||
interface RenderProps { | ||
contentType: 'message' | 'template'; | ||
renderedContent: RenderedContentUnion; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: At this point the content is not yet rendered so maybe just content
would be enough (applies to other places too)
lib/typescript/render/shared.ts
Outdated
import {isFromContact, RenderedContent, Contact} from 'httpclient'; | ||
import {formatTime} from 'dates'; | ||
import {DefaultMessageRenderingProps} from './components'; | ||
import {isFromContact, RenderedContentUnion} from 'httpclient'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: shared.ts
was never a great name for this file. Looking at it, what do you think about props.ts
or something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I think props.ts makes sense :)
frontend/sharedServices/BUILD
Outdated
load("@com_github_airyhq_bazel_tools//lint:buildifier.bzl", "check_pkg") | ||
load("@com_github_airyhq_bazel_tools//web:typescript.bzl", "ts_web_library") | ||
|
||
package(default_visibility = ["//visibility:public"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get the intention of this module, but I think the name is too generic and not quite accurate (It hosts a component and not a service). Only thing I can think of for now would be frontend/components
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
frontend/lib/avatar
would be my suggestion (given the previous point I made about it)
import {Channel, SourceType} from 'httpclient'; | ||
import {SourceTypeInfo} from '../MainPage'; | ||
import {Channel, Source} from 'httpclient'; | ||
import {SourceInfo} from '../MainPage'; | ||
import {fallbackImage} from '../../../services/image/index'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here updated naming to follow api naming Source
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome stuff 🥳 . Some minor comments. After the rebase is done I will 🟢 this.
@@ -14,7 +14,7 @@ const AvatarImage = (props: AvatarProps) => { | |||
|
|||
return ( | |||
<div className={styles.avatar}> | |||
<img className={styles.avatarImage} src={contact.avatarUrl || fallbackAvatar} /> | |||
<img className={styles.avatarImage} src={(contact && contact.avatarUrl) || fallbackAvatar} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use the new optional chaining operator instead:
<img className={styles.avatarImage} src={(contact && contact.avatarUrl) || fallbackAvatar} /> | |
<img className={styles.avatarImage} src={(contact?.avatarUrl) || fallbackAvatar} /> |
@@ -184,7 +184,7 @@ const ConversationMetadata = (props: ConnectedProps<typeof connector>) => { | |||
<AvatarImage contact={contact} /> | |||
</div> | |||
|
|||
<div className={styles.displayName}>{contact.displayName}</div> | |||
<div className={styles.displayName}>{contact && contact.displayName}</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like above:
<div className={styles.displayName}>{contact && contact.displayName}</div> | |
<div className={styles.displayName}>{contact?.displayName}</div> |
@@ -0,0 +1 @@ | |||
1.5:b2a9215c-db31-4f1b-b3db-1e3a1e879bbe |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the .vagrant
folder is gone now because minikube
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes! don't know how this snugged into my PR!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking great!
I think we should merge, I'm requesting changes so we don't forget to remove all the vagrant files before we merge :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
closes #1239
changes
InfoMessageWrapper
and updated all the frontendscreenshots
--> as all the message-rendering logic is moved to the wrapper, the template modal renders nicely all types of templates + the fallback image improves user experience for all non-valid images