feat: add prev and next buttons to the address bar on pointer platforms
This commit is contained in:
parent
28ff7ca327
commit
ac9118a453
3 changed files with 90 additions and 9 deletions
|
|
@ -1,12 +1,14 @@
|
||||||
|
import 'package:flutter/foundation.dart' show defaultTargetPlatform, TargetPlatform;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:signals/signals_flutter.dart' show SignalBuilder;
|
import 'package:signals/signals_flutter.dart' show SignalBuilder;
|
||||||
|
|
||||||
import '../../application/internal_pages.dart';
|
import '../../application/internal_pages.dart';
|
||||||
import '../app.dart';
|
import '../app.dart';
|
||||||
|
|
||||||
/// The single-field address bar. The left icon opens the settings page; the
|
/// The single-field address bar. The left icon opens the settings page and,
|
||||||
/// right icons reload, subscribe (only when the page offers something to
|
/// on pointer platforms, carries the back and forward buttons; the right
|
||||||
/// subscribe to) and bookmark the current page.
|
/// icons reload, subscribe (only when the page offers something to subscribe
|
||||||
|
/// to) and bookmark the current page.
|
||||||
class AddressBar extends StatefulWidget {
|
class AddressBar extends StatefulWidget {
|
||||||
const AddressBar({super.key});
|
const AddressBar({super.key});
|
||||||
|
|
||||||
|
|
@ -52,6 +54,14 @@ class _AddressBarState extends State<AddressBar> {
|
||||||
_controller.text = url;
|
_controller.text = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Touch devices navigate with swipes; desktops get buttons.
|
||||||
|
final hasButtons = switch (defaultTargetPlatform) {
|
||||||
|
TargetPlatform.linux ||
|
||||||
|
TargetPlatform.windows ||
|
||||||
|
TargetPlatform.macOS => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
// Only pages with dated entry links or an Atom feed can be subscribed
|
// Only pages with dated entry links or an Atom feed can be subscribed
|
||||||
// to (mews spec, section 6).
|
// to (mews spec, section 6).
|
||||||
final canSubscribe = page != null &&
|
final canSubscribe = page != null &&
|
||||||
|
|
@ -79,12 +89,41 @@ class _AddressBarState extends State<AddressBar> {
|
||||||
labelText: strings.t('reader-address-label'),
|
labelText: strings.t('reader-address-label'),
|
||||||
isDense: true,
|
isDense: true,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
prefixIcon: IconButton(
|
prefixIcon: hasButtons
|
||||||
|
? Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
icon: const Icon(Icons.tune),
|
icon: const Icon(Icons.tune),
|
||||||
tooltip: strings.t('reader-open-settings'),
|
tooltip: strings.t('reader-open-settings'),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
deps.browser.open(InternalPages.settingsUrl),
|
deps.browser.open(InternalPages.settingsUrl),
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
tooltip: strings.t('reader-back'),
|
||||||
|
onPressed: deps.browser.canGoBack
|
||||||
|
? deps.browser.back
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_forward),
|
||||||
|
tooltip: strings.t('reader-forward'),
|
||||||
|
onPressed: deps.browser.canGoForward
|
||||||
|
? deps.browser.forward
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: IconButton(
|
||||||
|
icon: const Icon(Icons.tune),
|
||||||
|
tooltip: strings.t('reader-open-settings'),
|
||||||
|
onPressed: () =>
|
||||||
|
deps.browser.open(InternalPages.settingsUrl),
|
||||||
|
),
|
||||||
|
prefixIconConstraints: hasButtons
|
||||||
|
? const BoxConstraints(minWidth: 138, minHeight: 48)
|
||||||
|
: null,
|
||||||
suffixIcon: Row(
|
suffixIcon: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ reader-address-placeholder = Enter a web address
|
||||||
reader-address-label = Web address
|
reader-address-label = Web address
|
||||||
reader-reload = Reload this page
|
reader-reload = Reload this page
|
||||||
reader-open-settings = Open settings
|
reader-open-settings = Open settings
|
||||||
|
reader-back = Go back
|
||||||
|
reader-forward = Go forward
|
||||||
|
|
||||||
## Settings page
|
## Settings page
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:io' show File;
|
import 'dart:io' show File;
|
||||||
|
|
||||||
import 'package:actors/actors.dart';
|
import 'package:actors/actors.dart';
|
||||||
|
import 'package:flutter/foundation.dart' show TargetPlatform, debugDefaultTargetPlatformOverride;
|
||||||
import 'package:drift/native.dart';
|
import 'package:drift/native.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
@ -383,6 +384,45 @@ void main() {
|
||||||
expect(find.textContaining('Page A'), findsOneWidget);
|
expect(find.textContaining('Page A'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('prev and next buttons navigate on pointer platforms',
|
||||||
|
(tester) async {
|
||||||
|
final stubDeps = AppDependencies(
|
||||||
|
database: MewsDatabase.test(NativeDatabase.memory()),
|
||||||
|
fetch: _FakeFetch({
|
||||||
|
'https://example.com/a.html':
|
||||||
|
_page('Page A', '<h1>Page A</h1><p>The first pages content.</p>'),
|
||||||
|
'https://example.com/b.html':
|
||||||
|
_page('Page B', '<h1>Page B</h1><p>The second pages content.</p>'),
|
||||||
|
}),
|
||||||
|
strings: strings,
|
||||||
|
);
|
||||||
|
// Test bindings default to android; a Linux host shows the buttons.
|
||||||
|
debugDefaultTargetPlatformOverride = TargetPlatform.linux;
|
||||||
|
|
||||||
|
await tester.pumpWidget(MewsApp(deps: stubDeps, strings: strings));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
final back = find.byIcon(Icons.arrow_back);
|
||||||
|
final forward = find.byIcon(Icons.arrow_forward);
|
||||||
|
expect(back, findsOneWidget);
|
||||||
|
expect(forward, findsOneWidget);
|
||||||
|
|
||||||
|
await stubDeps.browser.open('https://example.com/a.html');
|
||||||
|
await stubDeps.browser.open('https://example.com/b.html');
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await tester.tap(back);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(stubDeps.browser.currentPage.value?.url, 'https://example.com/a.html');
|
||||||
|
|
||||||
|
await tester.tap(forward);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(stubDeps.browser.currentPage.value?.url, 'https://example.com/b.html');
|
||||||
|
|
||||||
|
// The invariant check runs before test tear-downs, so reset by hand.
|
||||||
|
debugDefaultTargetPlatformOverride = null;
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('reports new posts once per page', (tester) async {
|
testWidgets('reports new posts once per page', (tester) async {
|
||||||
await pumpApp(tester);
|
await pumpApp(tester);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue