Uygulama yazma (eklentiler)

Uygulamalar, Antimatter'e içeriden özellik ekler: sunucuya kanca (hook) ile bağlanan Go dilinde bir sunucu kısmı ve menüler, paneller, ileti türleri ve sayfalar ekleyen React dilinde bir web uygulaması kısmı. Bu sayfa, uygulamaların nasıl düzenlendiğini, örnek olarak paketlenmiş Anket (Polls) uygulamasını kullanarak ve Antimatter'in Fusion arayüzünde nelerin değiştiğini gösterir.

Bir uygulamanın çalışma şekli

  • Sunucu kısmı bir Go programıdır. Sunucu, bunu ayrı bir süreç olarak başlatır ve RPC üzerinden iletişim kurar: uygulamanın kancalarını (bir iletinin gönderilmek üzere olması, bir komutun çalıştırılması…) çağırır ve uygulama, sunucunun eklenti API'sini (bir iletinin oluşturulması, bir kullanıcının okunması, veri depolanması…) çağırır. Ayrıca /plugins/<app id>/ altında HTTP yolları da sunabilir.
  • Web uygulaması kısmı, web uygulamasıyla birlikte tarayıcıya yüklenen bir JavaScript paketidir. registrant (kayıt defteri) ile React bileşenleri ve geri çağırma işlevleri (callback) kaydeder: bir menüdeki düğme, bir panel, bir ileti türünü çizme yöntemi.
  • Herhangi bir kısım isteğe bağlıdır. Bir uygulama, .tar.gz paketi olarak kurulur ve sistem yöneticileri onu etkinleştirir, devre dışı bırakır ve yapılandırır.
Sunucu kısmı, sunucunun verilerine tam erişimle çalışır. Bir uygulamayı, sunucuya dağıttığınız kod gibi ele alın.

Antimatter, Mattermost için yazılmış uygulamaları ve tersi, bir uygulama yalnızca her ikisinin de sahip olduğu kancaları kullandığı sürece çalıştırır. Yalnızca Antimatter'in sahip olduğu kancalar bölümüne bakın.

Paket düzeni

Ayrı bir başlangıç şablonu yoktur: antimatter-plugin-polls veya antimatter-plugin-todo gibi küçük bir paketlenmiş uygulamadan başlayın ve kimliğini (ID) ve kodunu değiştirin. Polls şu şekilde düzenlenmiştir:

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

İsteğe bağlı: /plugins/<app id>/public/ adresinde olduğu gibi sunulan bir public/ klasörü.

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: benzersiz ve kalıcı; genellikle tersine çevrilmiş bir alan adı kullanılır. Yolları, depolanan ayarları ve WebSocket olaylarını adlandırır.
  • version: anlamsal sürüm (semantic version). Paketlenmiş uygulamalar bunu plugin.json dosyasından hariç tutar; sürüm bilgilerini en son v* git etiketinden alır.
  • min_server_version: Antimatter 12.0.0 sürümünü bildirir.
  • settings_schema: Sistem Konsolu'ndaki uygulamanın ayarlar sayfası (bkz. Ayarlar).

make apply, manifest dosyasını server/manifest.go ve webapp/src/manifest.ts dosyalarına kopyalar; make manifest-check ise doğrular.

Sunucu bölümü

Sunucu bölümü, Mattermost uygulamalarıyla aynı Go modülünü, github.com/mattermost/mattermost/server/public modülünü, Go modül vekili (proxy) üzerinden alır. main fonksiyonu yalnızca uygulamayı başlatır:

package main

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

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

Plugin tipi, plugin.MattermostPlugin tipini gömerek p.API erişimi sağlar ve ihtiyaç duyduğu kancaları (hooks) uygular:

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

Yaygın kancalar

