Vytváření aplikací (pluginů)

Aplikace přidávají funkce do Antimatteru zevnitř: serverová část v Go, která se napojuje na server, a webová část v React, která přidává nabídky, panely, typy zpráv a stránky. Tato stránka ukazuje, jak je aplikace strukturována, s využitím přiložené aplikace Polls jako příkladu, a co se mění v rozhraní Fusion v Antimatteru.

Jak aplikace funguje

  • Serverová část je program v Go. Server ho spustí jako samostatný proces a komunikuje s ním přes RPC: volá hooky aplikace (zpráva má být odeslána, byl spuštěn příkaz…) a aplikace volá plugin API serveru (vytvořit příspěvek, přečíst uživatele, uložit data…). Může také sloužit HTTP cesty pod /plugins/<app id>/.
  • Webová část je JavaScript balíček načtený do prohlížeče spolu s webovou aplikací. Registruje React komponenty a callbacky v registru: tlačítko v nabídce, panel, způsob vykreslení typu zprávy.
  • Kterákoli z částí je volitelná. Aplikace se instaluje jako balíček .tar.gz a systémoví administrátoři ji zapínají, vypínají a konfiguruji.
Serverová část běží s plným přístupem k datům serveru. Aplikaci považujte za kód, který nasazujete na server.

Antimatter spouští aplikace psané pro Mattermost a naopak, pokud aplikace používá pouze hooky, které mají obě. Viz Hooky, které má pouze Antimatter.

Struktura balíčku

Neexistuje samostatná šablona pro začátek: začněte od malé přiložené aplikace, jako je antimatter-plugin-polls nebo antimatter-plugin-todo, a změňte její ID a kód. Aplikace Polls má tuto strukturu:

antimatter-plugin-polls/
├── plugin.json            the manifest
├── Makefile               build, test, bundle, deploy
├── build/                 the manifest and pluginctl helper tools (Go), setup.mk
├── assets/icon.svg        files shipped with the bundle
├── go.mod
├── server/
│   ├── main.go            plugin.ClientMain(&Plugin{})
│   ├── plugin.go          the Plugin type, OnActivate, ServeHTTP
│   ├── manifest.go        generated from plugin.json
│   ├── api.go, command.go, hooks.go, store.go …
│   └── dist/              compiled binaries, one per platform
└── webapp/
    ├── package.json, webpack.config.js, tsconfig.json
    ├── i18n/              en.json and one file per language
    ├── src/index.tsx      registers the app with the web app
    ├── src/manifest.ts    generated from plugin.json
    ├── src/translations.ts
    └── dist/main.js       the bundle

Volitelné: složka public/, která se servíruje beze změny na adrese /plugins/<app id>/public/.

plugin.json

{
  "id": "com.example.weather",
  "name": "Weather",
  "description": "Forecasts in the chat.",
  "homepage_url": "https://github.com/example/weather",
  "support_url": "https://github.com/example/weather/issues",
  "icon_path": "assets/icon.svg",
  "version": "1.0.0",
  "min_server_version": "12.0.0",
  "server": {
    "executables": {
      "linux-amd64": "server/dist/plugin-linux-amd64",
      "linux-arm64": "server/dist/plugin-linux-arm64",
      "darwin-amd64": "server/dist/plugin-darwin-amd64",
      "darwin-arm64": "server/dist/plugin-darwin-arm64",
      "windows-amd64": "server/dist/plugin-windows-amd64.exe"
    }
  },
  "webapp": {"bundle_path": "webapp/dist/main.js"},
  "settings_schema": {"settings": []}
}
  • id: jedinečné a trvalé; obvykle se používá obrácené doménové jméno. Určuje názvy cest, uložených nastavení a WebSocket událostí.
  • version: sémantická verze. Přiložené aplikace ji vynechávají v plugin.json; jejich nástroj pro sestavení ji bere z nejnovějšího git tagu v*.
  • min_server_version: Antimatter hlásí 12.0.0.
  • settings_schema: stránka nastavení aplikace v Systémové konzoli (viz Nastavení).

make apply zkopíruje manifest do server/manifest.go a webapp/src/manifest.ts; make manifest-check ho ověří.

Serverová část

