Chatter
@fieldia/chatter puts a record's conversation beside its form, the way an ERP does: messages to the record's followers, notes for the team, files, reactions, replies and @mentions, what is to be done by whom and when, and who follows the record. Plain DOM, the same in every framework.
npm install @fieldia/chatter
Beside a form
A sheet whose sidePanel is the slot chatter shows it beside the record. chatterSlot fills it, following the form: it waits while the record is new, starts once it is saved, and fetches again after each save — when a backend posts what changed.
import { mountViewer } from '@fieldia/viewer';
import { chatterSlot } from '@fieldia/chatter';
mountViewer(host, {
page, dataSource, recordId: 7,
slots: { chatter: chatterSlot({ source, locale: 'en' }) },
});
In React, Vue and Angular a slot is a component of your own; it gives chatterSlot an element and the form:
function Chatter({ form }: SlotComponentProps) {
const box = useRef<HTMLDivElement>(null);
useEffect(() => chatterSlot({ source })(box.current!, { form }), [form]);
return <div ref={box} />;
}
<FieldiaForm page={page} dataSource={dataSource} recordId={7} slots={{ chatter: Chatter }} />
On its own, anywhere: mountChatter(element, { source, record: { model: 'sale.order', id: 7 } }).
As a work item's Discussion: chatterSlot({ look: 'discussion', source, me, placeholder, references, onCount }) draws a card to write a comment in — the person's picture, then the formatted text's own editor, its toolbar inside the box while it is worked in, Cancel and Save under it — and a card for each comment, newest first, “commented Jun 14” on each. Tracked changes are left to the record's history. Ctrl+Enter saves; Save with nothing typed says why; Escape asks before typed words go. onCount(count, form) is told how many comments there are after each load and each one saved, for a count in the record's head.
It fetches again once the record is loaded again — after your server action changed it — and posts what a post step or your app's answer posts. chatterAttachments(source) hands its files to a sheet's attachment preview; "sidePanelBeside": "always" keeps it beside the sheet on any screen it fits. See Around a record.
Your server, through a source
The chatter reaches your backend only through an object you write, as you write a data source. Two methods are all it needs; each other one adds a part, and a part whose method is missing is not shown.
| Method | Adds |
|---|---|
messages(record) · post(record, message) | The conversation: messages, notes and tracked changes, newest first, and the composer. |
upload(record, file) | Files: chosen or dropped, sent with a message; an image shows as a preview. |
react(record, messageId, emoji) | Reactions, turned on and off with a click. |
people(query) | @mentions, who an activity is for, and followers to add. |
activityTypes() · activities(record) · schedule · markDone · cancel | Activities: overdue, today and planned; scheduled, done with what came of it, or cancelled. |
followers(record) · follow · unfollow | Who follows the record. |
createMemoryChatter({ me, people, activityTypes, records }) keeps everything in memory, for demos, tests and prototypes — it is what the demos run on.
Its history, as a tab
historySlot fills a slot — a History tab — with the record's history, read from the same messages: events with tracking are its changes, messages its comments. On top, the State Graph: how the field stateField moved, in time order, each arrow with why above it (the new value of reasonField) and who and when under it, then the state it came to, its dot the colour the page's own dot gives it (its colours, else its tones). Under it, each change grouped by age — today, yesterday, this week, last week, this month, older — the one chosen beside the list with each field's new value and the one it replaced, struck through.
import { historySlot } from '@fieldia/chatter';
mountViewer(host, {
page, dataSource, recordId: 7,
slots: { history: historySlot({ source, stateField: 'state', reasonField: 'reason', kind: 'work item' }) },
});
The page places { "type": "slot", "id": "s-history", "name": "history" } in its History tab. The list is worked by keyboard: ↑ and ↓ move through the groups' headings and rows and choose, the change shown following; Home and End; Enter or Space on a heading folds its group. A filter at the list's heading shows field changes, comments or both. Under 760px the change goes under the list. On its own: mountHistory(element, { source, record: { model: 'work.item', id: 7 } }).
What it takes care of
- Safe to show. Message HTML is cleaned before it is shown — no scripts, no event handlers. What people type is posted as escaped text, a paragraph for each block.
- Its own keys. Ctrl+Enter in the composer sends the message; it does not save the record around it. Escape closes the composer, or the list of people first.
- Nothing in a form submits. Every button is a plain button, so a reaction inside a form never sends the form.
- Four languages, as Fieldia's own words are:
localeandlabels, as the viewer takes them.