KancaÇalışma zamanı
OnActivate, OnDeactivateUygulama etkinleştirildiğinde veya devre dışı bırakıldığında, ayrıca sunucu başlatıldığında ve durdurulduğunda. Komutları ve botları OnActivate içinde kaydedin.
OnConfigurationChangeAyarları değiştiğinde; bunları p.API.LoadPluginConfiguration(&cfg) ile yükleyin.
ServeHTTP/plugins/<app id>/… adresine yapılan istekler. İstek bir oturum açmış kullanıcıdan geliyorsa sunucu Mattermost-User-Id başlığını ayarlar; bu başlık olmayan istekleri, herkese açık amaçlı olmadıkça reddedin.
ExecuteCommandUygulamanın kaydettiği bir komut çalıştırıldığında.
MessageWillBePosted, MessageWillBeUpdatedBir ileti kaydedilmeden önce: değiştirilmiş bir iletiyi veya reddetme nedenini döndürün.
MessageHasBeenPosted, MessageHasBeenUpdated, MessageHasBeenDeletedSonradan, yan etkiler için.
ReactionHasBeenAdded, ReactionHasBeenRemovedTepkiler.
UserHasJoinedChannel, UserHasLeftChannel, UserHasJoinedTeam, UserHasLeftTeam, ChannelHasBeenCreatedÜyelik ve kanal değişiklikleri. Bir takım, bir topluluktur.
FileWillBeUploadedBir dosya saklanmadan önce: değiştirin veya reddedin.
OnPluginClusterEventKümenin (cluster) başka bir sunucusundaki aynı uygulamadan gelen bir ileti.

Tam liste, tam imzalarla birlikte, server/public/plugin/hooks.go dosyasında; API ise server/public/plugin/api.go dosyasındadır. pluginapi paketi (server/public/pluginapi), API'yi daha kullanışlı yardımcılarla sarar.

Yararlı API çağrıları

  • CreatePost, UpdatePost, SendEphemeralPost: uygulamanın botu veya bir kullanıcı adına ileti gönderin.
  • EnsureBotUser(&model.Bot{Username, DisplayName}): uygulamanın botunu bir kez oluşturun ve kimliğini (ID) alın.
  • PublishWebSocketEvent("poll_updated", payload, &model.WebsocketBroadcast{ChannelId: id}): web uygulamasına bir olay gönderin. Olay, custom_<app id>_poll_updated olarak ulaşır.
  • OpenInteractiveDialog: bir dialog açın.
  • LogInfo, LogError ve benzerleri: sunucu günlüğüne, uygulamanın kimliğiyle (ID) etiketlenmiş olarak yazın.
  • GetUser, GetChannel, HasPermissionToChannel: bir kullanıcı adına işlem yapmadan önce, kimin ne yapabileceğini kontrol edin.

Bir uygulama, kümenin her sunucusunda çalışır. Durumu yalnızca bellekte değil, anahtar-değer deposunda veya veritabanında saklayın.

Yapay zeka servisi

Antimatter sunucusu, uygulamalar adına yapay zeka modellerine, bir yöneticinin kurduğu model bağlantıları aracılığıyla (yönetici kılavuzundaki Yapay Zeka bölümüne bakın) istek gönderir. Bir uygulama asla bir sağlayıcının anahtarını tutmaz: hangi kişi adına ve nerede hareket ettiğini belirtir; sunucu, bu kişinin orada bu bağlantıyı kullanıp kullanamayacağını kontrol eder, sınırlarını ve bütçelerini uygular, tokenleri kişi ve uygulama adına sayar ve içeriği denetlemeden çağrıyı denetler.

YöntemNe işe yarar
AIChat(actingUserID, channelID, teamID, *ai.ChatRequest)Sohbet tamamlama: iletiler, araçlar, yapılandırılmış çıktı (JSONSchema).
AIChatStream(…)Aynı işlem, akış halinde: kanal kapanana kadar akışın Events değerlerini okuyun veya çağrıyı durdurmak için Close çağrısı yapın.
AIEmbed(…, *ai.EmbedRequest)Metinlerin gömme (embedding) değerleri.
AITranscribe(…, *ai.TranscribeRequest)Sesin metne dönüştürülmesi.
AIDecide(…, *ai.DecideRequest)Bir metin hakkında karar modeline (evet veya hayır, bir seçim, bir puan) yöneltilen tiplemiş sorular.
AIConnectionsForUser(userID, channelID, teamID)Bir kişinin orada kullanabileceği bağlantılar, modelleri ve yetenekleriyle birlikte; asla anahtarlarıyla değil.

Her isteğin ConnectionID değeri bağlantıyı, Model değeri ise bu bağlantının modellerinden birini seçer (boş bırakılırsa bağlantının varsayılanı kullanılır). Operation, kullanım ve denetim kayıtları için kısa bir etikettir; asla içerik değildir. Bunların minimum sunucu sürümü 11.11'dir.

