Alkalmazások (bővítmények) írása

Az alkalmazások belülről bővítik az Antimattert: egy Go nyelven írt szerverrész, amely a szerverbe horgonyozódik, és egy React nyelven írt webalkalmazásrész, amely menüket, paneleket, üzenettípusokat és oldalakat ad hozzá. Ez az oldal a csatolt Polls-alkalmazás példáján mutatja be, hogyan épül fel egy alkalmazás, és mi változik az Antimatter Fusion-felületén.

Hogyan működik egy alkalmazás

  • A szerverrész egy Go program. A szerver külön folyamatként indítja el, és RPC-n keresztül kommunikál vele: meghívja az alkalmazás horgonyait (pl. egy üzenet即将 beküldésre kerül, egy parancs lefutott…), az alkalmazás pedig a szerver bővítmény-API-ját használja (pl. bejegyzés létrehozása, felhasználó olvasása, adatok tárolása…). HTTP útvonalakat is szolgálhat a /plugins/<app id>/ alatt.
  • A webalkalmazásrész egy JavaScript-csomag, amelyet a böngésző betölt a webalkalmazással együtt. A rejiszterben regisztrál React-komponenseket és visszahívásokat: egy gombot a menüben, egy panelt, egy üzenettípus megjelenítési módját.
  • Bármelyik rész opcionális. Az alkalmazás egy .tar.gz csomagként települ, a rendszeradminisztrátorok pedig engedélyezik, letiltják és konfigurálják.
A szerverrész teljes hozzáféréssel rendelkezik a szerver adataihoz. Az alkalmazást úgy kezelje, mint olyan kódot, amelyet a szerverre telepít.

Az Antimatter futtathat Mattermost-nak írt alkalmazásokat, és fordítva, amennyiben az alkalmazás csak olyan horgonyokat használ, amelyek mindkettőben léteznek. Lásd: Csak az Antimatterben lévő horgonyok.

A csomag szerkezete

Nincs külön kezdősablon: induljon egy kicsi, csatolt alkalmazásból, például a antimatter-plugin-polls vagy a antimatter-plugin-todo alkalmazásból, és módosítsa annak azonosítóját és kódját. A Polls szerkezete így néz ki:

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

Opcionális: public/ mappa, amely a /plugins/<app id>/public/ alatt szolgálja ki a fájlokat változatlanul.

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: egyedi és állandó; általában fordított domainnév. Ez nevezi meg az útvonalakat, a tárolt beállításokat és a WebSocket-eseményeket.
  • version: szemantikus verzió. A csatolt alkalmazások nem adják meg a plugin.json-ban; a build-eszköz a legutóbbi v* git-címkéből veszi.
  • min_server_version: az Antimatter 12.0.0 verziót jelent.
  • settings_schema: az alkalmazás beállításai a Rendszerkonzolon (lásd Beállítások).

A make apply parancs a manifestet a server/manifest.go és a webapp/src/manifest.ts fájlba másolja; a make manifest-check parancs pedig validálja.

A szerverrész

A szerverrész ugyanazt a Go-modult importálja, mint a Mattermost-alkalmazások, a github.com/mattermost/mattermost/server/public modult a Go-modul proxy-ról. A main függvény csak elindítja az alkalmazást:

package main

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

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

A Plugin típus beágyazza a plugin.MattermostPlugin típust, amely biztosítja a p.API hozzáférést, és megvalósítja a szükséges hookokat:

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)
}

Gyakori hookok

HookAmikor fut
OnActivate, OnDeactivateAmikor az alkalmazás engedélyezve vagy letiltva, valamint amikor a szerver elindul és leáll. A parancsokat és botokat a OnActivate hookban kell regisztrálni.
OnConfigurationChangeAmikor a beállítások megváltoznak; a p.API.LoadPluginConfiguration(&cfg) hívással tölthetők be.
ServeHTTPKérések a /plugins/<app id>/… útvonalra. A szerver beállítja a Mattermost-User-Id fejlécet, ha a kérés bejelentkezett felhasználótól érkezik; utasítsa vissza azokat a kéréseket, amelyeknél ez a fejléc hiányzik, hacsak nem nyilvánosak.
ExecuteCommandAmikor egy az alkalmazás által regisztrált parancs fut le.
MessageWillBePosted, MessageWillBeUpdatedMielőtt egy üzenet elmentésre kerül: módosított bejegyzést vagy elutasítás okát adja vissza.
MessageHasBeenPosted, MessageHasBeenUpdated, MessageHasBeenDeletedUtólag, mellékhatások végrehajtásához.
ReactionHasBeenAdded, ReactionHasBeenRemovedReakciók.
UserHasJoinedChannel, UserHasLeftChannel, UserHasJoinedTeam, UserHasLeftTeam, ChannelHasBeenCreatedTaglétiségi és csatornaváltozások. A csapat egy közösség.
FileWillBeUploadedMielőtt egy fájl tárolásra kerül: módosítsa vagy utasítsa vissza.
OnPluginClusterEventÜzenet ugyanazon alkalmazástól egy klaszter másik szerveréről.

A teljes lista, a pontos aláírásokkal, a server/public/plugin/hooks.go fájlban található; az API a server/public/plugin/api.go fájlban. A pluginapi csomag (server/public/pluginapi) barátságosabb segédfüggvényekkel burkolja be az API-t.

Hasznos API-hívások

  • CreatePost, UpdatePost, SendEphemeralPost: bejegyzés küldése az alkalmazás botjaként vagy felhasználóként.
  • EnsureBotUser(&model.Bot{Username, DisplayName}): egyszeri bot létrehozása az alkalmazás számára és annak azonosítójának lekérése.
  • PublishWebSocketEvent("poll_updated", payload, &model.WebsocketBroadcast{ChannelId: id}): esemény küldése a webalkalmazásnak. A custom_<app id>_poll_updated néven érkezik meg.
  • OpenInteractiveDialog: párbeszéd megnyitása.
  • LogInfo, LogError és társaik: írás a szerver naplófájlba, az alkalmazás azonosítójával címkézve.
  • GetUser, GetChannel, HasPermissionToChannel: ellenőrzés, ki mit tehet, mielőtt felhasználó nevében cselekszik.

Egy alkalmazás a klaszter minden szerverén fut. Az állapotot tárolja a kulcs-érték tárolóban vagy az adatbázisban, ne csak a memóriaban.

Az AI-szolgáltatás

Az Antimatter szervere AI-modelleket hív meg az alkalmazások számára, az adminisztrátor által beállított modellkapcsolatokon keresztül (lásd AI az adminisztrátori útmutatóban). Egy alkalmazás sosem tárol szolgáltatói kulcsot: megadja, kinek nevében és hol hajt végre műveletet, a szerver pedig ellenőrzi, hogy az adott felhasználó használhatja-e ott a kapcsolatot, alkalmazza a korlátokat és kereteket, a tokeneket pedig az adott felhasználó és az alkalmazás számlájára írja, a hívás tartalmának rögzítése nélkül.

MetódusMire szolgál
AIChat(actingUserID, channelID, teamID, *ai.ChatRequest)Csevegés-befejezés: üzenetek, eszközök, strukturált kimenet (JSONSchema).
AIChatStream(…)Ugyanez áramlással: olvasd a stream Events elemeit, amíg a csatorna nem záródik, vagy állítsd le a hívást a Close metódussal.
AIEmbed(…, *ai.EmbedRequest)Szövegek beágyazása (embeddings).
AITranscribe(…, *ai.TranscribeRequest)Beszéd szöveggé alakítása.
AIDecide(…, *ai.DecideRequest)Típusos kérdések egy döntéshozatali modellhez (igen vagy nem, választás, pontszám) egy szöveggel kapcsolatban.
AIConnectionsForUser(userID, channelID, teamID)Azok a kapcsolatok, amelyeket egy felhasználó ott használhat, modellekkel és képességekkel, de soha kulcsokkal.

Minden kérés ConnectionID mezője választja ki a kapcsolatot, a Model pedig annak egy modeljét (üresen a kapcsolat alapértelmezett modeljét jelöli). Az Operation egy rövid címké a használati és audit-nyilvántartásokhoz, soha nem tartalmaz tartalmat. Ezek minimális szerververziója 11.11.

A actingUserID értékét csak olyan forrásból vedd fel, amelyet a szerver hitelesített: a Mattermost-User-Id fejléc a ServeHTTP felé érkező kérésben, vagy a hurok kontextusában lévő felhasználó (a bejegyzés szerzője, vagy az a felhasználó, aki egy parancsot futtatott). Soha ne vedd fel egy üzenetből, webhook-teherből, URL-ből vagy egy másik alkalmazás kéréséből: így az alkalmazásod mások hozzáférését használná fel.

Ezek a metódusok és az ai csomag (server/public/ai) csak az Antimatter saját modulmásolatában létezik, nem a Mattermost által kiadott verzióban. Használatukhoz az Antimatter server/public könyvtára ellen kell fordítani, egy replace sorral a go.mod fájlban, például: replace github.com/mattermost/mattermost/server/public => ../antimatter/server/public. Ilyen alkalmazás csak Antimatter-en fut.

Adattárolás

Minden alkalmazásnak van saját kulcs-érték tárolója a szerver adatbázisában:

  • KVSet(key, value), KVGet(key), KVDelete(key): az értékek bájtok, általában JSON. A kulcsok legfeljebb 150 karakter hosszúak.
  • KVSetWithExpiry(key, value, seconds): az érték eltűnik az adott idő elteltével.
  • KVCompareAndSet(key, old, new) és KVSetWithOptions Atomic opcióval: biztonságos frissítések, ha több kérés vagy szerver ír ugyanarra a kulcsra. A Szavazás így ment el minden szavazást, és újrapróbálja a konfliktus esetén.
  • KVList(page, perPage): a kulcsok, oldalanként.

A webalkalmazás rész

A csomag egy osztályt regisztrál a window.registerPlugin segítségével. Az initialize metódus megkapja a registry-t és a Redux store-t:

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());
  • Megosztott könyvtárak. A webalkalmazás biztosítja a Reactet, a ReactDOMot, a Reduxot, a React Reduxot és a React Intlt: ezeket a webpack externals listájába kell felvenni (React, ReactDOM, Redux, ReactRedux, ReactIntl), nem pedig saját másolatokat csomagolni. Lásd a Szavazás webpack.config.js fájlját.
  • A szerveri rész hívása. A ${window.basename}/plugins/<app id>/… URL-t a Client4.getOptions() (a mattermost-redux/client-ből) által visszaadott opciókkal kell lekérni: ezek tartalmazzák a fejléceket, amelyeket a szervernek szüksége van a böngészőből érkező kérések elfogadásához.
  • Saját állapot. A registerReducer hozzáad egy reducer-t, amely a state['plugins-<app id>'] alatt tárolódik.
  • Típusok. Az Antimatter extra registry-metódusai nem szerepelnek a Mattermost közzétett típusaiban: a Szavazás a webapp/src/types/host.ts fájlban deklarálja a használt registry-t.

A registry metódusai és paraméterei a kódban vannak dokumentálva, a webapp/fusion/src/plugins/registry.ts (Fusion) és a webapp/channels/src/plugins/registry.ts (klasszikus) fájlokban.

A Fusion felület támogatása

Az Antimatternek két webes felülete van: a Fusion (az alapértelmezett) és a klasszikus. Mindkettő ugyanazokat az alkalmazáscsomagokat tölti be, és ugyanazt a registry-t használja, de a Fusion sok elemet saját maga rajzol, és a plugin komponenseket saját módon helyezi el. Tesztelje az alkalmazását mindkét felületen.

A felület azonosítása

Minden alkalmazás betöltése előtt mindkét felület beállítja a window.antimatterWebUI értékét 'fusion'-ra vagy 'classic'-ra, és ugyanezt az értéket a data-am-web-ui attribútumként is a lap gyökérelemére. A Mattermostban egyik sem van beállítva.

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

A csatolt alkalmazások ezt használják a megjelenítés eltérő formában történő megjelenítésére. A Szavazás alkalmazás a szavazási kártyáit és a létrehozási párbeszédablakot a Fusion stílusában rajzolja meg, és a Fusionban a /poll parancs saját párbeszédablakát nyitja meg. A Hívások és a Hangcsatornák kihagyják az oldalsávot és a toast-komponenseket, amelyeket a Fusion a saját hívásfelületével helyettesít. Az Ügynökök alkalmazás a mesterséges intelligencia műveleteit az üzenet menüjébe helyezi, a További műveletek csoportjába, nem pedig a lebegő eszköztáblába. A Fusion saját CSS-osztályai (az am- előtaggal) és a párbeszédablak-rétege (az am-layer elem) azok, amelyeket ezek az alkalmazások a felület illesztésére használnak; ezek nem dokumentált, stabil API-k.