Serverová část importuje stejný Go modul jako aplikace Mattermost, github.com/mattermost/mattermost/server/public, z Go modulu proxy. Její main pouze spustí aplikaci:

package main

import "github.com/mattermost/mattermost/server/public/plugin"

func main() {
	plugin.ClientMain(&Plugin{})
}

Typ Plugin obsahuje plugin.MattermostPlugin, což mu poskytuje p.API, a implementuje potřebné háčky:

type Plugin struct {
	plugin.MattermostPlugin
	router *mux.Router
}

func (p *Plugin) OnActivate() error {
	p.router = p.newRouter()
	return p.API.RegisterCommand(&model.Command{
		Trigger:          "weather",
		AutoComplete:     true,
		AutoCompleteDesc: "Show the forecast",
		AutoCompleteHint: "[city]",
	})
}

func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
	city := strings.TrimSpace(strings.TrimPrefix(args.Command, "/weather"))
	return &model.CommandResponse{
		ResponseType: model.CommandResponseTypeEphemeral,
		Text:         "Forecast for " + city + ": sunny",
	}, nil
}

func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) {
	p.router.ServeHTTP(w, r)
}

Běžné háčky

HáčekKdy se spustí
OnActivate, OnDeactivateKdyž je aplikace zapnuta nebo vypnuta, a když se server spouští a zastavuje. Příkazy a boty registrujte v OnActivate.
OnConfigurationChangeKdyž se změní její nastavení; načtěte je pomocí p.API.LoadPluginConfiguration(&cfg).
ServeHTTPPožadavky na /plugins/<app id>/…. Server nastaví hlavičku Mattermost-User-Id, když požadavek pochází od přihlášeného uživatele; požadavky bez ní zamítněte, pokud nemají být veřejné.
ExecuteCommandByl spuštěn příkaz, který aplikace registrovala.
MessageWillBePosted, MessageWillBeUpdatedPřed uložením zprávy: vraťte upravený příspěvek nebo důvod pro jeho zamítnutí.
MessageHasBeenPosted, MessageHasBeenUpdated, MessageHasBeenDeletedPo události, pro vedlejší efekty.
ReactionHasBeenAdded, ReactionHasBeenRemovedReakce.
UserHasJoinedChannel, UserHasLeftChannel, UserHasJoinedTeam, UserHasLeftTeam, ChannelHasBeenCreatedZměny členství a kanálů. Tým je komunita.
FileWillBeUploadedPřed uložením souboru: změňte ho nebo ho zamítněte.
OnPluginClusterEventZpráva ze stejné aplikace na jiném serveru v clusteru.

Úplný seznam s přesnými podpisy najdete v server/public/plugin/hooks.go; API je v server/public/plugin/api.go. Balíček pluginapi (server/public/pluginapi) obaluje API v uživatelsky přívětivějších pomocných funkcích.

Užitečné volání API

  • CreatePost, UpdatePost, SendEphemeralPost: publikujte jako bot aplikace nebo jako uživatel.
  • EnsureBotUser(&model.Bot{Username, DisplayName}): jednou vytvoříte bot aplikace a získáte jeho ID.
  • PublishWebSocketEvent("poll_updated", payload, &model.WebsocketBroadcast{ChannelId: id}): pošle událost do webové aplikace. Přijde jako custom_<app id>_poll_updated.
  • OpenInteractiveDialog: otevře dialog.
  • LogInfo, LogError a podobné: zapisuje do serverového logu, označené ID aplikace.
  • GetUser, GetChannel, HasPermissionToChannel: zkontrolujte, kdo co smí, než budete jednat jménem uživatele.

Aplikace běží na každém serveru v klastru. Uchovávejte stav v klíčově-hodnotovém úložišti nebo v databázi, ne pouze v paměti.

Služba AI

Server Antimatter volá modely AI pro aplikace prostřednictvím připojení k modelům, která nastavil administrátor (viz AI v administrátorském průvodci). Aplikace nikdy neuchovává klíč poskytovatele: pouze udává, pro koho a kde jedná, a server zkontroluje, zda má tato osoba v daném místě přístup k připojení, uplatní jeho limity a rozpočty, započítá tokeny na osobu i aplikaci a zaznamená volbu do auditního deníku bez obsahu.

