Commit Graph

4870 Commits

Author SHA1 Message Date
renovate[bot] 09085c8bb8 Update dependency element-hq/compound-design-tokens to v11.1.0 2026-10-07 08:45:48 +02:00
Doug 93825c01ad Initial implementation of AccountSettings. (#6275)
* Make the UserPreference macro also generate an accessor for the key name.

* Initial implementation of AccountSettings.

- Add an AccountUserDefaults storage with a userID prefix on the key.
- Migrate the 2 values reset by AppSettings.resetSessionSpecificSettings to AccountSettings.

* Rename UserSettings.volatile() to UserSettings.mock()

Given it now has a userID parameter with a default value..
2026-10-06 17:52:24 +01:00
Stefan Ceriu acd18f3aa9 Fix location sharing double sheet presentation on the Mac
Which would override each other and break the UI state
2026-10-06 17:40:19 +03:00
mxandreas 4640f9cec0 Add hooks for room creation and privacy & security settings screen. (#6270)
Add hooks for the create room and security and privacy screens.
2026-10-06 09:05:49 +00:00
Mauro 60c11c8172 Add the multi-account announcement screen (#6263)
* Add the multi-account announcement screen

A self-sizing bottom sheet telling users they can now add multiple accounts, matching the designs. Close and Add account both report back to the coordinator; nothing presents it yet.

* Use the Localazy strings for the multi-account announcement

The copy now lives in Localazy as screen.multi_account_announcement.*, named after the screen rather than the room list.

* Apply review suggestions to the multi-account announcement

- Use an ElementNavigationStack with a toolbar close button, like RedactConfirmationView, instead of an overlaid glass button.

* Remove the large text preview from the multi-account announcement

Other sheets built the same way don't snapshot Dynamic Type sizes, and the iOS 26 bar material it would show isn't rendered in snapshots.

* Use TitleAndIcon for the multi-account announcement

TitleAndIcon now takes an attributed subtitle (#6274), so the sheet no longer needs to rebuild its layout by hand. The snapshots are unchanged.
2026-10-05 18:03:19 +02:00
Mauro Romito 1b06ca0dcd Let TitleAndIcon take an attributed subtitle
Add an AttributedString subtitle initialiser to Compound's TitleAndIcon,
with the existing String initialiser forwarding to it so current call
sites are unchanged. Add a preview state with a bold run and re-record
the titleAndIcon snapshots.

This lets screens with partially bold copy, such as the multi-account
announcement, use TitleAndIcon instead of rebuilding its layout.
2026-10-05 16:04:22 +02:00
Mauro 8a6e297cba Media preview single stale refresh (#6272) 2026-10-05 14:35:16 +02:00
Mauro 49457f7ae8 Multi account feature flag (#6261)
* Add a multi-account feature flag

The flag lives in Developer options. Switching it on arms the upcoming one-time multi-account announcement, whose seen state defaults to true so fresh installs never see it.

* Add an Add account row to Settings behind the multi-account flag

The row sits in its own section under the user status, as in the designs. Tapping it is routed up to the settings flow, which ignores it until the add account flow exists.

* Remove the Developer options view model tests

The Developer options screen is excluded from coverage, so these tests don't add value.

* Apply review suggestions to the multi-account flag

- Order the multi-account requirements in DeveloperOptionsProtocol to match the screen's sections.
- Drop the redundant "Experimental" from the toggle's subtitle.
2026-10-05 13:04:16 +02:00
Doug 9ce3bd97d8 Move the status emoji placement on the home screen. (#6260)
* Minor tweaks to User Status.

- Change the layout of the status emoji on the Setting button.
- Remove the status emoji from the Settings screen (it's shown directly below in the status row).
2026-10-05 10:18:23 +01:00
ElementRobot 57f5498da0 Translations update (#6268)
Co-authored-by: Element CI <ci@element.io>
2026-10-05 10:01:50 +01:00
Stefan Ceriu 9f141585a2 Focus events on the live timeline when it has already loaded them
Opening a room on an event always built a detached timeline, even when the
live timeline already contained that event, e.g. when tapping the notification
for the newest message in a room.

The timeline controller now stays on the live timeline in that case, scrolling
to and highlighting the event, and only builds a detached timeline when the
event isn't loaded.
2026-10-02 16:52:19 +03:00
Stefan Ceriu 52b411e954 Revert "Open the room live when tapping the notification for its newest message"
This reverts commit 4c71cae0f.

Comparing against other delivered notifications doesn't reflect the timeline:
in rooms set to mentions and keywords, after swiping away newer notifications
or with messages sent from another device, newer messages can exist without a
notification, so the room opened live with the tapped event out of view.
2026-10-02 16:52:19 +03:00
Stefan Ceriu a50c80c179 Avoid flashing the room list skeletons on launch
On a cold launch the home screen appeared just before the cached rooms
published, briefly showing skeletons that were immediately replaced.

The home screen now starts in a new awaitingCachedRooms mode, keeping
the list blank while a cached room list is about to publish and falling
back to the skeletons after 500ms. Without a cache the skeletons show
straight away as before.
2026-10-02 16:37:22 +03:00
Mauro 4b6953e80f Point agents to Apple's Xcode agent skills in AGENTS.md (#6256) 2026-10-02 15:00:16 +02:00
Copilot c41a38159b Only offer joined rooms in the forward and share pickers (#6255)
* Initial plan

* Offer only joined rooms in destination pickers

Co-authored-by: Velin92 <34335419+Velin92@users.noreply.github.com>

* Clean whitespace in share picker test

Co-authored-by: Velin92 <34335419+Velin92@users.noreply.github.com>

* Restrict the shared picker room list to joined rooms

The alternate room summary provider, which backs the forward, share and
Add rooms pickers and global search, now always includes the SDK's
joined filter in its base filter. This replaces the per-call joinedOnly
flags, so Add rooms no longer offers invited rooms or spaces and no
screen can forget the flag.

A filter set before the provider has its room list is now applied once
the list is set, instead of being reset to the default. The share picker
hit this on a cold launch from the share extension.

The forward and share pickers also hide upgraded rooms, matching the
Suggestions section. The SDK has no filter for these.

Also registers the share picker's test file in the project, fixes the
blank-line indentation SwiftFormat flagged, and re-records the picker
snapshots without the upgraded room.

* Check room membership for forwarding suggestions and search breadcrumbs

RoomSummary now has isJoined, built from the room's membership. The
forwarding suggestions and the search breadcrumbs used a missing
joinRequestType to mean joined, which is also the case for rooms that
have been left or banned from. It only worked because the room list's
base filter already drops those.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Velin92 <34335419+Velin92@users.noreply.github.com>
Co-authored-by: Mauro Romito <mauro.romito@element.io>
2026-10-02 12:44:33 +00:00
Stefan Ceriu 4fe58c2d0a Don't hold the room list on skeletons for an empty filtered result
The room list keeps showing skeletons while the provider reports loaded
but no summaries have published yet, to avoid flashing an empty list
before the first batch lands. That guard also fired when a filter
(chips, space or search) legitimately matched nothing, leaving the home
screen on placeholders forever instead of showing the empty filter
state.
2026-10-02 15:11:22 +03:00
Mauro d3604e8115 Announce toasts with VoiceOver (#6250)
* Announce toasts with VoiceOver

Toasts are posted from a separate overlay window and never take
VoiceOver focus, so their text was never spoken. UserIndicatorController
now posts a high priority announcement with the title whenever a toast
with readable text is submitted, including re-submissions of an already
visible toast with the same ID. Modal indicators are not announced.

* Remove empty-title toast announcement check

Co-authored-by: Velin92 <34335419+Velin92@users.noreply.github.com>

* Hide toasts from VoiceOver

The toast title is now announced by UserIndicatorController when the
toast is submitted, so letting VoiceOver also focus the toast itself
would only read the same text twice.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-10-02 12:29:18 +02:00
Mauro f91b7441e7 Suggest recently visited rooms when forwarding messages (#6251)
* Suggest recently visited rooms when forwarding messages

The Forward to screen now shows up to 5 recently visited rooms in a
Suggestions section, above a Chats section that lists every room. The
suggestions are never searched: while searching, only the Chats section
is shown and filtered. The source room, invites, spaces and upgraded
rooms are never suggested, and the section is hidden when there is
nothing to suggest.

The suggestions are resolved from the room summaries rather than room
proxies, so they show up without holding back the list, and they are
re-resolved as the room list loads after a cold start. ClientProxy now
exposes the recently visited room IDs for this.

The recently visited rooms filter is now async, so callers choose its
isolation instead of asserting it at runtime.

* Apply review suggestions to the forwarding suggestions

Rooms:
- Each room is listed once. Suggested rooms are left out of Chats unless
  searching, where Chats lists every result. This also removes the
  duplicated row whose selection changed without the tapped row's
  animation.
- The Chats header is hidden when the section has no rooms.

Suggestions:
- Resolve them once when the screen opens instead of on every room list
  update.
2026-10-02 12:04:07 +02:00
Mauro Romito e67a4c388f Read the number of selected messages to VoiceOver
The count was only on the forward button at the bottom, so VoiceOver
users had to go and find it after every tap. While selecting, each
selectable message now starts its label with "N messages selected".
The label changes on every toggle, so VoiceOver reads the new count
followed by the message, as in other messaging apps.
2026-10-01 21:39:47 +02:00
Mauro Romito 8ddd161625 Keep message selection active when every message is unselected
Unselecting the last message used to end the selection, closing it
without the user asking. Selection mode is now its own state: Forward
starts it and it only ends with the close button, once the messages are
forwarded, when the timeline changes or the feature is turned off. With
nothing selected the forward button stays visible but disabled.
2026-10-01 17:43:42 +02:00
Stefan Ceriu cdb5d4ee31 Group dependencies shared by Compound and the app in one Renovate PR
swift-snapshot-testing is pinned in both compound-ios/Package.swift and
project.yml. Renovate updated them from different groups, so one side got
bumped without the other and the Xcode build broke on the mismatched
Package.resolved. A dedicated "Shared Dependencies" group updates both
together, and since that PR touches project.yml the XcodeGen workflow
regenerates the project as well.
2026-10-01 16:37:26 +03:00
Stefan Ceriu 18b0975c99 Run all Renovate dependency groups on the same schedule
Tool and Compound dependencies had no schedule while project dependencies
were only updated every 2 weeks, so their PRs landed at unrelated times.
Put them all on the same biweekly Wednesday slot and rename the Compound
group to "Compound Dependencies" for consistency with the others.
2026-10-01 16:37:26 +03:00
Stefan Ceriu f084274acb Use current Renovate option names for the Compound rule
`managers` and `matchPaths` are deprecated in favour of `matchManagers`
and `matchFileNames`. Renovate was migrating them on the fly, this makes
the config match what it actually runs.
2026-10-01 16:37:26 +03:00
Stefan Ceriu a4ff68ed84 Bump swift-snapshot-testing in the main project too 2026-10-01 16:37:26 +03:00
renovate[bot] 9a62206a4f Update dependency pointfreeco/swift-snapshot-testing to v1.19.6 2026-10-01 16:37:26 +03:00
Stefan Ceriu 11dc19f7ed Await the session restore instead of polling for it
Replace the 100ms polling loop in the background app refresh with a
subject that the state machine's transition handler sets as soon as the
app is no longer restoring a session, whatever the outcome. The refresh
now awaits it once, with the same 5 second timeout, instead of waking
up repeatedly.

Move the wait back into the class body and silence type_body_length for
AppCoordinator rather than keeping a single-function extension around
to stay under the limit.
2026-10-01 15:59:42 +03:00
Stefan Ceriu a56a5c2819 Wait for the session restore in background app refresh
When iOS relaunches a terminated app for the task, the session is still
being restored asynchronously when the refresh starts. Wait up to 5
seconds for the restore to finish, bailing out early if the app isn't
restoring a session (e.g. signed out or the restore failed).

# Conflicts:
#	ElementX/Sources/Application/AppCoordinator.swift
2026-10-01 15:59:42 +03:00
Stefan Ceriu 38312b7473 Fix the search tab's layout now that its built against os27 2026-10-01 15:59:16 +03:00
Mauro Romito b65e8f6a7c Fix formatting-only edits not rendering in the timeline
Components of a formatted body were identified by their plain characters, so an edit
that only added formatting (e.g. wrapping a word in inline code) kept the same ID.
SwiftUI then reused the existing MessageText, whose @State string ignores new input,
and the old unformatted text kept rendering.

Identify components by the hash of their attributed string so formatting changes
produce a new ID.
2026-10-01 14:56:53 +02:00
Mauro Romito 7911df9537 Fix the media viewer getting stuck on "Loading more…" when swiping forward
QuickLook requests every page up front when the viewer opens. At that point the
media timeline is still empty, so every page after the tapped media is cached as
a forward pagination placeholder. Once the timeline loads it reports forward as
end reached, so no pagination happens and nothing ever replaces those cached
placeholders, leaving the viewer stuck when swiping forward.

When landing on a placeholder, or when the timeline updates, the controller now
checks whether the data source holds a different item at that index and if so
refreshes the current page. The refresh waits for any swipe to settle, as
refreshing mid-swipe breaks QLPreviewController.
2026-10-01 14:31:29 +02:00
Mauro Romito 61384f9986 Use the window scene initialiser for UIWindow in tests
UIWindow.init() is deprecated in iOS 26. AuthenticationStartScreenViewModelTests
now creates its fake window from the test host's connected scene, matching
ServerSelectionScreenViewModelTests.
2026-10-01 10:56:08 +02:00
Mauro Romito 26c417ae68 Move AlertInfo's button types out of the generic type
With Swift 6.4, the default value for AlertInfo.primaryButton warned that
T's Hashable conformance may be isolated and can't be used in a main
actor-isolated context. The nested AlertButton/AlertTextField types
don't depend on T, so move them to top level as AlertInfoButton and
AlertInfoTextField, and keep typealiases so existing call sites compile.

Constraining T to SendableMetatype isn't possible as the synthesized
Hashable conformances of our main actor-isolated alert enums are isolated.
2026-10-01 10:55:40 +02:00
Mauro 831234865b Fix Swift 6.4 warnings (#6240) 2026-10-01 10:55:19 +02:00
Mauro Romito 99c0fa5729 Title the forwarding screen "Forward to"
The forwarding screen now picks destinations for one or several
messages, so "Forward message" no longer fits. Use the common_forward_to
string from Localazy, as in the designs, and re-record its snapshots.
2026-09-30 17:40:55 +02:00
Stefan Ceriu b21d27dbbc Jump straight to notification tap and external deep link targets
Whether to animate a route was decided after resuming the client
services, by which time a notification tap or an external deep link
had already made the app active, so the user watched a navigation
animation from the room list. Read the app state as soon as the route
arrives instead, so these land directly on their target while routes
handled in the foreground keep animating.
2026-09-30 17:06:57 +03:00
Stefan Ceriu a87d65b05a Surface failed reactions as a send failure on their message
A reaction that fails to send can now block the room's send queue, but the
timeline only surfaced failures for the item itself, its edit or its
redaction. EventTimelineItemProxy.pendingSend now also falls back to our
first failed reaction, targeting it with SendTarget.reaction(key:), so the
message shows the red exclamation mark and the sending failed alert can
retry or remove it.

In-flight reactions are deliberately ignored so reacting doesn't flash a
sending indicator on the message.
2026-09-30 15:16:13 +03:00
Stefan Ceriu 4c71cae0f5 Open the room live when tapping the notification for its newest message
Tapping a message notification always opened the room on a timeline
focussed on that event, even when it was the newest message and the
live timeline already ends with it.

On tap, the notification manager now checks whether any other delivered
notification for the same room is more recent. If none is, the room is
opened live instead of focussed. Older notifications in a room keep
focussing on their event.
2026-09-30 14:50:04 +03:00
Stefan Ceriu 7973da8251 Throw from session directories and only fatal error when making sense. 2026-09-30 12:24:12 +03:00
Stefan Ceriu a8bc26dc93 Fix various UI test snapshot issues. 2026-09-30 12:23:54 +03:00
Stefan Ceriu 5a3c4ad2f5 Make the background app refresh task live in its own extension as well 2026-09-30 12:20:54 +03:00
Stefan Ceriu 49481f9af7 Add a search backfill app background processing task
To run when the device is idle, charging and connected to the network.
2026-09-30 12:20:54 +03:00
Stefan Ceriu d08b0e0090 Start a foreground search backfill request when entering the message search tab 2026-09-30 12:20:54 +03:00
Stefan Ceriu 790bf0a44e Adopt the new search backfill client interfaces. 2026-09-30 12:20:54 +03:00
Valere 8aae06d3ae Bump element-call-ios version to 0.1.0-rc.9 2026-09-30 08:20:36 +03:00
Stefan Ceriu ec4d1a3c04 Update some icons to use the primary accent color instead of tertiary for accessibility reasons. 2026-09-29 22:11:16 +03:00
Stefan Ceriu 0de6231399 Give search breadcrumbs a consistent row height
Query and room breadcrumbs reserved slightly different heights, and
rooms without a description collapsed further. Both now reserve the
space of a title and subtitle so every row lines up.
2026-09-29 21:53:04 +03:00
Stefan Ceriu a78e4f0c2a Add a clear all button to recent searches
Lets users wipe their whole search history at once from the recent
searches section header, as per the designs.
2026-09-29 21:53:04 +03:00
Stefan Ceriu 50b38555b6 Allow removing individual search breadcrumbs
Each recent search now has a trailing close button to remove it from
the history. The view action is logged without its payload so removed
queries don't end up in the logs.
2026-09-29 21:53:04 +03:00
Stefan Ceriu 313365ad83 Only record query breadcrumbs when the search is submitted
Queries were previously stored whenever a result was selected, which
meant half-typed queries ended up in the history. Now a query is only
recorded when the user submits the search field, while selecting a
result only records the room.
2026-09-29 21:53:04 +03:00
Stefan Ceriu bc018251c7 Update translations to bring in action.clear_all 2026-09-29 21:53:04 +03:00