Ahol a Fusion a bővítménykomponenseket megjeleníti

Regisztrációs metódusAhol jelenik meg a Fusionban
registerAppBarComponent, registerChannelHeaderButtonActionAz app-sáv, a jobb szélen lévő ikonok oszlopa, az avatarod és az Az Antimatter névjegye gomb között. Ide kerülnek a csatorna-fejléc gombjai is, nem a csatorna-fejlécbe. Rejtve van, ha a rendszeradminisztrátor bekapcsolja az Apps Bar letiltása beállítást (ExperimentalSettings.DisableAppBar, alapértelmezetten ki).
registerRightHandSidebarComponentA jobb oldali panel. Az emberek a szélét húzva átméretezhetik, és új ablakban megnyithatják. Ha az alkalmazás egyben termék is, a panel a Megnyitás teljes oldalként lehetőséget kínálja. Lásd: A panel új ablakban.
registerChannelHeaderIconIkonok a csatorna-fejlécben, a hívás gombok után; mobilon a fejléc További menüjében.
registerCallButtonActionA csatorna-fejléc hívás gombja; több szolgáltató is megosztja a Egyéb hívási módok menüt.
registerPostDropdownMenuActionAz üzenet Üzenet műveletek (⋯) menüje, a További műveletek almenujában; valamint az üzenetszál panel Üzenetszál műveletei menüjében, a További műveletek alatt. A Fusion szürke színnel rajzolja meg az alkalmazás ikonját minden művelet előtt: a csatolt alkalmazások saját ikonját, egyébként pedig azt az ikont, amelyet az alkalmazás az alkalmazás-sínen mutat, vagy ha ez sincs, egy semleges ikont. Egy elem, amely nem tartalmaz szöveget a művelet szövegének elején (egy ikon, amelyet az alkalmazás maga rajzol), rejtve marad.
registerPostDropdownSubMenuActionEgy csoport a További műveletek almenujában, a különálló műveletek után. Egyik alkalmazás sem kap saját bejegyzést az üzenet menüjében.
registerPostActionComponentAz üzenet lebegő eszköztáblája, a ⋯ gomb előtt.
registerPostTypeComponent, registerPostMessageAttachmentComponent, registerPostHeaderComponent, registerPostWillRenderEmbedComponentAz üzenet maga: a teste, a szövege alatt, az idő után, valamint a hivatkozási előnévek helyett.
registerChannelHeaderMenuActionA csatorna Csatorna opciók menüje (⋯ egy csatornán az oldalsávban, vagy jobb klikk), dugós ikonú elemként.
registerMainMenuActionA közösség menüje (a közösség neve a csatornalista tetején), a További műveletek alatt, alkalmazásonként csoportosítva.
registerSidebarBrowseOrAddChannelMenuActionA közösség menüje, a Csatorna létrehozása és a Kategória létrehozása után.
registerFileUploadMethodA szerkesztő + menüje (Hozzáadás az üzenethez), a Fájl feltöltése után.
registerPostEditorActionComponentA szerkesztő formázósávja, ha a formázás látható.
registerAIActionMenuItemComponentA szerkesztő MI menüje.
registerFileDropdownMenuAction, registerFilePreviewComponentEgy fájl beállításmenüje, valamint a fájl előnézete.
registerUserSettingsProfil és beállítások, alkalmazásonként egy-egy fül a Bővítmények fejezet alatt.
registerChannelSettingsTabA csatorna beállítások párbeszédablaka, az Alkalmazások fejezet alatt.
registerPopoverUserAttributesComponent, registerPopoverUserActionsComponentA profil-kártya, amely akkor jelenik meg, ha egy nevet vagy avartarsatot kattintasz.
registerLeftSidebarHeaderComponent, registerSidebarChannelLinkLabelComponent, registerChannelIconOverride, registerBottomTeamSidebarComponentA közösség csatornalistájának teteje, a csatorna neve és ikonja mellett, valamint a bal oldali közösségi sáv alja.
registerChannelIntro, registerChannelIntroButtonAction, registerChannelToastComponent, registerNewMessagesSeparatorActionComponent, registerChannelComposerBannerComponent, registerComposerPlaceholderA csatorna előzményeinek kezdete, az üzenetlista teteje, az olvasatlan sor, a szerkesztőmező feletti rész, valamint a szerkesztőmező helyett megjelenő szöveg.
registerChannelTypeOptionA Csatorna létrehozása párbeszédablak csatornatípusai.
registerSearchComponentsA csatorna fejlécének keresőmezője: ha üres, akkor az alkalmazáskörzetek jelennek meg benne.
registerUserGuideDropdownMenuActionAz Antimatter névjegye (az alkalmazás sávjának alján), a Súgó menü alatt.
registerProductTeljes oldalak a termék URL-címén, amelyek a Termékek alatt szerepelnek az Az Antimatter névjegye menüben. A termékoldalak megtartják a klasszikus oldalkeretet, amelynek termékmenüje csak akkor jelenik meg, ha egy alkalmazás regisztrál egy terméket, vagy hozzáad egy bejegyzést. A csatolt Kanban és Playbook alkalmazások már nem regisztrálnak termékeket: az oldalaik a saját oldalsávjaikban nyílnak meg.
registerRootComponent, registerGlobalComponent, valamint olyan hookok, mint a registerMessageWillBePostedHook, registerSlashCommandWillBePostedHook, registerMessageWillFormatHook, registerFilesWillUploadHook, registerDesktopNotificationHook, registerWebSocketEventHandler, registerReducer és registerTranslationsA klasszikus felülettel megegyező működés.
registerAdminConsolePlugin, registerAdminConsoleCustomSettingA Rendszerkonzol, amely mindkét felületen a klasszikus változat.