actingUserID değerini yalnızca sunucunun doğruladığı bir kaynaktan alın: ServeHTTP adresinize gelen isteğin Mattermost-User-Id başlığı veya bir kancanın bağlamındaki kullanıcı (bir gönderinin yazarı, bir komutu çalıştıran kullanıcı). Asla bir iletiden, bir webhook yükünden, bir URL'den veya başka bir uygulamanın isteğinden almayın: uygulamanız, başka bir kişinin erişimini harcar.

Bu yöntemler ve ai paketi (server/public/ai), yalnızca Antimatter'in modül kopyasında mevcuttur; Mattermost tarafından yayınlanan sürümde bulunmaz. Bunları kullanmak için, go.mod dosyanızda bir replace satırıyla Antimatter'in server/public dizinine karşı derleme yapın; örneğin replace github.com/mattermost/mattermost/server/public => ../antimatter/server/public. Bu tür bir uygulama yalnızca Antimatter üzerinde çalışır.

Veri saklama

Her uygulamanın, sunucunun veritabanında kendi anahtar-değer deposu vardır:

  • KVSet(key, value), KVGet(key), KVDelete(key): değerler bayt olarak, genellikle JSON biçimindedir. Anahtarlar en fazla 150 karakter olabilir.
  • KVSetWithExpiry(key, value, seconds): değer bu süre dolunca silinir.
  • KVCompareAndSet(key, old, new) ve Atomic ile KVSetWithOptions: birden fazla istek veya sunucu aynı anahtarı yazarken güvenli güncellemeler sağlar. Anket, her oyu bu şekilde kaydeder ve çakışma durumunda yeniden dener.
  • KVList(page, perPage): anahtarlar, sayfa sayfa listelenir.

Web uygulaması bölümü

Yığın (bundle), window.registerPlugin ile bir sınıf kaydeder. initialize metodu kayıt defterini (registry) ve Redux deposunu alır:

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());
  • Paylaşılan kütüphaneler. Web uygulaması React, ReactDOM, Redux, React Redux ve React Intl sağlar: bunları kendi kopyalarınızı yığmak (bundle) yerine webpack externals olarak listeleyin (React, ReactDOM, Redux, ReactRedux, ReactIntl). Anket'in webpack.config.js dosyasına bakın.
  • Sunucu bölümünüzü çağırma. ${window.basename}/plugins/<app id>/… adresini, Client4.getOptions() (kaynağı mattermost-redux/client) ile gelen seçenekleri kullanarak getirin (fetch): bu seçenekler, sunucunun tarayıcıdan gelen isteği kabul etmesi için gereken başlıkları taşır.
  • Kendi durumunuz. registerReducer bir reducer ekler; bu, state['plugins-<app id>'] altında saklanır.
  • Tipler. Antimatter'in ek kayıt defteri (registry) yöntemleri, Mattermost'un yayımladığı tiplerde yer almaz: Anket, kullandığı kayıt defterini webapp/src/types/host.ts dosyasında tanımlar.

Kayıt defterinin (registry) yöntemleri ve parametreleri, webapp/fusion/src/plugins/registry.ts (Fusion) ve webapp/channels/src/plugins/registry.ts (klasik) dosyalarındaki kodda belgelenmiştir.

Fusion arayüzünü destekleme

Antimatter'in iki web arayüzü vardır: varsayılan olan Fusion ve klasik arayüz. İkisi de aynı uygulama yığınlarını (bundle) yükler ve aynı kayıt defterine (registry) sahiptir, ancak Fusion birçok öğeyi kendisi çizer ve eklenti bileşenlerini kendi yöntemine göre yerleştirir. Uygulamanızı her iki arayüzde de test edin.

Arayüzü algılama

Herhangi bir uygulama yüklenmeden önce, her iki arayüz de window.antimatterWebUI değerini 'fusion' veya 'classic' olarak ayarlar ve aynı değeri sayfanın kök öğesinde data-am-web-ui olarak belirler. Mattermost'ta bu değerlerden hiçbiri ayarlanmaz.

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