MetodaCo dělá
AIChat(actingUserID, channelID, teamID, *ai.ChatRequest)Doplnění chatu: zprávy, nástroje, strukturovaný výstup (JSONSchema).
AIChatStream(…)To samé, ale v proudu: čtěte Events proudu, dokud se kanál neuzavře, nebo ho Closete, aby se volba zastavila.
AIEmbed(…, *ai.EmbedRequest)Embeddingy textů.
AITranscribe(…, *ai.TranscribeRequest)Přepis řeči do textu.
AIDecide(…, *ai.DecideRequest)Typované dotazy na rozhodovací model (ano nebo ne, výběr, skóre) k textu.
AIConnectionsForUser(userID, channelID, teamID)Připojení, která osoba v daném místě může používat, včetně jejich modelů a funkcí, nikdy však klíčů.

ConnectionID každé žádosti vybírá připojení a Model jeden z jeho modelů (prázdné pro výchozí model připojení). Operation je krátká značka pro záznamy o využití a audit, nikdy neobsahuje obsah. Minimální verze serveru je 11.11.

Hodnotu actingUserID berte pouze ze zdroje, který server ověřil: hlavička Mattermost-User-Id v žádosti do vašeho ServeHTTP, nebo uživatel z kontextu háčku (autor příspěvku, uživatel, který spustil příkaz). Nikdy ji neberte ze zprávy, payloadu webhooku, URL ani z žádosti jiné aplikace: vaša aplikace by pak utratila přístup jiného uživatele.

Tyto metody a balíček ai (server/public/ai) existují pouze v kopii modulu od Antimatter, ne ve verzi publikované Mattermost. K jejich použití sestavujte proti server/public od Antimatter s řádkem replace ve vašem go.mod, např. replace github.com/mattermost/mattermost/server/public => ../antimatter/server/public. Taková aplikace běží pouze na Antimatter.

Poskytování nástrojů agentům

Aplikace nabízí své vlastní nástroje agentům pomocí RegisterAITools([]*model.AIToolDefinition): název každého nástroje, jeho titul, popis, vstupní JSON schéma a typ (read, write nebo external_write), který určuje, zda jej agent může spustit samostatně podle své politiky (viz Nástroje v průvodci pro administrátory). Oprávnění jsou kontrolována na argumentu kanálu nebo komunity v daném volání, nebo na serveru, podle toho, co nástroj vyžaduje. Když agent nástroj použije, server zavolá hook ExecuteAITool(c, *model.AIToolExecution) *model.AIToolResult s ověřeným uživatelem, který požadavek podal, v poli UserId: jednejte pouze s jeho oprávněními, nikdy s oprávněními agenta. Nástroje jsou pojmenovány ve formátu <app id>.<name>; minimální verze serveru je 11.11; tyto metody a hook existují pouze v server/public aplikace Antimatter, stejně jako výše zmíněná AI služba. Aplikace s ID antimatter nebo ty, jejichž ID začíná na mcp., nemohou registrovat nástroje.

Ukládání dat

Každá aplikace má ve databázi serveru vlastní klíčově-hodnotové úložiště:

  • KVSet(key, value), KVGet(key), KVDelete(key): hodnoty jsou bajty, obvykle JSON. Klíče mají maximálně 150 znaků.
  • KVSetWithExpiry(key, value, seconds): hodnota po uplynutí této doby zmizí.
  • KVCompareAndSet(key, old, new) a KVSetWithOptions s Atomic: bezpečné aktualizace, když stejný klíč zapisuje více požadavků nebo serverů. Hlasování takto ukládá každý hlas a v případě konfliktu operaci opakuje.
  • KVList(page, perPage): klíče, stránka po stránce.

Webová část aplikace

Balíček registruje třídu pomocí window.registerPlugin. Její initialize přijímá registr a Redux store:

import manifest from './manifest';

class Plugin {
    initialize(registry, store) {
        registry.registerTranslations((locale) => translationsFor(locale));
        registry.registerPostTypeComponent('custom_poll', PollPost);
        registry.registerWebSocketEventHandler(`custom_${manifest.id}_poll_updated`, (msg) => {
            store.dispatch(pollUpdated(JSON.parse(msg.data.poll)));
        });
        registry.registerReconnectHandler(() => store.dispatch(reloadPolls()));
    }
}