Ami a Fusion nem jelenít meg vagy nem futtat

  • A registerPostDropdownMenuComponent (egy komponens az üzenetmenüben), a registerPostCardTypeComponent (egy bejegyzés kártyája a jobb oldali panelben) és a registerProductSwitcherMenuItem nem jelennek meg a Fusion fő nézeteiben. Az első helyett használja a registerPostDropdownMenuAction metódust.
  • A registerMessageWillBeUpdatedHook nem fut le, ha egy üzenetet a Fusionban szerkesztenek. A szerkesztéseket a szerveren a MessageWillBeUpdated hookkal ellenőrizze.
  • A registerActionAfterChannelCreation csak a klasszikus csatornalétrehozási párbeszédablakból fut le, amelyet a Fusion a Csatorna létrehozása párbeszédablak További beállítások… menüjéből nyit meg.
  • Az alkalmazás sáv komponensei akkor is megjelennek, ha a supportedProductIds értéke bármilyen is.
  • A registerNeedsTeamRoute és registerCustomRoute útvonalai a klasszikus oldalkeretet használják, hasonlóan a termékekhez.

Kizárólag az Antimatterben meglévő hookok

Ezek a regisztrációs metódusok nem léteznek a Mattermostban. Ellenőrizze, hogy a metódus létezik-e, mielőtt meghívná, így az alkalmazás ott is működik:

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');
}
MetódusMit ad hozzá
registerComposerMenuItem({text, icon, fusionIcon, action, shouldRender})Egy elem az üzenetmező + menüjében, mindkét felületen. Az action paraméter {channelId, rootId} értéket kap.
registerEmojiPickerTab({id, label, icon, fusionIcon, order, component, shouldRender, replacesGifPicker})Az üzenetmező emoji-választó egy füle, például GIF-ek vagy matricák. A Fusionban a 10-nél kisebb order értékű fülök az Emoji fül előtt jelennek meg. A komponens megkapja a channelId, rootId, filter, onFilterChange, onSelectDone, insertText, isFusion és a Fusionban a pickerKit paramétert (lásd alább).
registerChannelViewPanel({matcher, component})Egy panel a csatorna-fejléc és az üzenetek között, amelyeket a matcher(state, channel) függvény választ ki. Elrejtheti az üzeneteket és a szerkesztőt (setMessagesVisible), ahogy a hangcsatornák teszik.
registerSidebarChannelFooterComponent(component)Egy sor a csatorna alatt az oldalsávban, például a csatlakozottak listája egy hívás során. A komponens a channel objektumot kapja.
registerIntegrationsOption({title, description, fusionIcon, action, shouldRender})Csak Fusion: egy bejegyzés az Integrációk oldalon, a saját emoji mellett. A GIFs app a saját GIFjeit és matricáit ide adja hozzá.

Nézetek hozzáadása és kezelése az emoji-választóban

A Fusionban az emoji-választó fülekhez egy pickerKit prop tartozik: azokat a komponenseket, amelyeket az Emoji-fül a saját hozzáadói és kezelő nézeteihez használ, így a fülek nézetei egységesek maradnak, és a választón belül maradnak. A GIFs és Memes alkalmazások ezzel adják hozzá és kezelik a GIF-eket, matricákat és meme-sablonokat. Tartalmazza a version (1) mezőt, a usePickerView és useOnEscape hookokat, valamint a PickerViewButtons (a keresőmező melletti + és kezelő gombok), PickerViewFrame (a vissza nyilakkal ellátott nézet), PickerNotice, PickerField, PickerSearch, PickerDropZone és PickerManageList komponenseket. A Escape billentyűvel a nézetből a fül rácsnézetére lehet visszatérni. Ellenőrizd, hogy a prop jelen van: más gazdák nem adják meg, és ilyenkor a fül saját párbeszédablakait használja. A típusok a webapp/fusion/src/types/store/plugins.ts (EmojiPickerTabProps) és a komponensek a webapp/fusion/src/fusion/popovers/picker_views.tsx fájlban találhatók.

A panel új ablakban

A Fusion a registerRightHandSidebarComponent komponensét két további prop-pal látja el, hogy a Megnyitás új ablakban opció a panelt ott nyissa meg, ahol éppen volt, nem pedig az alkalmazás főoldalán:

  • setPanelState(state): akkor hívd meg, ha a panel helye változik, egy rövid, az alkalmazásod által definiált szöveggel, például 'note/<id>' vagy 'board/<id>/card/<id>', illetve null értékkel, ha a panel a főoldalon van. Ez az ablak címébe kerül, ezért soha ne helyezz belé titkot. A 512 karaktert meghaladó, vagy vezérlő karaktereket tartalmazó állapot elvetettik.
  • initialPanelState: az új ablakban ez a panel korábbi állapota, vagy undefined. Kezeld mint nem megbízható bemenetet: a bejelentkezett felhasználóként szerezd be újra az elemet, és ha nem nyitható meg, az alkalmazás főoldalát vagy egy „nem található” nézetet jeleníts meg.

Az állapot felejtődik el, ha a panel bezárul, vagy egy másik alkalmazás panelje veszi át a helyét. Ha saját kezdeményezésből nyitod meg a panel ablakát, a popoutRhsPlugin (amely a window.WebappUtils.popouts-ban is elérhető) az állapotot opcionális ötödik argumentumként fogadja el. A Notes, Whiteboard, Todo, Kanban és Playbooks alkalmazások használják ezt.

A fusionIcon a Fusion ikonkészletének egy ikonját nevezi meg (például poll, sticker, calendar vagy image; lásd webapp/fusion/src/fusion/components/icon). A Fusion ezt jeleníti meg a icon helyett, hogy az elem összhangban legyen a többivel.

Beállítások

A plugin.json settings_schema mezője leírja az app oldalát a Rendszerkonzol › Bővítmények alatt:

"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."}
  ]
}

Beállítás-típusok: bool, text, longtext, number, dropdown, radio, generated (véletlenszerű titok), username és custom (egy komponens által rajzolt, amelyet a webalkalmazás-rész a registerAdminConsoleCustomSetting függvénnyel regisztrál). A beállítások csoportosíthatók sections segítségével.

A szerver-rész ezekbe a struktúrába olvassa be, amelynek mezői a kulcsokkal egyeznek:

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
}