Yerleşik uygulamalar, arayüzü farklı şekilde çizmek için bunu kullanır. Anketler, anket kartlarını ve oluşturma iletişim kutusunu Fusion stiline göre çizer ve Fusion'daki /poll komutu için kendi iletişim kutusunu açar. Aramalar ve Ses kanalları, Fusion'un kendi arama arayüzüyle değiştirdiği kenar çubuğu ve bildirim bileşenlerini hariç tutar. Ajanlar uygulaması, AI işlemlerini, kaydırma araç çubuğu yerine Diğer işlemler altında bir grup olarak ileti menüsüne yerleştirir. Bu uygulamaların arayüzle uyumlu olması için kullandığı şey, Fusion'un kendi CSS sınıfları (am- önekiyle) ve iletişim kutusu katmanı (am-layer öğesi)dir; bunlar belgelenmiş ve kararlı bir API değildir.

Fusion'un eklenti bileşenlerini gösterdiği yerler

Kayıt yöntemiFusion'da göründüğü yer
registerAppBarComponent, registerChannelHeaderButtonActionUygulama rayı, sağ kenarda avatarınız ile Antimatter hakkında düğmesi arasındaki simge sütunu. Kanal başlığı düğmeleri de oraya gider, kanal başlığına değil. Sistem yöneticisi Uygulamalar çubuğu kullanılmasın (ExperimentalSettings.DisableAppBar, varsayılan olarak kapalı) seçeneğini açtığında gizlenir.
registerRightHandSidebarComponentSağdaki panel. Kullanıcılar, kenarını sürükleyerek boyutlandırabilir ve yeni bir pencerede açabilir. Uygulama aynı zamanda bir ürünse, panel Tam sayfa olarak aç seçeneğini sunar. Yeni penceredeki panel bölümüne bakın.
registerChannelHeaderIconKanal başlığındaki arama düğmesinin ardından gelen simgeler; telefonlarda başlığın Diğer menüsünde.
registerCallButtonActionKanal başlığındaki arama düğmesi; birden fazla sağlayıcı Arama yapmanın diğer yolları menüsünü paylaşır.
registerPostDropdownMenuActionİletinin İleti işlemleri (⋯) menüsündeki Diğer işlemler alt menüsü; ayrıca konu panelindeki Konu işlemleri menüsünde, Diğer işlemler altında. Fusion, her işlemin önünde uygulamanın simgesini gri renkte çizer: yerleşik uygulamalar için kendi simgesi, aksi halde uygulamanın uygulama rayında gösterdiği simge, yoksa nötr bir simge. İşletme metninin başında metin içermeyen bir öğe (uygulamanın kendisinin çizdiği simge) gizlenir.
registerPostDropdownSubMenuActionTekil işlemlerden sonra, aynı Diğer işlemler alt menüsünde bir başlık içeren grup. Hiçbir uygulama, ileti menüsünde kendi başına bir girişe sahip olmaz.
registerPostActionComponentİletinin üzerine gelinen araç çubuğu, ⋯ düğmesinden önce.
registerPostTypeComponent, registerPostMessageAttachmentComponent, registerPostHeaderComponent, registerPostWillRenderEmbedComponentİletinin kendisi: gövdesi, metninin altında, zamanının ardından ve bağlantı önizlemelerinin yerinde.
registerChannelHeaderMenuActionKanalın Kanal ayarları menüsü (kenar çubuğundaki bir kanalda ⋯ veya sağ tık), fiş simgeli öğeler olarak.
registerMainMenuActionTopluluk menüsü (kanal listesinin üstündeki topluluğun adı), Diğer işlemler altında, uygulamaya göre gruplandırılmış.
registerSidebarBrowseOrAddChannelMenuActionTopluluk menüsü, Kanal oluştur ve Kategori oluştur ardından.
registerFileUploadMethodYazma alanının + menüsü (İletiye ekle), Dosya yükle öğesinin ardından.
registerPostEditorActionComponentYazma alanının biçimlendirme çubuğu, biçimlendirme gösterildiğinde.
registerAIActionMenuItemComponentYazma alanının Yapay zekâ menüsü.
registerFileDropdownMenuAction, registerFilePreviewComponentBir dosyanın seçenek menüsü ve dosya önizlemesi.
registerUserSettingsProfil ve ayarlar, Uygulama ekleri başlığı altında her uygulama için bir sekme.
registerChannelSettingsTabKanal ayarları penceresi, Uygulamalar başlığı altında.
registerPopoverUserAttributesComponent, registerPopoverUserActionsComponentBir adın veya profil fotoğrafının üzerine tıklandığında görünen profil kartı.
registerLeftSidebarHeaderComponent, registerSidebarChannelLinkLabelComponent, registerChannelIconOverride, registerBottomTeamSidebarComponentBir topluluğun kanal listesinin üst kısmı, bir kanalın adının yanında, kanal simgesinin yanında ve sol taraftaki topluluk rayının alt kısmı.
registerChannelIntro, registerChannelIntroButtonAction, registerChannelToastComponent, registerNewMessagesSeparatorActionComponent, registerChannelComposerBannerComponent, registerComposerPlaceholderBir kanalın geçmişinin başlangıcı, ileti listesinin üst kısmı, okunmamış satırı, yazma alanının üstü ve yazma alanının yer tutucusu.
registerChannelTypeOptionKanal oluştur iletişim kutusunun kanal türleri.
registerSearchComponentsKanal başlığının arama kutusu: boş olduğunda uygulama kapsamları görünür.
registerUserGuideDropdownMenuActionAntimatter hakkında (uygulama rayının alt kısmı), Yardım altında.
registerProductÜrünün URL'sindeki tam sayfalar, Antimatter hakkında bölümünde Ürünler altında listelenir. Ürün sayfaları, klasik sayfa çerçevesini korur; ürün menüsü yalnızca bir uygulama ürün kaydeder veya ona bir giriş eklediğinde görünür. Yerleşik Kanban ve Playbooklar uygulamaları artık ürün kaydetmez: sayfaları yan panellerinde açılır.
registerRootComponent, registerGlobalComponent, registerMessageWillBePostedHook, registerSlashCommandWillBePostedHook, registerMessageWillFormatHook, registerFilesWillUploadHook, registerDesktopNotificationHook, registerWebSocketEventHandler, registerReducer, registerTranslations gibi kancalarKlasik arayüzdeki gibi çalışır.
registerAdminConsolePlugin, registerAdminConsoleCustomSettingHer iki arayüzde de klasik olan Sistem Konsolu.

