No description
Find a file
toki 538e8d0d78 update roadmap and documentation
- Update README.md and example/README.md
- Update agent-ops/roadmap/current.md and PHASE.md
- Archive self-contained-test-harness.md milestone
- Add docs/ directory
2026-05-25 21:55:31 +09:00
.claude sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
agent-ops update roadmap and documentation 2026-05-25 21:55:31 +09:00
agent-task sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
android update mattermost push plugin implementation and examples 2026-05-25 21:16:56 +09:00
docs update roadmap and documentation 2026-05-25 21:55:31 +09:00
example update roadmap and documentation 2026-05-25 21:55:31 +09:00
ios initial commit: mattermost-push-plugin 2026-05-25 13:45:37 +09:00
lib update mattermost push plugin implementation and examples 2026-05-25 21:16:56 +09:00
macos initial commit: mattermost-push-plugin 2026-05-25 13:45:37 +09:00
test update mattermost push plugin implementation and examples 2026-05-25 21:16:56 +09:00
.aiexclude sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
.clineignore sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
.clinerules sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
.cursorignore sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
.cursorrules sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
.geminiignore sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
.gitignore chore: update .gitignore, README, and add agent-ops roadmap 2026-05-25 14:53:54 +09:00
.metadata initial commit: mattermost-push-plugin 2026-05-25 13:45:37 +09:00
AGENTS.md sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
analysis_options.yaml initial commit: mattermost-push-plugin 2026-05-25 13:45:37 +09:00
CHANGELOG.md initial commit: mattermost-push-plugin 2026-05-25 13:45:37 +09:00
CLAUDE.md sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
GEMINI.md sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
LICENSE initial commit: mattermost-push-plugin 2026-05-25 13:45:37 +09:00
opencode.json sync agent-ops, add documentation files and updates 2026-05-25 14:33:55 +09:00
pubspec.yaml initial commit: mattermost-push-plugin 2026-05-25 13:45:37 +09:00
README.md update roadmap and documentation 2026-05-25 21:55:31 +09:00

mattermost_push_plugin

Android-first Flutter plugin for Mattermost push notifications.

The plugin owns the push-notification domain that should not be duplicated in each consuming app: native FCM handling, notification display, notification tap events, inline replies, dismiss handling, ACK delivery, device-token storage, auth-token storage, and Mattermost signing-key storage.

Platform Status

Platform Status Notes
Android Production target Native FCM service, Room storage, JJWT verification, OkHttp ACK client, notification builder, inline reply receiver, dismiss receiver
iOS Stub Plugin scaffold exists, behavior is not implemented yet
macOS Stub Plugin scaffold exists, behavior is not implemented yet

Repository Layout

mattermost-push-plugin/
  lib/                    # Public Dart API and Flutter-facing behavior
  android/                # Android native plugin implementation
  ios/                    # iOS plugin scaffold
  macos/                  # macOS plugin scaffold
  test/                   # Dart unit tests for the plugin contract
  example/                # Thin Flutter app used as the integration test harness
    test/                 # Widget tests for the harness app
    integration_test/     # Device/emulator integration tests

The example/ app is the official independent consumer app for this repository. Use it to validate plugin integration without coupling tests to any real product app. Consuming apps should only test their own wiring, such as initialization, credential handoff, and navigation callbacks.

How It Works

graph TD
    A[Mattermost Server] -->|Secure JWT Push| B(Android FCM Service)
    B -->|Verify signing key| C{Valid?}
    C -->|Yes| D[Store payload and build notification]
    C -->|No| E[Drop push]
    D -->|Send ACK| A
    D -->|Display notification| F(Android status bar)
    F -->|Tap| G[Flutter opened event]
    F -->|Inline reply| H[Reply receiver]
    H -->|Send reply| A
    G -->|Stream and callbacks| I(Host app navigation)

Host App Responsibilities

The plugin isolates push handling, but the host application still owns app-level integration:

  1. Configure Firebase for the host app, including google-services.json under android/app/.
  2. Initialize Firebase as required by the host app before push registration.
  3. Call MattermostPushPlugin.instance.initialize() during app startup.
  4. Save auth tokens and signing keys after login.
  5. Clear auth tokens on logout.
  6. Send the formatted device token to the Mattermost server.
  7. Connect notification-open callbacks to the host app router.

Installation

For local development or another app in the same workspace, use a path dependency:

dependencies:
  mattermost_push_plugin:
    path: ../mattermost-push-plugin

For a private Git dependency, point the consuming app at the repository and pin the branch, tag, or commit according to your release process.

Dart Usage

Import the public API:

