Document RoomMemberModerationState, RoomMemberModerationEvents and
RoomMemberModerationRenderer along with their members and parameters.
The renderer doc states that it draws nothing until the host screen asks for a
member's actions, and that onSelectAction exists so the host can navigate for the
actions that need a screen of their own.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Document PollContentStateFactory, SendPollResponseAction and EndPollAction along
with their members and parameters.
The two action interfaces exist to wrap a plain timeline call, so their docs say
what they add: both capture an analytics event on success, which is the reason
callers should go through them rather than calling the timeline directly.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Document LeaveRoomState, LeaveRoomEvent and LeaveRoomRenderer.
The renderer doc records the two things a host screen needs to know, both taken
from LeaveRoomPresenter and LeaveRoomView: it draws nothing until a leave event is
sent, and onSelectNewOwners fires specifically when the user is the last owner of
a room that still has other members.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Document InvitePeopleState and each of its properties, InvitePeopleEvents,
InvitePeopleRenderer and the presenter factory.
The state properties now say what drives them, in particular that
createRoomFromDmAction covers the case of inviting a third person into an
existing direct message, and that the factory accepts a null room for that flow.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Document HtmlConverterProvider, MessageComposerContext,
PinnedEventsTimelineProvider, the MessagesEntryPoint node proxy and the voice
message composer factory.
HtmlConverterProvider carries the ordering constraint that matters: the converter
is rebuilt from the Compose theme, so Update has to be composed before provide is
called.
MessagesEntryPoint and its Callback are navigation plumbing and out of scope.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Document EnterpriseService, SessionEnterpriseService and
RemoteEnterpriseConfigProvider along with all their members and parameters.
Two points that callers get wrong are stated explicitly: these services return
permissive defaults on a standard build, so there is no need to branch on the
build type, and SessionEnterpriseService only answers meaningfully once init has
run. The many nullable getters now say what null means in each case, which is
generally "no override, use the app default".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was added as a temporary measure to allow matrix.org users to use Element X before it supported MAS, but it hasn't been needed for a very long time
The GITHUB_MAX_ISSUE_BODY_SIZE constant added with the description truncation
is a five-digit literal, above detekt's acceptableLength of 4, so
:features:rageshake:impl:detekt failed. Write it as 65_536, matching
MAX_LOG_LINES_SIZE in RageshakeConfig.
The timeline action sheet offered Forward on a live location share, but
forwarding one always failed: Mode.Live maps to the SDK's
MsgLikeKind.LiveLocation, which RoomContentForwarder rejects outright. So
the action was shown for exactly the set of events on which it could
never work.
canBeForwarded() now returns true for a location event only when its mode
is Static. Ended live shares keep Mode.Live and fail for the same reason,
so they are excluded too. ActionListPresenter needs no change: its check
is the function's only call site in the repo, so the single gate covers
the timeline sheet, the threaded timeline and the pinned messages list.
Hiding the action mirrors Element Web and the sticker exclusion already
present in the same function. Converting a live share into a static
m.location on forward would be a product decision and is out of scope.
Relates to https://github.com/element-hq/element-x-android/issues/7190
The whole crash call stack was appended to the bug report description, and the
bug report server copies that description verbatim into the GitHub issue it
opens. GitHub refuses an issue body over 65536 characters, so the server's save
step failed and answered "Could not save report". The stored crash data is only
cleared after a successful send, so every retry failed the same way.
Truncate the description before sending it. The user's own text and the GitHub
issue link come first, so only the tail of the crash stack is dropped. The
uncaught exception handler already writes the full stack through Timber, so it
stays available in the attached log files.
The 40 KB budget leaves room for the other reported fields, mainly the list of
omitted log file names, which is not otherwise bounded.
* Expand the emoji picker to full height when the keyboard is shown
The picker content is rendered with fillMaxSize, so the sheet measures itself to
the full window height. That makes the partially expanded anchor available, and
the modal sheet auto-shows at that anchor, half way down the screen.
The sheet reserves the IME inset through its content window insets, but applies
that padding at its own bottom edge, which is off-screen while it rests half way
down. The band that is both on-screen and above the keyboard is then only about
a tenth of the screen, which is why the matching emoji end up behind it.
Expand the sheet while the keyboard is visible so its bottom edge returns to the
screen edge and the inset it already reserves lands on top of the keyboard.
Modifier.imePadding on the sheet content is not an option: the sheet applies the
insets with windowInsetsPadding, which consumes them, so a nested imePadding
reads zero.
* Cover the keyboard driven sheet expansion with a test
The IME reaction moves into ExpandSheetWhenImeIsVisible so a Robolectric test can
drive it: dispatching window insets to the composition flips
WindowInsets.isImeVisible, and the sheet state is observable from the test. Both
the shown and the hidden case are asserted, which is the patch coverage this
change was missing.
* Do not leave a blank strip below the expanded emoji search
The 10dp of bottom padding on the search bar separates the collapsed
field from the category tabs underneath it. It stayed applied once the
search bar expanded to fill the picker, so the picker stopped 10dp short
of its own bottom edge.
That strip used to sit below the visible part of a half height sheet.
Now that the sheet expands against the keyboard it reads as a gap
between the results and the keyboard, so only pad while collapsed.
heroes is populated for any room whose name is derived from its members,
not only for direct messages, so a two person room that is not a DM
showed a member's Matrix ID as its subtitle.
Carry isDm on SelectRoomInfo and read the hero only when it is set. The
canonical alias still takes precedence, so nothing changes for a room
that has one.
Restore the snapshot backed set the store started with and declare
allowedEventIds as SnapshotStateSet so the interface says out loud that
reads of it are observable.
TimelineProtectionPresenter reads the set inside derivedStateOf, so a
plain set silently stops the timeline from re-rendering when an event is
revealed. Naming the type keeps that requirement from being mistaken for
an implementation detail.
* Show a Message not found placeholder when a replied to message cannot be loaded
When the replied to event could not be fetched, the reply preview rendered the
raw SDK error string in critical styling, and tapping it emitted a focus
request that could never succeed, so the user got a dead end dialog.
Render the localised error_message_not_found string in the neutral italic style
already used for redacted and undecryptable replies, and drop the click handler
for that case. The existing key is reused rather than adding a new one; a "you
do not have access" wording was rejected because this state covers any failure
to load the event, not only a permission problem.
* Update screenshots
* Trigger CI after recording screenshots
---------
Co-authored-by: ElementBot <android@element.io>
The store no longer needs a mutableStateSetOf: an ordinary Set kept in a
mutableStateOf does the same job, and matches DefaultMediaPreviewConfigStateStore.
The Compose state itself has to stay. TimelineProtectionPresenter derives
ProtectionState from the store inside a derivedStateOf, so the set must be a
snapshot object for a reveal to invalidate it and reach the timeline. Backing it
with a plain mutableSetOf leaves the presenter with nothing to observe and it
stops emitting after ShowContent.
The pinned events timeline is built with an exclusion filter rather than an
allowlist, so an event of a type the app has no renderer for reaches both
pinned surfaces. The banner falls through to the formatter catch-all and reads
"Unsupported event", and the list renders an unknown content tile.
Drop unknown content and message-like or state events that failed to parse from
both surfaces, along with any day divider left without an event to introduce,
and show the empty state when nothing renderable remains. The spec makes this
the client's call. Such an event can still be unpinned from the room timeline,
where it is still rendered.
Undecryptable and redacted events are untouched: they carry information the
user needs.