Fusion'un göstermediği veya çalıştırmadığı öğeler

  • registerPostDropdownMenuComponent (ilet menüsündeki bir bileşen), registerPostCardTypeComponent (sağdaki paneldeki bir gönderinin kartı) ve registerProductSwitcherMenuItem, Fusion'un ana görünümlerinde gösterilmez. İlki yerine registerPostDropdownMenuAction kullanın.
  • registerMessageWillBeUpdatedHook, Fusion'da bir iletinin düzenlendiğinde çalışmaz. Düzenlemeleri sunucuda MessageWillBeUpdated kancasıyla kontrol edin.
  • registerActionAfterChannelCreation yalnızca klasik kanal oluşturma iletişim kutusundan çalışır; Fusion bunu Kanal oluştur iletişim kutusundaki Diğer seçenekler… öğesinden açar.
  • Uygulama çubuğu bileşenleri, supportedProductIds değerleri ne olursa olsun gösterilir.
  • registerNeedsTeamRoute ve registerCustomRoute kaynaklı rotalar, ürünler gibi klasik sayfa çerçevesini korur.

Yalnızca Antimatter'da bulunan kancalar

Bu kayıt yöntemleri Mattermost'ta mevcut değildir. Uygulamanın orada da çalışabilmesi için bir yöntemi çağırmadan önce mevcut olup olmadığını kontrol edin:

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');
}
YöntemNe ekler
registerComposerMenuItem({text, icon, fusionIcon, action, shouldRender})Her iki arayüzde de ilet kutusunun + menüsünde bir öğe. action, {channelId, rootId} alır.
registerEmojiPickerTab({id, label, icon, fusionIcon, order, component, shouldRender, replacesGifPicker})İleti kutusunun emoji seçicisindeki bir sekme, örneğin GIF'ler veya çıkartmalar. Fusion'da, order değeri 10'dan düşük olan sekmeler Emoji sekmesinden önce gelir. Bileşen channelId, rootId, filter, onFilterChange, onSelectDone, insertText, isFusion ve Fusion'da pickerKit (aşağıda) parametrelerini alır.
registerChannelViewPanel({matcher, component})Kanal başlığı ile iletiler arasında, matcher(state, channel) tarafından seçilen kanallar için bir panel. Ses kanallarının yaptığı gibi iletileri ve yazma alanını gizleyebilir (setMessagesVisible).
registerSidebarChannelFooterComponent(component)Kenar çubuğundaki bir kanalın altındaki satır, örneğin arama içindeki kişiler. channel değerini alır.
registerIntegrationsOption({title, description, fusionIcon, action, shouldRender})Yalnızca Fusion: Bütünleştirmeler sayfalarındaki bir giriş, özel emojilerin yanında. GIF uygulaması özel GIF'lerini ve çıkartmalarını oraya ekler.

Emoji seçicideki görünümleri ekleyin ve yönetin

Fusion'da bir emoji seçici sekmesi pickerKit özelliğine (prop) sahip olur: Emoji sekmesinin kendi ekleme ve yönetme görünümleri için kullandığı bileşenler, böylece bir sekmenin görünümleri aynı görünüme sahip olur ve seçici içinde kalır. GIFs ve Memes uygulamaları GIF'leri, çıkartmaları ve meme şablonlarını eklemek ve yönetmek için bunu kullanır. version (1), usePickerView ve useOnEscape hook'larını, PickerViewButtons (arama kutusunun yanındaki + ve yönetme düğmeleri), PickerViewFrame (geri oku olan bir görünüm), PickerNotice, PickerField, PickerSearch, PickerDropZone ve PickerManageList bileşenlerini içerir. Escape tuşu, bir görünümden sekmenin ızgarasına geri döner. Özelliğin mevcut olduğundan emin olun: diğer konaklar (hosts) bunu hariç tutar ve bu durumda sekme kendi diyaloglarını korur. Tipler webapp/fusion/src/types/store/plugins.ts (EmojiPickerTabProps) ve bileşenler webapp/fusion/src/fusion/popovers/picker_views.tsx dosyasında bulunur.

Paneli yeni bir pencerede

Fusion, registerRightHandSidebarComponent bileşenine iki ek prop verir; böylece Yeni pencerede aç paneli uygulama ana sayfasında değil, bulunduğu yerde açar:

  • setPanelState(state): panel yer değiştirdiğinde çağırın. Kısa bir dize, örneğin 'note/<id>' veya 'board/<id>/card/<id>', veya ana sayfasında null kullanın. Bu değer pencerenin adresine yazılır, bu yüzden gizli bilgiler asla içermemelidir. 512 karakterden uzun veya denetim karakterleri içeren durumlar düşürülür.
  • initialPanelState: yeni pencerede panelin sahip olduğu durum veya undefined. Bunu güvensiz bir girdi olarak ele alın: öğeyi oturum açan kişi olarak yeniden getirin ve kişi öğeyi açamıyorsa uygulamanın ana sayfasını veya bulunamadı görünümünü gösterin.

Panel kapatıldığında veya başka bir uygulamanın paneli onun yerini aldığında durum unutulur. Bir panelin penceresini kendiniz açmak için popoutRhsPlugin (aynı zamanda window.WebappUtils.popouts içinde) durumu isteğe bağlı beşinci argüman olarak alır. Not, Beyaz tahta, Yapılacaklar, Kanban ve Playbook'lar bunu kullanır.

fusionIcon, Fusion'un simge setindeki bir simgeyi belirtir (örneğin poll, sticker, calendar veya image; bkz. webapp/fusion/src/fusion/components/icon). Fusion, icon yerine bunu gösterir, böylece öğe diğerleriyle uyumlu olur.

Ayarlar

plugin.json içindeki settings_schema, uygulamanın Sistem Konsolu › Uygulama ekleri sayfasını açıklar:

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

Ayar türleri: bool, text, longtext, number, dropdown, radio, generated (rastgele bir gizli anahtar), username ve custom (web uygulaması bölümünün registerAdminConsoleCustomSetting ile kaydettiği bir bileşen tarafından çizilir). Ayarlar sections içinde gruplandırılabilir.

Sunucu bölümü, alanları anahtarlarla eşleşen bir yapıya (struct) okur:

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
}

