-
-
Notifications
You must be signed in to change notification settings - Fork 830
Display favourite messages in a room-like view #9719
Display favourite messages in a room-like view #9719
Conversation
c9c8950
to
24ee66c
Compare
Work will continue on this in January |
24ee66c
to
5db674d
Compare
d6f3607
to
cfdf190
Compare
Prerequisite: matrix-org/matrix-analytics-events#76 |
f399b7a
to
d0ac969
Compare
d0ac969
to
86e5b1b
Compare
86e5b1b
to
7538692
Compare
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.
generally seems good, thank you!
src/components/structures/FavouriteMessagesView/ConfirmClearDialog.tsx
Outdated
Show resolved
Hide resolved
src/components/structures/FavouriteMessagesView/FavouriteMessagesPanel.tsx
Outdated
Show resolved
Hide resolved
lastRoomId = roomId!; | ||
} | ||
|
||
const resultLink = "#/room/" + roomId + "/" + mxEvent.getId(); |
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 appears to be an assumption of our url routing which isn't guaranteed - can we convert it to a view_room
dispatch instead?
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.
It's copied from here: https://github.com/matrix-org/matrix-react-sdk/blob/develop/src/components/structures/RoomSearchView.tsx#L255
I presume that could do with fixing too?
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 into the code, it seems that EventTile
does not currently support an onClick
or similar event, so the only way to allow clicking a tile to jump somewhere else is with its highlightLink
property as we have done here and in RoomSearchView
.
Given how long this PR has festered, I'd like to argue that fixing this is outside of the scope of this PR, and something we can do later.
src/components/structures/FavouriteMessagesView/FavouriteMessagesView.tsx
Outdated
Show resolved
Hide resolved
2a7bb84
to
3ab2417
Compare
i18n test is failing with
which I don't understand |
OK, CI seems happy so requesting a re-review from @t3chguy |
Scratch that - techguy is too busy today so I'll ask someone else. |
Played a bit around with it on the deployment here. I like it 👍 If you favourite a poll, it explodes with:
(same for broadcasts) You cannot jump to the room message, if you favourite an image, voice message or location share (click the message in the favourites view). The room separation in the favourite view could be improved. Maybe just add more spacing above the room names. If you favourite a thread root, the thread summary is shown in favourites. But clicking it does nothing. Ctrl + F (enabled in options) doesn't trigger search in the favourites view. It would be nice if the search field can get the focus if you click the 🔍 Room search with unexpected result, when hitting random keys on the keyboard. |
If I read correctly, this will save all favorite message ids in a single account data entry ( |
Because this PR started out using local storage, I totally forgot when I converted it to use SettingsStorage that there is an MSC we are planning to follow: matrix-org/matrix-spec-proposals#2437 Note: that MSC proposes using Room Account Data, but I plan to use general Account Data because I think favourites should not be tied to rooms. @bwindels I think that MSC also uses a single account data entry for everything - will that cause you similar problems? |
Thanks for the MSC link. I always forget about room account data :) The size of the array would indeed be more reasonable when using room account data, as well as the scope for races would be smaller. The race is indeed described as a limitation. The size of the array would indeed be more reasonable when using room account data). So yeah, the race is a discussion for the MSC.
But favourites are always for events, which can only occur within a room, no?
|
|
||
.mx_FavMessagesHeader_cancelButton { | ||
background-color: $alert; | ||
mask: url("$(res)/img/cancel.svg"); |
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.
We are trying to get rid of mask in favour of the svg
as a React component directly
Example of use:
https://github.com/matrix-org/matrix-react-sdk/blob/develop/src/components/views/rooms/wysiwyg_composer/components/FormattingButtons.tsx#L21
} | ||
|
||
.mx_FavMessagesHeader_sortButton::before { | ||
mask-image: url("$(res)/img/element-icons/room/sort-twoway.svg"); |
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.
Same comment about the mask
} | ||
|
||
.mx_FavMessagesHeader_clearAllButton::before { | ||
mask-image: url("$(res)/img/element-icons/room/clear-all.svg"); |
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.
Same comment about the mask
color: $primary-content; | ||
font-weight: $font-semi-bold; | ||
font-size: $font-18px; | ||
margin: 0 8px; |
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.
We can use here $spacing-8
mask-position: center; | ||
mask-size: 17px; | ||
padding: 9px; | ||
margin: 0 12px 0 3px; |
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.
Use $spacing-*
let lastInSection = true; | ||
const nextEv = props?.timeline[j + 1]; | ||
if (nextEv) { | ||
lastInSection = |
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.
const lastInSection = nextEv &&
( dateSeparator(mxEv, nextEv) ||
mxEv.getSender() !== nextEv.getSender() ||
!shouldFormContinuation(mxEv, nextEv, context?.showHiddenEvents, threadsEnabled)
)
recalcEvents(); | ||
}, [searchQuery, recalcEvents]); | ||
|
||
registerFavouritesChangedListener(() => recalcEvents()); |
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.
At every render, it will call the registerFavouritesChangedListener
and add listener
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.
Do you have an alternative suggestion?
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.
We can add the listener in a useEffect
:
useEffect(() => {
const handler = () => recalcEvents();
registerFavouritesChangedListener(handler);
return () => {
// here we would like to unregister the handler
unregisterFavouritesChangedListener(handler);
}
}, [registerFavouritesChangedListener, recalcEvents]
If we want to avoid to repeat this behaviour every time we use the useFavourites
hook. We can pass the handler as argument of the hook and make all the register/unregister in it.
const displayedFavourites: FavouriteStorage[] = filterFavourites(searchQuery, favouriteMessages); | ||
const promises: Promise<MatrixEvent | null>[] = displayedFavourites.map((f) => fetchEvent(f, matrixClient)); | ||
const events = await Promise.all(promises); | ||
return events.filter((e) => e !== null) as MatrixEvent[]; // force cast because typescript doesn't understand what `filter` does |
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.
We can use https://stackoverflow.com/questions/43118692/typescript-filter-out-nulls-from-an-array this kind of predicate to cleanly filter without casting the type
useFavouriteMessages(favouriteMessagesStore); | ||
const [, forceRefresh] = useState([]); | ||
|
||
registerFavouritesChangedListener(() => forceRefresh([])); |
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.
It seems to be a bit hacky here, maybe useFavouriteMessages
should return something to trigger the rerender ?
} { | ||
const [, setX] = useState<string[]>(); | ||
const myListeners: (() => void)[] = []; |
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.
At every render, myListeners
is replaced by an empty array which seems to be unwanted behaviour.
It needs to be a useState
or useRef
Yes, but I argue that: a) When I want to view my favourites I don't want to iterate all rooms |
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.
Some minor findings in the code.
align-items: center; | ||
min-width: 0; | ||
margin: 0 20px 0 16px; | ||
padding-top: 8px; |
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.
Can you use spacing variables, where possible?
Like for instance here $spacing-8
.
@@ -0,0 +1,57 @@ | |||
/* | |||
Copyright 2022 The Matrix.org Foundation C.I.C. |
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.
It's already 2023 🍾
} | ||
|
||
const FavouriteMessageTile: FC<IProps> = (props: IProps) => { | ||
let context!: React.ContextType<typeof RoomContext>; |
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.
context
doesn't seem to be set anywhere.
height={26} | ||
resizeMethod="crop" | ||
/> | ||
<span>Favourite Messages</span> |
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.
String missing translation.
(also strings in some other places)
); | ||
}; | ||
|
||
export default ConfirmClearFavouritesDialog; |
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.
Applies to the entire PR: We should use named exports.
} | ||
|
||
const FavouriteMessagesPanel: FC<IProps> = (props: IProps) => { | ||
const favouriteMessagesPanelRef = useRef<ScrollPanel>(); |
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 should also solve the TS error:
const favouriteMessagesPanelRef = useRef<ScrollPanel>(); | |
const favouriteMessagesPanelRef = useRef<ScrollPanel | null>(null); |
* new instance, anyone who is listening to a different instane will not | ||
* be notified about changes to this instance. | ||
*/ | ||
public static get instance(): FavouriteMessagesStore { |
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 is a new store. Can we directly make it available via the SdkContext and get rid of the static instance here?
content: IContent; | ||
} | ||
|
||
interface IState { |
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.
interface IState { | |
interface State { |
as per new code style
Not too picky! Well spotted. |
This is all awesome feedback - thank you @weeman1337 @florianduros @bwindels ! I was hoping to squeeze the work to get this PR into shape in between other things, and given how much work there is left to do, I don't think I can do that right now :-( So, any of the following would be much appreciated:
Thanks! |
Not currently being worked on, so moving to draft. |
Originally developed by @yaya-usman but remodelled quite a bit now by @andybalaam
This creates a new view that you can access in a similar way to a room:
It displays all your favourite messages.
Apologies for the huge review. Do ask me and I can walk you through it interactively.
Here's what your changelog entry will look like:
✨ Features