Typed activity timeline for a contact or record, with category tabs, a type filter, relationship summary, upcoming tasks, an emit-only Add menu and a detail sheet with replies and reactions.
Category tabs (All / Communication / Tasks / Updates) with counts, plus a filter button that opens the grouped type filter in an action sheet.
Summary row: last contacted, next activity, open task count, the Ask Docy button (renderAiAssistant) and the + Add menu (addActions + onAddActivity).
Upcoming tasks and meetings. Task checkboxes call onToggleUpcomingTask.
Timeline: calendar day groups (Today, Yesterday, This week, Last week, This month, Last month, Earlier this year, Last year, Older) on a continuous rail. Communication types render as cards (title, preview, time range, attachment and reply counts, participant avatars). Tasks and updates render as one-line rows with an attachment marker. The list is virtualised with FlashList.
Detail sheet (large action sheet): type chip, time range and full timestamp, a Details block of the activity's own metadata (call direction / duration, meeting location, email recipients, task status / priority / due date, …), participants, body, summary, key points, follow-ups (onToggleFollowUp), document cards (image thumbnails, per-type icons, tap to open), related records, a reaction row, the reply thread for comments, the primary action and Delete.
The Add menu emits onAddActivity(type). Pass renderAddForm as well and the panel opens your form for the picked type in its own sheet — typically a LogActivityForm scoped with activityTypes={[type]}. Call close() after a successful submit. With a single addActions entry the trigger skips the menu and opens the form directly.
Timeline timestamps count up while the event is recent and become a date once it isn't: now → 5m → 3h → 2d → 3w → 21 Sep → 21 Sep 2025. The units are translation keys (ui.contactActivity.time.*) and the absolute form uses the device locale.
Connect it to Docyrus with useDocyrusContactActivity, which is also re-exported from this component.
Without maxHeight the panel is flex-1 and fills its parent, so give the parent a bounded height (a screen, or a sized View). Do not nest it in a vertical ScrollView. Pass maxHeight for a fixed-height panel.
For comment activities the detail sheet loads replies with onLoadReplies(activityId) and renders them with the team-chat PostCard / PostComposer (as on web): @ mentions, emoji, attachments and — with dataSources + onSearchEntity — entity links. Submitting calls onCreateComment({ content, parent_id, attachments, linked_entities, mentioned_user_ids, hashtags }). parent_id is the raw comment id taken from metadata.__sourceId. Own replies can be deleted after confirmation (onDeleteComment).
With onToggleReaction, the sheet shows reaction chips for the activity and for each reply. Tap the smiley / + button, or long-press a reply, to pick one of the preset emoji (or More for the full EmojiPicker). targetId is the reply id, or the activity's metadata.__sourceId (falling back to its id).
Editing replies is not offered (the panel has no update callback — same as web).
The panel was rewritten as the web typed timeline.
ActivityItem { id, type, title, description?, timestamp, user, metadata? } was replaced by ContactActivity (subject, created_on, created_by resolved against users, reply_count, …).
ActivityType grew from 6 to 18 members. status_change was removed. Use status_update or status.
The built-in "Add activity" form was removed. onAddActivity is now emit-only, (type: string) => void, instead of ({ type, title, description }). Open a LogActivityForm from it.
emptyMessage and style were removed. The empty state is translated (ui.contactActivity.*) and uses contactName. Style the panel with className.
Without maxHeight the panel is flex-1 and must not be nested in a vertical ScrollView.