import 'package:mattermost_push_plugin/mattermost_push_plugin.dart';

Initialize the plugin:

await MattermostPushPlugin.instance.initialize();

Listen to raw notification events:

MattermostPushPlugin.instance.onNotification.listen((data) {
  // data includes the native payload plus a push type.
});

Listen to notification-open events:

MattermostPushPlugin.instance.onNotificationOpened.listen((event) {
  final serverUrl = event.serverUrl;
  final channelId = event.channelId;
  final rootId = event.rootId;
});

Register navigation callbacks:

MattermostPushPlugin.instance.onNavigateToChannel = (
  String serverUrl,
  String channelId,
) {
  // Navigate to the channel in the host app.
};

MattermostPushPlugin.instance.onNavigateToThread = (
  String serverUrl,
  String rootId,
) {
  // Navigate to the CRT thread in the host app.
};

Handle device-token registration:

MattermostPushPlugin.instance.onDeviceTokenReady = (String deviceToken) {
  // Example format: android_rn-v2:YOUR_FCM_TOKEN
  // Send this token to the Mattermost server.
};

Store credentials and signing keys:

await MattermostPushPlugin.instance.setAuthToken(
  'https://mattermost.example.com',
  'USER_AUTH_TOKEN',
  identifier: 'user_session_id',
);

await MattermostPushPlugin.instance.setSigningKey(
  'https://mattermost.example.com',
  'SERVER_PUBLIC_SIGNING_KEY',
);

await MattermostPushPlugin.instance.clearAuthToken(
  'https://mattermost.example.com',
);

final token = await MattermostPushPlugin.instance.getDeviceToken();

Event Types

The Dart API currently recognizes these native event types:

Type Meaning
message A Mattermost message notification
clear Notification clear or dismiss event
session Session-related event
token_refresh Device-token refresh event
opened User opened a notification

NotificationOpenedEvent parses server_url, channel_id, root_id, and is_crt_enabled. If CRT is enabled and root_id is present, the plugin calls onNavigateToThread; otherwise it calls onNavigateToChannel when a channel is available.

Android Notes

The Android manifest declares the plugin's own MattermostFirebaseMessagingService and removes the default FlutterFire messaging components with tools:node="remove". This avoids duplicate FCM service registration when the host app also depends on firebase_messaging.

Host apps should not redeclare duplicate FCM services unless they intentionally coordinate that behavior with this plugin.

Android Gradle tests require a configured Android SDK through ANDROID_HOME or example/android/local.properties.

Development Commands

Run Dart analysis and unit tests from the repository root:

flutter analyze
flutter test

Run harness-app checks:

cd example
flutter analyze
flutter test
flutter test integration_test

Run Android native unit tests:

cd example/android
./gradlew testDebugUnitTest

If the local machine does not have an Android SDK or Android device/emulator, use the separate SSH-based Android test environment documented in docs/android-test-environment.md.

Test Strategy

Use deterministic tests for repository-owned behavior. The example integration test can inject a debug native event through the plugin method channel so the EventChannel and opened-routing path can run without Firebase FCM.

Use the repository tests as the source of truth for plugin behavior:

  1. Root test/ covers Dart API behavior, event parsing, callback routing, and device-token formatting.
  2. android/src/test/ should cover native payload parsing, signing-key lookup, local storage, notification behavior, and ACK request behavior.
  3. example/test/ covers the thin Flutter harness app.
  4. example/integration_test/ covers real plugin registration, method-channel behavior, event-channel behavior, notification-open routing, and manifest integration on a device or emulator using the test/debug injection path.
  5. Manual smoke tests cover true Firebase FCM delivery and real Mattermost ACK flows because they depend on external infrastructure.

Manual Smoke Checklist

Use this checklist before promoting a plugin change to a consuming app:

  • FCM payload wakes MattermostFirebaseMessagingService.
  • Valid Mattermost push payload passes signing-key verification.
  • Invalid or unsigned payload is dropped.
  • System notification displays the expected title, message, avatar, and CRT threading behavior.
  • ACK request is sent to the Mattermost server and receives a successful response.
  • Tapping a notification launches or resumes the app and emits onNotificationOpened.
  • Channel notifications call onNavigateToChannel.
  • CRT thread notifications call onNavigateToThread.
  • Inline reply action sends the reply through the native receiver.
  • Dismiss action clears native notification records as expected.
  • Device token is stored with the android_rn-v2: prefix.

Current Limitations

  • Android is the only implemented runtime target.
  • iOS and macOS are scaffolded but no-op.
  • Full FCM delivery cannot be reliably automated in a headless test runner.
  • The example/ app is intentionally small and should not grow into a product app.