window.registerPlugin(manifest.id, new Plugin());
  • Sdílené knihovny. Webová aplikace poskytuje React, ReactDOM, Redux, React Redux a React Intl: vyjmenujte je jako webpack externals (React, ReactDOM, Redux, ReactRedux, ReactIntl) místo toho, abyste balíčkovali vlastní kopie. Viz webpack.config.js aplikace Hlasování.
  • Volání vaší serverové části. Pošlete požadavek na ${window.basename}/plugins/<app id>/… s možnostmi z Client4.getOptions() (z mattermost-redux/client): nesou hlavičky, které server potřebuje k přijetí požadavku z prohlížeče.
  • Váš vlastní stav. registerReducer přidá reducer, který se ukládá pod state['plugins-<app id>'].
  • Typy. Další metody registru v Antimatter nejsou součástí zveřejněných typů Mattermost: Hlasování deklaruje registr, který používá, v webapp/src/types/host.ts.

Metody registru a jejich parametry jsou zdokumentovány v kódu, v webapp/fusion/src/plugins/registry.ts (Fusion) a webapp/channels/src/plugins/registry.ts (klasické).

Podpora rozhraní Fusion

Antimatter má dvě webová rozhraní: Fusion, které je výchozí, a klasické. Oba načítají stejné balíčky aplikací a mají stejný registr, ale Fusion si mnoho věcí vykresluje samo a komponenty pluginů umísťuje svým vlastním způsobem. Otestujte svou aplikaci v obou.

Zjištění rozhraní

Před načtením jakékoli aplikace nastaví obě rozhraní window.antimatterWebUI na 'fusion' nebo 'classic' a stejnou hodnotu na kořenový prvek stránky jako data-am-web-ui. V Mattermost není žádná z nich nastavena.

export function isFusionUI(): boolean {
    return window.antimatterWebUI === 'fusion' || document.documentElement.dataset.amWebUi === 'fusion';
}

Sdílené aplikace toto využívají k odlišnému vykreslování. Aplikace Hlasování vykresluje karty hlasování ve stylu Fusionu a v Fusionu je hlasování napsáno přímo v kompozitoru (jeden z režimů kompozitoru) namísto v dialogovém okně. Aplikace Hovory a Hlasové kanály vynechávají komponenty bočního panelu a vyskakovacích oznámení, které Fusion nahrazuje vlastním rozhraním hovorů. Aplikace AI chat umisťuje své akce AI do nabídky zprávy, jako skupinu pod Další akce, namísto nástrojové lišty zobrazované při najetí kurzorem. Vlastní CSS třídy Fusionu (s předponou am-) a jeho vrstva dialogových oken (element am-layer) jsou to, co tyto aplikace využívají k přizpůsobení rozhraní; nejedná se o zdokumentované, stabilní API.

Kde Fusion zobrazuje komponenty pluginů

Metoda registruKde se v Fusionu zobrazí
registerAppBarComponent, registerChannelHeaderButtonActionLišta aplikací, sloupec ikon na pravém okraji, mezi vaším avatarom a tlačítkem O aplikaci Antimatter. Tam se také umísťují tlačítka v hlavičce kanálu, nikoli v samotné hlavičce. Skryje se, když systémový administrátor zapne Vypnout lištu aplikací (ExperimentalSettings.DisableAppBar, výchozí stav je vypnuto).
registerRightHandSidebarComponentPravý panel. Jeho hlavička je součástí Fusionu a je stejná pro všechny aplikace: ikona a název aplikace (registrovaný titulek, pokud se vykresluje pouze text, jinak popisek v liště aplikací nebo název z manifestu), poté Obnovit velikost panelu (po změně velikosti), Otevřít v novém okně a Zavřít. Titulek, který vykresluje cokoliv jiného (ovládací prvek, odkaz, obrázek), je vynechán, proto své vlastní ovládací prvky aplikace vykreslete přímo v panelu, například v úzkém řádku nahoře. Panel lze změnit tažením za jeho okraj. Viz Panel v novém okně.
registerChannelHeaderIconIkony v hlavičce kanálu, za tlačítky pro hovory; na mobilních telefonech v menu Více v hlavičce.
registerCallButtonActionTlačítko pro hovor v hlavičce kanálu; několik poskytovatelů sdílí menu Další způsoby, jak zavolat.
registerPostDropdownMenuActionMenu Akce se zprávou (⋯) zprávy, v jeho podmenu Další akce; také v menu Akce s vláknem panelu vlákna, pod Další akce. Fusion vykresluje ikonu aplikace před každou akcí, v šedé: vlastní ikonu pro integrované aplikace, jinak ikonu, kterou aplikace zobrazuje v liště aplikací, jinak neutrální ikonu. Prvek bez textu na začátku textu akce (ikona, kterou aplikace vykresluje sama) je skryt.
registerPostDropdownSubMenuActionSkupina položek ve stejné podnabídce Další akce, umístěná za jednotlivými akcemi, oddělená linkou a bez nadpisu. Žádná aplikace nezíská vlastní položku v nabídce zprávy.
registerPostActionComponentNabídka zprávy při najetí kurzorem, před tlačítkem ⋯.
registerPostTypeComponent, registerPostMessageAttachmentComponent, registerPostHeaderComponent, registerPostWillRenderEmbedComponentSamotná zpráva: její tělo, pod jejím textem, za jejím časem a na místě náhledů odkazů.
registerChannelHeaderMenuActionMenu Možnosti kanálu kanálu (⋯ u kanálu v bočním panelu, nebo pravým tlačítkem myši), jako položky s ikonou zásuvky.
registerMainMenuActionMenu komunity (název komunity nahoře v seznamu kanálů), pod Další akce, seskupeno podle aplikace.
registerSidebarBrowseOrAddChannelMenuActionMenu komunity, za Vytvořit kanál a Vytvořit kategorii.
registerFileUploadMethodMenu + v kompozitoru (Přidat do zprávy), za Nahrát soubor.
registerPostEditorActionComponentFormátovací lišta v kompozitoru, když je formátování zobrazeno.
registerAIActionMenuItemComponentMenu AI v kompozitoru.
registerFileDropdownMenuAction, registerFilePreviewComponentMenu možností souboru a náhled souboru.
registerUserSettingsProfil a nastavení, jedna záložka na aplikaci pod nadpisem Pluginy.
registerChannelSettingsTabDialog nastavení kanálu, pod nadpisem Aplikace.
registerPopoverUserAttributesComponent, registerPopoverUserActionsComponentKarta profilu, která se zobrazí, když kliknete na jméno nebo avatar.
registerLeftSidebarHeaderComponent, registerSidebarChannelLinkLabelComponent, registerChannelIconOverride, registerBottomTeamSidebarComponentHorní část seznamu kanálů komunity, vedle názvu kanálu, ikony kanálu a spodní části bočního panelu komunity vlevo.
registerChannelIntro, registerChannelIntroButtonAction, registerChannelToastComponent, registerNewMessagesSeparatorActionComponent, registerChannelComposerBannerComponent, registerComposerPlaceholderZačátek historie kanálu, horní část seznamu zpráv, čára nepřečtených zpráv, nad editorem zpráv a placeholder editoru.
registerChannelTypeOptionTypy kanálů v dialogu Vytvořit kanál.
registerSearchComponentsVyhledávací pole v hlavičce kanálu: rozsahy aplikace se zobrazí, když je prázdné.
registerUserGuideDropdownMenuActionO aplikaci Antimatter (spodní část bočního panelu aplikace), pod Nápověda.
registerProductCelé stránky na URL produktu, v klasickém rámu stránky, bez nabídky produktů: Fusion žádnou nabídku produktů nemá a nikde je nevytváří, proto odkazujte na stránku sami. Žádná sdílená aplikace neregistruje produkt: Kanban, Playbooky a AI chat se otevírají v bočních panelech.
registerRootComponent, registerGlobalComponent, hooky jako registerMessageWillBePostedHook, registerSlashCommandWillBePostedHook, registerMessageWillFormatHook, registerFilesWillUploadHook, registerDesktopNotificationHook, registerWebSocketEventHandler, registerReducer, registerTranslationsFungují stejně jako v klasickém rozhraní.
registerAdminConsolePlugin, registerAdminConsoleCustomSettingSystémová konzole, která je v obou rozhraních klasická.

