feat: add a hub for the client's own pages and gate mews:do action links
This commit is contained in:
parent
df705d831f
commit
b311b9f7dd
21 changed files with 966 additions and 404 deletions
74
app/lib/application/pages/mews_urls.dart
Normal file
74
app/lib/application/pages/mews_urls.dart
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/// The addresses of the client's own pages, under the `mews:` scheme.
|
||||
/// One spelling of the scheme, shared by the navigator and the page builders.
|
||||
library;
|
||||
|
||||
/// Internal addresses come in two shapes. A page address is a navigation and
|
||||
/// joins the walk history; an action address under `do/` changes something and
|
||||
/// leaves the reader where they are.
|
||||
///
|
||||
/// View state (sort, search, tag filter) rides in the query string rather than
|
||||
/// in a signal, because swipe previews are built from the address alone and
|
||||
/// going back should undo a sort.
|
||||
abstract final class MewsUrls {
|
||||
static const scheme = 'mews';
|
||||
|
||||
static const hub = 'mews:hub';
|
||||
static const bookmarks = 'mews:bookmarks';
|
||||
static const subscriptions = 'mews:subscriptions';
|
||||
static const history = 'mews:history';
|
||||
static const settings = 'mews:settings';
|
||||
|
||||
/// Whether [url] belongs to the internal scheme.
|
||||
static bool isInternal(String url) => Uri.tryParse(url)?.scheme == scheme;
|
||||
|
||||
/// Whether [url] changes something instead of going somewhere, e.g.
|
||||
/// mews:do/set-theme?value=dark.
|
||||
static bool isAction(String url) {
|
||||
final uri = Uri.tryParse(url);
|
||||
if (uri == null || uri.scheme != scheme) return false;
|
||||
final segments = uri.pathSegments;
|
||||
return segments.length == 2 && segments.first == 'do';
|
||||
}
|
||||
|
||||
/// The verb of an action address, or null when [url] is not one.
|
||||
static String? verbOf(String url) =>
|
||||
isAction(url) ? Uri.parse(url).pathSegments[1] : null;
|
||||
|
||||
/// A single spelling for every internal address, so the walk history entry
|
||||
/// and the built page's url always compare equal. Bare `mews:` is the hub.
|
||||
static String canonical(String url) {
|
||||
if (!isInternal(url)) return url;
|
||||
final uri = Uri.parse(url);
|
||||
if (uri.path.isEmpty) return hub;
|
||||
return uri.removeFragment().toString();
|
||||
}
|
||||
|
||||
/// An action address carrying [args] as its query string.
|
||||
static String action(String verb, [Map<String, String> args = const {}]) =>
|
||||
Uri(
|
||||
scheme: scheme,
|
||||
path: 'do/$verb',
|
||||
queryParameters: args.isEmpty ? null : args,
|
||||
).toString();
|
||||
|
||||
/// [base] with [changes] merged over its query string. An empty or null
|
||||
/// value drops the parameter, so clearing a search box returns the whole
|
||||
/// list. Merging rather than replacing keeps the other parameters, which
|
||||
/// `Uri.replace(queryParameters:)` would discard.
|
||||
static String page(Uri base, [Map<String, String?> changes = const {}]) {
|
||||
final params = {...base.queryParameters};
|
||||
for (final change in changes.entries) {
|
||||
final value = change.value;
|
||||
if (value == null || value.isEmpty) {
|
||||
params.remove(change.key);
|
||||
} else {
|
||||
params[change.key] = value;
|
||||
}
|
||||
}
|
||||
return Uri(
|
||||
scheme: scheme,
|
||||
path: base.path,
|
||||
queryParameters: params.isEmpty ? null : params,
|
||||
).toString();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue