-
Notifications
You must be signed in to change notification settings - Fork 12
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
feat: Chat Improvements #3365
Open
fcolarich
wants to merge
168
commits into
dev
Choose a base branch
from
feat/chat/chat_improvements_main
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
feat: Chat Improvements #3365
+34,541
−24,306
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
…bottom of its content. Fixed: There was an event handler that was never unsubscribed. The scroll view uses now clamped movement and the LoopListView is not using item snapping.
* Added new button to chat bubble + some layout fixes + show on hover * Added paste logic and hasValue check for clipboard * Added logic to show paste popup anywhere from anywhere + paste logic * Fixed some logic + prefabs * Fixed functionality + paste prefab tweaks * missing changes * Post dev merge fix * Changes to Chat Entries to be properly reused prefabs * Fixed chat scale * Fixed paste popup canvas ref * Fixed some rect issues with own chats * Fix after merge * Extracted design consts into SO for easier access * Update ChatEntryConfigurationSO.cs * fixed chat scale * Re-added missing paste popup position marker * Added character limit check when pasting text * Added context menu with copy option * fixed closing task * Update ChatController.cs * Fixed non masked frame on chats * Fixes from CR * Added clipboard manager service class to handle clipboard operations and callbacks * Update ChatController.cs * tweaks to clipboard manager
…for the controller Many fields and methods moved from ChatController to ChatView and adapted so the code flow is the same but separating concerns. The controller does not know anything about how the view is implemented. ChatView now exposes some public methods for the controller to call them, as well as some events to which the controller has to subscribe. Some dependencies injected to ChatController (emoji menu prefabs) are now directly referenced by ChatView in the Chat prefab. Some setting constants exposed in the inspector of ChatView. Now the chat bubbles toggle uses the isOn property and its events instead of showing / hiding images of the toggle manually. Functionality has not changed. The rest of scripts only contain renamed events.
The flag hasToAnimate was added to the ChatMessage structure as a way to know which messages were already animated in the chat window (a fade-in FX is used when a new message arrives) when using LoopListView2, which reuses ChatEntryViews through internal pools. It made no sense to store data strictly related to a view in the model. This workaround is not necessary anymore, ChatView is now in charge of knowing how many messages to animate.
… can access some external systems Added a new asmdef MVC.ViewDependencies. ChatView and EmojiSuggestionPanel inherit now from IViewWithSystemDependencies.
…of prompts and menus IViewWithSystemDependencies renamed to IViewWithGlobalDependencies. In order to implement GlobalUIViews class, I had to create asmdefs: Chat.Commands, Chat.MessageBus, UI.ChatEntryMenuPopup and UI.PastePopupToast. Scripts related to each asmdef were moved too into their own folder. Now the ViewDependencies instance is created in the Dynamic world container and sent to the ChatPlugin.
Some original code of EmojiSuggestionPanel reverted.
Now it is possible to have multiple conversations that reuse the scroll view of the Chat view, and send messages to any of them. Created the ChatChannelId struct which may be replaced by anything else in the future to identify a channel by its type and name. Currently it's not possible to send messages to specific channels of other users, that has to be implemented in the future, although the base has been put in, the protobuf Chat message may store the Id of the channel at the start of the actual message and can be parsed by both endpoints (see MultiplayerChatMessageBus). Currently everything is sent to the special channel "nearby". The ClearChatCommand had to be adapted since it has to be sent to the current chat channel, but only ChatView knows what's the current channel. So now the command does not change the Chat history, it calls the ChatController (injected in a later step during the initialization of the program).
ChatController and ChatView refactoring: Now the view is a black box for the controller
…hat/unreadmessages
ChatMessages do not store view data anymore, hasToAnimate removed
…/decentraland/unity-explorer into feat/chat/chat_improvements_main
…/decentraland/unity-explorer into feat/chat/chat_improvements_main
…esent A VerticalLayout component in Content was causing that LoopListView was going crazy. Now the layout has been removed and the width of the items adjusted. Added margins to Viewport, removed from Content (in Chat.prefab too). Changed the fading effect in the viewport of the Member list, as top padding was removed (with the VerticalLayout), top is now zero. The ChatMemberListItem prefab is now independent, it is not a variant of the Friends item prefab anymore. Unused components have been removed.
…/decentraland/unity-explorer into feat/chat/chat_improvements_main
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Congrats! You have been
cursedselected to review this PR!You will get 10 karma points added to your character*
The first question that might pop into your mind must be:
What kind of demonic ritual is this?
Well, turns out it is a really cool and groovy demonic ritual, and now you are part of it too!*
The first thing you should know is that this implements all the logic requested here + some improvements done thinking in a future shape that will require to have independent chat channels for different conversations.
All these features were done in their own PRs and merged, here are the PRs in case you want some specific detail not mentioned here:
Mentions
Hyperlinks
Unread Messages
Members List
Copy Paste
So, we will go through all the relevant classes and assets, with some short explanations about the logic and mostly the why the hell did you do this's,:
ChatEntry
We have split the default
ChatEntry
into a base one and 3 variants, so we dont have to replicate all the changes into the 3 independent ones. This is necessary to display Own, System and Other users' messages. This also included making prefabs of some of the elements of the asset to make it easier to edit without conflicts and to reuse them + splitting the responsibility of theChatEntryView
. These are theChatEntryMessageBubbleElement
,ChatEntryMessageContentElement
&&ChatEntryUsernameElement
, now each tasked with specific responsibilities. The only logic that we have not extracted is the profile picture, because we plan to replace it with a generic asset after this shape, as this logic is re-copied in so many places.We also added a new button that opens a small popup menu with options (for now only with the Copy option, but might have more in the future).
As you will notice these elements are pure views, without a controller, given that they only affect the visual state of the asset and do not change the data at all. This means they are mostly monobehaviours. This pattern will repeat all along the code with some sprinkles on top to keep it fun.
IViewWithGlobalDependencies
We will do a small stop here to talk about the
IViewWithGlobalDependencies
. This new type of view has access to a new shared class calledViewDependencies
which includes many generic dependencies that are needed in views without requiring a controller just to get a reference. Ideally these dependencies should not allow to write data, but only to process it in some way or another to use of the view. Like getting a list of profiles or getting access to theClipboardManager
or to the also newMVCManagerMenusAccessFacade
. In turn this class provides a restricted access to the MVC to display some specific popups that should be accessible from any view, for example, a generic context menu with profile data or the prompt to change realm.ChatView
We split the historic chat asset and view into many sub-views and prefabs. We also moved a bunch of code from the controller into the view, as its code that strictly related to visual changes and not data changes. So the view now exposes its functionality to the controller through public methods and events instead of by giving it full access to everything.
ChatInputBox
This little thing represents the area of the chat dedicated to receive user inputs, including the emoji button and the character counter, as well as the suggestion box to display suggestions.
This asset was extracted from the big Chat asset, again to make it easier to edit and debug and is governed by the
ChatInputBoxElement
which makes use of theViewDependencies
mentioned earlier.From the
ChatInputBoxElement
we have extracted a lot of functionality that managed emoji suggestions. Now those are in their own independent classes, starting with theInputSuggestionPanelElement
which displays any kind of suggestion (which now are a generic type that gets assigned through the editor, so we can have suggestion panels with different kind of suggestions each).Also a lot of changes were done to better handle different types of suggestions, with different regex's for them, the new copy/paste functionality and the new channels handling.
ChatMessageViewerElement
The other half of the Chat, where the messages are displayed, it was also extracted from the main Chat asset and class, with a lot of custom logic

This class handles how the messages are displayed, as well as how the new messages are notified and loaded, the new messages indicator, etc. This includes a lot of changes to the message loading logic, to properly follow the new rules for new messages arriving either while the player is looking or when they have the chat disabled.
ChatCommandBus
&IChatCommandsBus
This little 🚌 was created to remove dependencies between chat commands and everything else. Now, the chat commands invoke methods in the bus and the receiver end subscribes to them. No weird reference sharing needed nor weird public access to classes created.
So far we only replaced a few of these dependencies (mainly the ones that gave us circular refs), but all chat commands should go through the bus as much as possible instead of referencing directly other classes (unless it makes sense of course :))
We also did some housekeeping and moved all chat commands to where they belong, the Chat/Commands/ folder where they will live happy ever after.
ChatChannel
This class represents a conversation thread, this is implemented but is not functional completely, as we only have one channel right now (Nearby). The channel stores all the messages corresponding to it and signals through events when the message list has changed somehow.
This also involves changes to the
ChatHistory
, that now adds messages to the corresponding channel. Of course, this code is still WIP in the sense that we only have one source of messages, but it will be further changed in future shapes when conversations are implemented.ChatMessage
Changes to this class focus mostly in adding new fields or changing some field names to better reflect their content, as well as being moved from its original location. The new fields added are:
ClipboardManager
Encapsulates logic used to interact with the clipboard and sends events upon copy or paste. Also includes a
CopyAndSanitize
method to remove all rich text tags from a copied text, used when copying text from a chat message.This included changes to the
ISystemClipboard
, to encapsulate theGUIUtility.systemCopyBuffer
access, so now we can Set and Get, as well as check if it HasValue.IProfileNameColorHelper
This class was introduced to remove dependencies with the Chat caused by the use of the
ChatEntryConfigurationSO
. This basically helps to select a color from a list using a string (usually the avatar name) as input. Now the list of colors is defined directly in theDynamicSettings
and used by this class, so it no longer needs to be a SO at all.DCLCursor
Added a new boolean IsStyleForced that helps when setting a cursor style (which changes the visual aspect of the cursor), making it ignore the value set in the ECS systen
UpdateCursorInputSystem
. This is needed when hovering over an hyperlink inside a text component. Given how our system works, there is no way to discriminate against a link inside a text vs the whole TMP_Text component. But we can do it from the text component directly, but the system would overwrite this change and is where forcing it comes into place.NametagView
Even thou it looks like it was heavily modified, most of it were name changes for fields + the introduction of the mentionBackground which needs to be displayed instead of the normal background when the message contains a mention.
FloatingPanel
prefabWas removed because it is no longer used
GenericContextMenuPlugin
And associated classes, we needed to add 2 new context menu types (
GenericContextMenuOpenUserProfileButtonView
&GenericContextMenuMentionUserButtonView
), even thou they are basically menus with a button, they have some integrated behavior related to Profiles that we needed them to properly do and with current implementation we cannot reuse easily a view with different types of content. It remains a point that can be improved in the future.Also, we added the option to display the context menu using different anchor points, so it doesnt need to be always on the top left.
GenericPopupsPlugin
And related classes, this were created before the GenericContextMenu was available, it serves a similar purpose, to display small popups for COPY and PASTE. It is already planned to migrate them to use the new GenericContextMenu
DefaultProfileCache
Added the option to get a Profile by full Username (including the # 1234 suffix), as in some places we only have that information. This meant creating a small dictionary that maps usernames to walletIds.
Profile
Added several improvements to avoid re-calculating certain data, particularly strings and colors.
Now the profile stores the assigned color for that user, so we only calculate it once when the profile is created.
Also now we store the WalletId (so the # 1234) string directly, so no more string operations to get it from the username.
Also we store the MentionName which includes the @. This is only cached on first use, so its not created unless we use it.
ChatSoundsSettings
Along with all the related classes needed to modify the settings menu, we replaced the toggle for chat sounds with a dropdown, that allows us to enable all sounds, disable them all or enable only mention sounds (so only when someone mentions you, you hear a sound).
CustomInputField
This is a class that inherits from
TMP_InputField
, because we need to override what goes on with certain input actions (like ctrl+v and when using the arrow keys UP and DOWN) during theOnUpdateSelected
flow. Also, we added additional functionality that handles inserting and replacing text in an efficient way, avoiding unnecessary OnInputChanged events. Check theTryHandleSpecialKeys
method for some more details.It also allows us to send events upon receiving a right click, which we need to display a paste popup (but could be used for other functionality in other situations as well). Normally the TMP_InputField would just ignore right clicks altogether.
TextHyperlinkHandlerElement
As it names says, it allows for handling hyperlinks in objects with a
TMP_Text
. It reacts to mouse movements to highlight hyperlinks and reacts on click doing different actions depending on the type of link. Uses heavily the newViewDependencies
for this.BaseSuggestionElement
&IInputSuggestionElementData
These and their inheritors are the replacement for the old emoji suggestions. These work in a generic way to define any kind of suggestion, for now only Emojis and Profiles, but can be expanded rather easily. These elements are then loaded into a SO that is referenced by the
InputSuggestionPanelElement
, so each panel can display different types of suggestions depending on how it is configured.ITextFormatter
&HyperlinkTextFormatter
This new class helps format text, by changing the input text into something else using the defined rules inside the formatter.
In the case of the
HyperlinkTextFormatter
we use it to add rich text Links and to remove invalid rich text tags (all except bold and cursive). Also, this formatter does some validation for some of the links, so it knows if to make it a link or not and which format to give them. This happens for parcel coordinates, where we check if they are inside the bounds and also for user names, where we check if its our own username (so we color it) but not make it a link or we check if we got their profile in our cache, in which case its made a profile link.GenesisCityData
We had to add a new Rect[] called EXTENDED_WORLD_BOUNDS because when doing the check of
IsInsideBounds
, the rect.Contains excludes limit values, so we had to extend the bounds so now it includes them.Summon Instructions
Please follow the instructions in these PRs for testing the different features:
Mentions
Hyperlinks -> with the caveat that only http/https web addresses are recognized now.
Unread Messages
Member List