Co Fusion nezobrazuje ani nespouští

  • registerPostDropdownMenuComponent (komponenta v nabídce zprávy) a registerPostCardTypeComponent (karta příspěvku v pravém panelu) se ve hlavních zobrazeních Fusionu nezobrazují. registerProductSwitcherMenuItem ve Fusionu nic nedělá, protože Fusion nemá nabídku produktů (zapíše se varování). Místo prvního funkce použijte registerPostDropdownMenuAction.
  • registerMessageWillBeUpdatedHook se nespustí, když se zpráva upraví v Fusion. Úpravy ověřujte na serveru pomocí hooku MessageWillBeUpdated.
  • registerActionAfterChannelCreation se spouští pouze z klasického dialogu pro vytváření kanálu, který Fusion otevírá z Další možnosti… v dialogu Vytvořit kanál.
  • Komponenty lišky aplikace se zobrazují bez ohledu na supportedProductIds.
  • Cesty z registerNeedsTeamRoute a registerCustomRoute si zachovávají klasický rámec stránky, stejně jako produkty.

Hooky, které má pouze Antimatter

Tyto metody registru neexistují v Mattermost. Před voláním metody ověřte, že existuje, aby aplikace fungovala i tam:

if (typeof registry.registerComposerMenuItem === 'function') {
    registry.registerComposerMenuItem({
        text: 'Create a poll',
        icon: <i className='icon icon-chart-bar'/>,
        fusionIcon: 'poll',
        action: ({channelId, rootId}) => openPollCreation(channelId, rootId),
    });
} else {
    registry.registerChannelHeaderButtonAction(icon, (channel) => openPollCreation(channel.id), 'Create a poll', 'Create a poll');
}
MetodaCo přidává
registerComposerMenuItem({text, icon, fusionIcon, action, shouldRender})Položka v menu + v poli pro zprávu, v obou rozhraních. action přijímá {channelId, rootId}.
registerEmojiPickerTab({id, label, icon, fusionIcon, order, component, shouldRender, replacesGifPicker})Karta výběru emoji v poli pro zprávu, například GIFy nebo samolepky. V Fusion se karty s order menším než 10 zobrazí před kartou Emoji. Komponenta přijímá channelId, rootId, filter, onFilterChange, onSelectDone, insertText, isFusion a v Fusion pickerKit (viz níže).
registerChannelViewPanel({matcher, component})Panel mezi záhlavím kanálu a zprávami, určený pro kanály, které vybere matcher(state, channel). Může skrýt zprávy i editor (setMessagesVisible), stejně jako to dělají hlasové kanály.
registerSidebarChannelFooterComponent(component)Řádek pod kanálem v bočním panelu, například lidé v jeho hovoru. Přijímá channel.
registerIntegrationsOption({title, description, fusionIcon, action, shouldRender})Pouze Fusion: položka na stránkách Integrace, vedle vlastních emoji. Aplikace GIFs tam přidá své vlastní GIFy a samolepky.

Přidat a spravovat pohledy ve výběru emoji

Ve Fusion se tabulce výběru emoji přidává prop pickerKit: komponenty, které tabulka Emoji používá pro vlastní zobrazení přidávání a správy, aby se pohledy tabulky podobaly a zůstaly uvnitř výběru. Aplikace GIFs a Memes ho používají k přidávání a správě GIFů, samolepek a šablon memů. Obsahuje version (1), hooky usePickerView a useOnEscape a komponenty PickerViewButtons (tlačítka + a správy vedle vyhledávacího pole), PickerViewFrame (pohled se šipkou zpět), PickerNotice, PickerField, PickerSearch, PickerDropZone a PickerManageList. Klávesa Escape se vrací z pohledu do mřížky tabulky. Zkontrolujte, že prop je přítomen: jiní hostitelé ho vynechávají a tabulka pak používá vlastní dialogy. Typy jsou v webapp/fusion/src/types/store/plugins.ts (EmojiPickerTabProps) a komponenty v webapp/fusion/src/fusion/popovers/picker_views.tsx.

Režimy kompozitoru