Yöneticiler, bunları yapılandırmada, PluginSettings.Plugins.<app id> altında, küçük harfli anahtarlarla da ayarlayabilir (bkz. yönetici kılavuzu).

Çeviriler

Antimatter 37 dile çevrilmiştir ve paketlenen uygulamalar da öyle. Web uygulaması bölümü bunu şu şekilde yapar:

  1. Tüm dizeleri React Intl ile, bir kimlik (ID) ve İngilizce metniyle yazın: <FormattedMessage id='polls.create' defaultMessage='Create a poll'/> veya defineMessages. Kimliklerin başına uygulamanızın adını ekleyin.
  2. İngilizce dizeleri webapp/i18n/en.json dosyasına çıkarın (paketlenen uygulamalar formatjs extract kullanır).
  3. Yanına, aynı kimliklere sahip her dil için bir dosya ekleyin: fr.json, de.json, pt-BR.json, zh-CN.json…
  4. Bunları web uygulamasına verin; kullanıcıdan dilini ister:
    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);

Eksik bir dize, İngilizce defaultMessage değerine geri düşer. Sunucu tarafının gönderdiği dizeler için (komut yanıtları, bot iletileri) pluginapi/i18n paketi, paketle birlikte gelen çeviri dosyalarını (örneğin assets/i18n) yükler ve her kullanıcı için kendi dilinde bir yerelleştirici sağlar.

Derleme, kurulum ve hata ayıklama

Go ve Node.js (uygulamanın .nvmrc dosyasındaki sürüm) gerekir. Uygulamanın klasöründe:

KomutNe işe yarar
make distSunucu tarafını her platform için ve web uygulaması tarafını derler, paketi dist/<app id>-<version>.tar.gz konumuna yazar.
make testGo ve JavaScript testlerini çalıştırır.
make check-styleKodu denetler (golangci-lint, ESLint) ve TypeScript türlerini kontrol eder.
make deployDerler, ardından paketi sunucuya kurar ve etkinleştirir.
make watch, make deploy-from-watchİlk komut her kaydedişte web uygulaması tarafını yeniden derler; ikinci komut derlediği içeriği paketler ve sunucuya kurar.
make enable, make disable, make resetUygulamayı sunucuda etkinleştirir, devre dışı bırakır veya yeniden başlatır.
make logs, make logs-watchSunucu günlüğündeki uygulamanın satırlarını gösterir.
make attachÇalışan sunucu tarafına Delve hata ayıklayıcısını bağlar (önce AM_DEBUG=1 ile derleyin).

make deploy ve diğer sunucu komutları, bir yerel mod soketi bulduklarında onu kullanır (/var/tmp/antimatter_local.socket veya AM_LOCALSOCKETPATH): bunları yerel mod açıkken sunucunun makinesinde çalıştırın. Aksi halde MM_SERVICESETTINGS_SITEURL ile belirtilen sunucuya AM_ADMIN_TOKEN veya AM_ADMIN_USERNAME ve AM_ADMIN_PASSWORD ile giriş yaparlar. Derleme araçları henüz site URL'si için bir AM_ adını okumaz.

Bir paketi elle kurun

  • Bir sistem yöneticisi, AM_PLUGINSETTINGS_ENABLEUPLOADS=true ayarıyla yüklemelere izin verir (varsayılan olarak kapalıdır; Sistem Konsolu'nda bir anahtarı yoktur).
  • Ardından Sistem Konsolu › Uygulama ekleri › Uygulama eki yönetimi › Uygulama eki yükle veya amctl plugin add weather.tar.gz ardından amctl plugin enable com.example.weather.
  • Sunucuda Uygulama eki imzası istensin seçeneği açıksa, yalnızca güvendiği bir anahtarla imzalanmış paketleri kabul eder; bkz. Diğer uygulamaların yüklenmesi.
Geliştirme sunucusunda hızlı tekrarlar için sunucuda AM_SERVICESETTINGS_ENABLEDEVELOPER=true ayarını yapın: hatalar bu durumda ayrıntılarını taşır. Paketlenmiş uygulamaların Makefile'ları, kabuğunuzda MM_SERVICESETTINGS_ENABLEDEVELOPER ayarlıysa sunucu tarafını yalnızca sizin platformunuz için derler.