Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@
"notificationTargetUnavailable": "The agent from this notification is no longer available.",
"hostPairNotifications": "Create notification pairing code",
"hostPairingCodeTitle": "Pair the notification plugin",
"hostPairingCodeIntro": "On the Herdr host, copy and run these commands. Replace /path/to/drover with the checkout path. When setup asks, paste the pairing code.",
"hostPairingLinkCommandLabel": "1. Link the plugin (first time only)",
"hostPairingSetupCommandLabel": "2. Run setup",
"hostPairingCodeLabel": "3. Pairing code",
"hostPairingCodeIntro": "Drover couldn't find the notification plugin on this host. Run this on the Herdr host to install it, then tap Create notification pairing code again — Drover will find it and pair for you.",
"hostPairingInstallCommandLabel": "Install the plugin",
"hostPairingManualNote": "Pairing by hand instead? The plugin's own setup script asks for the code and the URL below.",
"hostPairingCodeLabel": "Pairing code",
"hostPairingUrlLabel": "Completion URL",
"hostPairAutoDetectedTitle": "Notification plugin detected",
"hostPairAutoDetectedBody": "Drover found the drover.notify plugin already linked on this host. Set up push notification pairing automatically?",
Expand Down
8 changes: 4 additions & 4 deletions app/lib/l10n/app_ja.arb
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
"notificationTargetUnavailable": "この通知のエージェントは利用できなくなりました。",
"hostPairNotifications": "通知用のペアリングコードを作成",
"hostPairingCodeTitle": "通知 plugin をペアリング",
"hostPairingCodeIntro": "Herdr host で、次のコマンドをコピーして実行してください。/path/to/drover は checkout のパスに置き換えます。setup が表示した prompt にペアリングコードを貼り付けてください。",
"hostPairingLinkCommandLabel": "1. plugin を link(初回のみ)",
"hostPairingSetupCommandLabel": "2. setup を実行",
"hostPairingCodeLabel": "3. ペアリングコード",
"hostPairingCodeIntro": "この host に通知 plugin が見つかりませんでした。Herdr host で次のコマンドを実行してインストールし、もう一度「通知用のペアリングコードを作成」をタップしてください。Drover が検出して自動でペアリングします。",
"hostPairingInstallCommandLabel": "plugin をインストール",
"hostPairingManualNote": "手動でペアリングする場合は、plugin の setup スクリプトが下のコードと URL を尋ねます。",
"hostPairingCodeLabel": "ペアリングコード",
"hostPairingUrlLabel": "完了 URL",
"hostPairAutoDetectedTitle": "通知 plugin を検出しました",
"hostPairAutoDetectedBody": "この host には drover.notify plugin がすでに link されています。プッシュ通知のペアリングを自動設定しますか?",
Expand Down
16 changes: 8 additions & 8 deletions app/lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -323,25 +323,25 @@ abstract class AppLocalizations {
/// No description provided for @hostPairingCodeIntro.
///
/// In en, this message translates to:
/// **'On the Herdr host, copy and run these commands. Replace /path/to/drover with the checkout path. When setup asks, paste the pairing code.'**
/// **'Drover couldn\'t find the notification plugin on this host. Run this on the Herdr host to install it, then tap Create notification pairing code again — Drover will find it and pair for you.'**
String get hostPairingCodeIntro;

/// No description provided for @hostPairingLinkCommandLabel.
/// No description provided for @hostPairingInstallCommandLabel.
///
/// In en, this message translates to:
/// **'1. Link the plugin (first time only)'**
String get hostPairingLinkCommandLabel;
/// **'Install the plugin'**
String get hostPairingInstallCommandLabel;

/// No description provided for @hostPairingSetupCommandLabel.
/// No description provided for @hostPairingManualNote.
///
/// In en, this message translates to:
/// **'2. Run setup'**
String get hostPairingSetupCommandLabel;
/// **'Pairing by hand instead? The plugin\'s own setup script asks for the code and the URL below.'**
String get hostPairingManualNote;

/// No description provided for @hostPairingCodeLabel.
///
/// In en, this message translates to:
/// **'3. Pairing code'**
/// **'Pairing code'**
String get hostPairingCodeLabel;

/// No description provided for @hostPairingUrlLabel.
Expand Down
10 changes: 5 additions & 5 deletions app/lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,17 @@ class AppLocalizationsEn extends AppLocalizations {

@override
String get hostPairingCodeIntro =>
'On the Herdr host, copy and run these commands. Replace /path/to/drover with the checkout path. When setup asks, paste the pairing code.';
'Drover couldn\'t find the notification plugin on this host. Run this on the Herdr host to install it, then tap Create notification pairing code again — Drover will find it and pair for you.';

@override
String get hostPairingLinkCommandLabel =>
'1. Link the plugin (first time only)';
String get hostPairingInstallCommandLabel => 'Install the plugin';

@override
String get hostPairingSetupCommandLabel => '2. Run setup';
String get hostPairingManualNote =>
'Pairing by hand instead? The plugin\'s own setup script asks for the code and the URL below.';

@override
String get hostPairingCodeLabel => '3. Pairing code';
String get hostPairingCodeLabel => 'Pairing code';

@override
String get hostPairingUrlLabel => 'Completion URL';
Expand Down
9 changes: 5 additions & 4 deletions app/lib/l10n/app_localizations_ja.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,17 @@ class AppLocalizationsJa extends AppLocalizations {

@override
String get hostPairingCodeIntro =>
'Herdr host で、次のコマンドをコピーして実行してください。/path/to/drover は checkout のパスに置き換えます。setup が表示した prompt にペアリングコードを貼り付けてください。';
'この host に通知 plugin が見つかりませんでした。Herdr host で次のコマンドを実行してインストールし、もう一度「通知用のペアリングコードを作成」をタップしてください。Drover が検出して自動でペアリングします。';

@override
String get hostPairingLinkCommandLabel => '1. plugin を link(初回のみ)';
String get hostPairingInstallCommandLabel => 'plugin をインストール';

@override
String get hostPairingSetupCommandLabel => '2. setup を実行';
String get hostPairingManualNote =>
'手動でペアリングする場合は、plugin の setup スクリプトが下のコードと URL を尋ねます。';

@override
String get hostPairingCodeLabel => '3. ペアリングコード';
String get hostPairingCodeLabel => 'ペアリングコード';

@override
String get hostPairingUrlLabel => '完了 URL';
Expand Down
23 changes: 11 additions & 12 deletions app/lib/src/screens/host_setup_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,13 @@ class _HostSetupScreenState extends State<HostSetupScreen> {
context: context,
builder: (context) {
final l10n = AppLocalizations.of(context)!;
final pluginPath = '/path/to/drover-notify';
final herdrBin = _shellCommandPath(config.herdrBin);
final linkCommand = '$herdrBin plugin link $pluginPath';
final setupCommand =
'node $pluginPath/bin/setup.mjs --completion-url '
'${_shellQuote(pairing.completionUrl)} --herdr-bin $herdrBin';
// `plugin install` clones from GitHub, so there is no checkout path for
// the user to substitute, and the install location is herdr's to
// choose — which is why no setup command is offered here. Once the
// plugin is installed, [_detectPlugin] finds it and pairing runs
// automatically; the code and URL below are only for pairing by hand.
final installCommand = '$herdrBin plugin install keinstn/drover-notify';
return AlertDialog(
title: Text(l10n.hostPairingCodeTitle),
content: SingleChildScrollView(
Expand All @@ -257,14 +258,12 @@ class _HostSetupScreenState extends State<HostSetupScreen> {
Text(l10n.hostPairingCodeIntro),
const SizedBox(height: 16),
_CopyableValue(
label: l10n.hostPairingLinkCommandLabel,
value: linkCommand,
key: const ValueKey('pairing_install_command'),
label: l10n.hostPairingInstallCommandLabel,
value: installCommand,
),
const SizedBox(height: 16),
_CopyableValue(
label: l10n.hostPairingSetupCommandLabel,
value: setupCommand,
),
Text(l10n.hostPairingManualNote),
const SizedBox(height: 16),
_CopyableValue(
label: l10n.hostPairingCodeLabel,
Expand Down Expand Up @@ -516,7 +515,7 @@ String _shellCommandPath(String value) => value.startsWith('~/')
String _shellQuote(String value) => "'${value.replaceAll("'", "'\"'\"'")}'";

class _CopyableValue extends StatelessWidget {
const _CopyableValue({required this.label, required this.value});
const _CopyableValue({super.key, required this.label, required this.value});

final String label;
final String value;
Expand Down
59 changes: 59 additions & 0 deletions app/test/screens/host_setup_screen_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,65 @@ void main() {
await tester.pumpWidget(const SizedBox());
});

testWidgets(
'the manual pairing dialog offers `plugin install` with no path to edit',
(tester) async {
tester.view.physicalSize = const Size(800, 1600);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);

await tester.pumpWidget(
MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: HostSetupScreen(
initial: const HostConfig(
host: 'example.com',
user: 'dev',
herdrBin: '~/.local/bin/herdr',
privateKeyPem:
'-----BEGIN OPENSSH PRIVATE KEY-----\n'
'abc\n'
'-----END OPENSSH PRIVATE KEY-----',
),
onSubmit: (_) async {},
onCreatePairingCode: (_) async => _samplePairing,
),
),
);

await tester.tap(
find.widgetWithText(OutlinedButton, 'Create notification pairing code'),
);
await tester.pumpAndSettle();

// The one-liner installs from GitHub, so nothing in it is a placeholder
// the user has to replace by hand.
expect(
find.descendant(
of: find.byKey(const ValueKey('pairing_install_command')),
matching: find.textContaining(
'plugin install keinstn/drover-notify',
),
),
findsOneWidget,
);

// Nothing anywhere in the dialog asks the user to substitute a path, and
// the fabricated `node <path>/bin/setup.mjs` step is gone with it.
expect(find.textContaining('/path/to/'), findsNothing);
expect(find.textContaining('plugin link'), findsNothing);
expect(find.textContaining('setup.mjs'), findsNothing);

// The pairing code and completion URL stay, for pairing by hand.
expect(find.text(_samplePairing.code), findsOneWidget);
expect(find.text(_samplePairing.completionUrl), findsOneWidget);

await tester.pumpWidget(const SizedBox());
},
);

testWidgets('detected plugin: confirming auto-pairs and shows success', (
tester,
) async {
Expand Down
26 changes: 18 additions & 8 deletions docs/push-notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,28 @@ installs or updates executable code on the Herdr host.

The plugin lives in its own repo,
[`keinstn/drover-notify`](https://github.com/keinstn/drover-notify). The host
needs Herdr 0.7.0 or newer and Node.js 18 or newer. Clone the plugin repo onto
the host — it's private, so clone over SSH — then link it:
needs Herdr 0.7.5 or newer and Node.js 18 or newer. Install it on the host:

```sh
git clone git@github.com:keinstn/drover-notify.git
herdr plugin link /path/to/drover-notify
herdr plugin install keinstn/drover-notify
```

Once `keinstn/drover-notify` is public, `herdr plugin install
keinstn/drover-notify` will replace the clone step above — **not yet
available** while the repo is private, since `herdr plugin install` only
supports an unauthenticated HTTPS clone.
`herdr plugin install` fetches over unauthenticated HTTPS, which is why this is
the one-line path now that the repo is public.

drover-notify's own README says Herdr 0.7.0, which is right for the plugin by
itself. The floor above is drover's — `kMinHerdrVersion` in
`app/lib/src/herdr/herdr_version.dart` is `(0, 7, 5)` — because below it drover
still reads and steers agents but refuses to start them. **Do not lower this
number to match the plugin's README.**

If you would rather run your own checkout — a fork, or a branch you are testing
— clone it and link that instead:

```sh
git clone https://github.com/keinstn/drover-notify.git
herdr plugin link /path/to/drover-notify
```

## Pair the plugin

Expand Down
Loading