V aplikaci Fusion přidá registerComposerMode({id, label, fusionIcon, placeholder, sendLabel, component, shouldRender}) další způsob psaní zprávy v kompozitoru, podobně jako to dělá aplikace Hlasování pro hlasování. Tento režim je nabízen v nabídce + kompozitoru pod svým label. Po výběru si kompozitor zachová textové pole (s placeholder daného režimu, např. otázkou v hlasování), zobrazí pod ním váš component a lištu s názvem režimu a tlačítkem Zrušit; tlačítko Odeslat použije sendLabel a spustí váš odesílací skript místo odeslání textu.

  • Komponenta přijímá channelId, rootId (vlákno v poli pro odpověď), message (text kompozitoru), setSubmit a onCancel (návrat na běžnou zprávu se zachováním textu).
  • Funkci setSubmit předávejte funkci, která je volána s textem kompozitoru a po odeslání se vyhodnotí na nic (nebo prázdný řetězec): kompozitor se pak vymaže a vrátí se do stavu běžné zprávy. Pokud se vyhodnotí na řetězec, zobrazí se na místě jako chyba.
  • placeholder a sendLabel jsou řetězce nebo funkce využívající intl z knihovny react-intl. shouldRender(state, {channelId, rootId}) rozhoduje o tom, zda kompozitor nabízí daný režim.
  • Pro otevření režimu z jiného místa, například pomocí příkazu s lomítkem bez argumentů: window.dispatchEvent(new CustomEvent('am-composer-mode', {detail: {id, channelId, rootId}})).

Režimy kompozitoru jsou dostupné pouze ve Fusionu: zkontrolujte, zda registr tuto funkci obsahuje, a pokud ne, ponechte si vlastní dialog. Metoda vrací id režimu a režim je odstraněn při odebrání aplikace.

Panel v novém okně

Fusion předává komponentě registrované pomocí registerRightHandSidebarComponent dva další props, aby možnost Otevřít v novém okně otevřela panel tam, kde byl, nikoli na domovské stránce aplikace:

  • setPanelState(state): zavolejte ho, když se panel přesune, s krátkým řetězcem, který vaša aplikace definuje, např. 'note/<id>' nebo 'board/<id>/card/<id>', nebo null na domovské stránce. Hodnota se zapíše do adresy okna, takže do ní nikdy neukládejte tajná data. Stav delší než 512 znaků nebo obsahující ovládací znaky se zahodí.
  • initialPanelState: v novém okně je to stav, který panel měl, nebo undefined. Považujte ho za nedůvěryhodný vstup: položku znovu načtěte jako přihlášená osoba a pokud ji nemůže otevřít, zobrazte domovskou stránku aplikace nebo pohled „položka nenalezena“.

Stav se zapomene, když se panel zavře nebo ho nahradí panel jiné aplikace. Chcete-li okno panelu otevřít sami, použijte popoutRhsPlugin (který je také v window.WebappUtils.popouts); stav přijímá jako volitelný pátý argument. Používají ho Poznámky, Tabule, Úkoly, Kanban a Playbooky.

fusionIcon určuje ikonu z sady ikon Fusion (například poll, sticker, calendar nebo image; viz webapp/fusion/src/fusion/components/icon). Fusion ji zobrazí místo icon, aby se položka ladila s ostatními.

Nastavení

settings_schema v plugin.json popisuje stránku aplikace v Systémová konzole › Pluginy:

"settings_schema": {
  "header": "Forecasts come from the weather service below.",
  "settings": [
    {"key": "ServiceURL", "display_name": "Service URL", "type": "text", "default": "https://api.example.com"},
    {"key": "Units", "display_name": "Units", "type": "dropdown", "default": "metric",
     "options": [{"display_name": "Celsius", "value": "metric"}, {"display_name": "Fahrenheit", "value": "imperial"}]},
    {"key": "EnableAlerts", "display_name": "Severe weather alerts", "type": "bool", "default": false,
     "help_text": "Post alerts in the channels that follow a city."}
  ]
}

Typy nastavení: bool, text, longtext, number, dropdown, radio, generated (náhodný tajný klíč), username a custom (vykreslené komponentou, kterou webová část aplikace zaregistruje pomocí registerAdminConsoleCustomSetting). Nastavení lze seskupit do sections.

Serverová část je načte do struktury, jejíž pole odpovídají klíčům:

type configuration struct {
	ServiceURL   string
	Units        string
	EnableAlerts bool
}

func (p *Plugin) OnConfigurationChange() error {
	var cfg configuration
	if err := p.API.LoadPluginConfiguration(&cfg); err != nil {
		return err
	}
	p.setConfiguration(&cfg)
	return nil
}

Administrátoři je mohou nastavit i v konfiguraci, pod PluginSettings.Plugins.<app id>, s klíči ve小 písmenách (viz průvodce pro administrátory).

Překlady

Antimatter je přeložen do 37 jazyků, stejně jako přibalené aplikace. Webová část to dělá takto:

  1. Každý řetězec pište pomocí React Intl, s ID a jeho anglickým textem: <FormattedMessage id='polls.create' defaultMessage='Create a poll'/> nebo defineMessages. ID předčínejte názvem vaší aplikace.
  2. Vyextrahujte anglické řetězce do webapp/i18n/en.json (přibalené aplikace používají formatjs extract).
  3. Přidejte jeden soubor pro každý jazyk vedle něj, se stejnými ID: fr.json, de.json, pt-BR.json, zh-CN.json…
  4. Předá je webové aplikaci, která se uživatele zeptá na jeho jazyk:
    import fr from '../i18n/fr.json';
    import ptBR from '../i18n/pt-BR.json';
    
    const catalogs = {fr, 'pt-BR': ptBR};
    
    export function translationsFor(locale) {
        return catalogs[locale] || catalogs[locale.split('-')[0]] || {};
    }
    
    registry.registerTranslations(translationsFor);

Chybějící řetězec se vrátí na anglický defaultMessage. U řetězců, které posílá serverová část (odpovědi na příkazy, zprávy botů), balíček pluginapi/i18n načte soubory s překlady dodané v balíčku (například assets/i18n) a poskytne lokalizátor v jazyce každého uživatele.

Sestavení, instalace a ladění

Budete potřebovat Go a Node.js (verzi uvedenou v souboru .nvmrc aplikace). V složce aplikace:

PříkazCo dělá
make distSestaví serverovou část pro všechny platformy a webovou aplikaci a zapíše balíček do dist/<app id>-<version>.tar.gz.
make testSpustí testy Go a JavaScript.
make check-styleZkontroluje kód (golangci-lint, ESLint) a ověří typy TypeScript.
make deploySestaví, poté nainstaluje a zapne balíček na serveru.
make watch, make deploy-from-watchPrvní příkaz při každém uložení sestaví webovou aplikaci; druhý sestavení zabalí a nainstaluje na server.
make enable, make disable, make resetZapne, vypne nebo restartuje aplikaci na serveru.
make logs, make logs-watchZobrazí řádky aplikace v serverovém logu.
make attachPřipojí ladící nástroj Delve ke spuštěné serverové části (nejprve sestavte s AM_DEBUG=1).

make deploy a ostatní serverové příkazy používají místní režim socketu, pokud ho najdou (/var/tmp/antimatter_local.socket, nebo AM_LOCALSOCKETPATH): spustíte je na stroji serveru s zapnutým místním režimem. Jinak se přihlásí na server určený proměnnou MM_SERVICESETTINGS_SITEURL pomocí AM_ADMIN_TOKEN, nebo AM_ADMIN_USERNAME a AM_ADMIN_PASSWORD. Sestavovací nástroje zatím nečtou proměnnou AM_ pro adresu serveru.

Ruční instalace balíčku

  • Systémový administrátor povolí nahrávání pomocí AM_PLUGINSETTINGS_ENABLEUPLOADS=true (výchozí je vypnuto; v Systémové konzoli není přepínač).
  • Pak Systémová konzole › Pluginy › Správa pluginů › Nahrát plugin, nebo amctl plugin add weather.tar.gz a poté amctl plugin enable com.example.weather.
  • Pokud má server zapnuté Vyžadovat podpis pluginu, přijímá pouze balíčky podepsané klíčem, kterému důvěřuje; viz Instalace dalších aplikací.
Pro rychlé iterace na vývojovém serveru nastavte na serveru AM_SERVICESETTINGS_ENABLEDEVELOPER=true: chyby pak nesou podrobnosti. Makefiles balíčkových aplikací sestaví serverovou část pouze pro vaši platformu, pokud je ve vašem shellu nastavena proměnná MM_SERVICESETTINGS_ENABLEDEVELOPER.