Az adminisztrátorok a konfigurációban is beállíthatják ezeket, a PluginSettings.Plugins.<app id> alatt, kisbetűs kulcsokkal (lásd az adminisztrátori útmutatót).

Fordítások

Az Antimatter 37 nyelvre van fordítva, a csomagolt alkalmazások is. A webalkalmazás-rész így végzi el:

  1. Minden szöveget React Intl-lel írj meg, azonosítóval és angol szöveggel: <FormattedMessage id='polls.create' defaultMessage='Create a poll'/> vagy defineMessages. Az azonosítókat az alkalmazás nevével kezdő előtaggal látni el.
  2. Az angol szövegeket a webapp/i18n/en.json fájlba kell kivonni (a csomagolt alkalmazások a formatjs extract parancsot használják).
  3. Minden nyelvhez adj hozzá egy fájlt az en.json mellé, ugyanazokkal az azonosítókkal: fr.json, de.json, pt-BR.json, zh-CN.json…
  4. Adja át őket a webes alkalmazásnak, amely a felhasználó nyelvét kéri:
    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);

Ha egy sztring hiányzik, az angol defaultMessage értékre esik vissza. Azoknál a sztringeknél, amelyeket a szerver rész küld (parancsválaszok, botüzenetek), a pluginapi/i18n csomag betölti a csomagban szállított fordítási fájlokat (például az assets/i18n mappát), és minden felhasználó nyelvét használó lokalizátort ad.

Fordítás, telepítés és hibakeresés

Go és Node.js szükséges (az alkalmazás .nvmrc fájljában megadott verzió). Az alkalmazás mappájában:

ParancsMire szolgál
make distFordítja a szerver részt minden platformra, valamint a webes alkalmazás részét, és a csomagot a dist/<app id>-<version>.tar.gz fájlba írja.
make testVégrehajtja a Go és JavaScript tesztek.
make check-styleEllenőrzi a kódot (golangci-lint, ESLint), és ellenőrzi a TypeScript típusokat.
make deployFordítja, majd telepíti és engedélyezi a csomagot a szerveren.
make watch, make deploy-from-watchAz első minden mentéskor újra fordítja a webes alkalmazás részét; a második csomagolja a fordított kódot, és telepíti a szerverre.
make enable, make disable, make resetEngedélyezi, letiltja vagy újraindítja az alkalmazást a szerveren.
make logs, make logs-watchMegjeleníti a szerver naplófájljában az alkalmazás sorait.
make attachCsatlakoztatja a Delve hibakeresőt a futó szerver részhez (előbb fordítsa AM_DEBUG=1 értékkel).

A make deploy és a többi szerverparancs helyi módban működő szoketot használ, ha talál ilyet (/var/tmp/antimatter_local.socket, vagy AM_LOCALSOCKETPATH): futtassa ezeket a szerver gépén, helyi módban. Egyébként a MM_SERVICESETTINGS_SITEURL által megadott szerverre jelentkeznek be AM_ADMIN_TOKEN, vagy AM_ADMIN_USERNAME és AM_ADMIN_PASSWORD használatával. A fordítóeszközök még nem olvasnak be AM_ nevű változót a szerver URL-jéhez.

Csomag kézi telepítése

  • A rendszeradminisztrátor engedélyezi a feltöltést a AM_PLUGINSETTINGS_ENABLEUPLOADS=true beállítással (alapértelmezetten kikapcsolva; nincs erre kapcsoló a Rendszerkonzolban).
  • Ezután Rendszerkonzol › Bővítmények › Bővítmény kezelés › Bővítmény feltöltése, vagy amctl plugin add weather.tar.gz, majd amctl plugin enable com.example.weather.
  • Ha a szerveren be van kapcsolva a Bővítmény aláírás szükséges, csak olyan csomagokat fogad el, amelyeket egy megbízható kulccsal aláírtak; lásd Egyéb appok telepítése.
Gyors iterációkhoz fejlesztői szerveren állítsa be a szerveren a AM_SERVICESETTINGS_ENABLEDEVELOPER=true értéket: ekkor a hibák részletes információt tartalmaznak. A csatolt alkalmazások Makefile-jai csak akkor fordítják a szerver részt a saját platformjára, ha a shell-ben be van állítva a MM_SERVICESETTINGS_ENABLEDEVELOPER.