summaryrefslogtreecommitdiff
path: root/player-android/lib/app_routes.dart
blob: 4d31fbd4e31fdf5badbbc638023acddc8776a5fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/// Centralised route path constants for go_router.
///
/// Keeping these in a standalone file lets screen widgets reference route
/// paths without importing router.dart, which would create circular imports
/// (router.dart imports screen files; screens should not import the router).
abstract final class AppRoutes {
  static const login = '/login';
  static const home = '/home';
  static const mediaDetail = '/media/:id';
  static const share = '/share';

  /// Public share-viewer route — no authentication required.
  ///
  /// The ':token' segment is the opaque share token issued by the server.
  /// This route is intentionally outside the authenticated route set so
  /// anyone with a share link can view the shared media without logging in.
  static const shareViewer = '/share/:token';

  static const settings = '/settings';

  /// First-run setup route shown when no admin account exists yet.
  static const bootstrap = '/bootstrap';

  /// Route that lists media items inside a specific set.
  /// The ':setId' segment is a numeric set identifier.
  static const mediaGrid = '/sets/:setId';

  /// Route for the video player screen.
  /// The ':mediaId' segment identifies the media item to play.
  static const videoPlayer = '/video/:mediaId';

  /// Route for the audio player screen.
  /// The ':mediaId' segment identifies the media item to play.
  static const audioPlayer = '/audio/:mediaId';

  /// Route that lists all podcast feeds (sets where isPodcast is true).
  /// Opens [PodcastListScreen] and supports the SubscribeDialog FAB.
  static const podcasts = '/podcasts';

  /// Route that shows the Continue Watching screen (in-progress media items).
  static const continueWatching = '/continue';

  /// Route for the notes editor screen for a specific media item.
  /// The ':mediaId' segment identifies the media item whose note is edited.
  static const notes = '/notes/:mediaId';

  /// URL prefix used in the router redirect guard to identify share-viewer URLs.
  ///
  /// The go_router pattern [shareViewer] is a template string with a `:token`
  /// placeholder and cannot be used directly as a prefix check.  This constant
  /// encapsulates the literal prefix so [router.dart]'s redirect logic can
  /// reference a named constant rather than embedding a raw string literal
  /// (Dependency Inversion — high-level routing policy depends on this abstraction,
  /// not on a hardcoded '/share/' string).
  static const shareViewerPrefix = '/share/';

  /// Returns the concrete path for the share-viewer page of a given [token].
  static String shareViewerPath(String token) => '/share/$token';

  /// Returns the concrete path for a media-detail page given a numeric [id].
  static String mediaDetailPath(int id) => '/media/$id';

  /// Returns the concrete path for the media-grid page of a given [setId].
  static String mediaGridPath(int setId) => '/sets/$setId';

  /// Returns the concrete path for the video player of a given [mediaId].
  static String videoPlayerPath(String mediaId) => '/video/$mediaId';

  /// Returns the concrete path for the audio player of a given [mediaId].
  static String audioPlayerPath(String mediaId) => '/audio/$mediaId';

  /// Returns the appropriate player path for [type] and [mediaId].
  ///
  /// Centralises the audio-vs-video routing decision so call-sites do not need
  /// to repeat the same if/else.  Audio maps to [audioPlayerPath]; every other
  /// type (including 'video' and unknown) maps to [videoPlayerPath].
  static String playerPathForType(String type, String mediaId) =>
      type == 'audio' ? audioPlayerPath(mediaId) : videoPlayerPath(mediaId);

  /// Route that lists all share links created by the authenticated user.
  /// Opens [MySharesScreen] from Settings.
  static const shares = '/shares';

  /// Route for browsing folders within a set.
  ///
  /// The ':setId' path segment identifies the set; the optional 'path' query
  /// parameter specifies the current subfolder (empty or absent = root).
  static const folderBrowser = '/browse/:setId';

  /// Returns the concrete path for the folder browser of a given [setId].
  ///
  /// [path] is the optional subfolder path; omit or pass null for the root.
  static String folderBrowserPath(int setId, {String? path}) {
    final base = '/browse/$setId';
    if (path == null || path.isEmpty) return base;
    return '$base?path=${Uri.encodeComponent(path)}';
  }

  /// Returns the concrete path for the notes editor of a given [mediaId].
  static String notesPath(String mediaId) => '/notes/$mediaId';

  /// Route that lists episodes for a single podcast set.
  ///
  /// The ':setId' path segment is the numeric podcast set identifier.
  /// Opens [PodcastEpisodesScreen] where the user can see episode titles,
  /// played state, and playback progress.
  static const podcastEpisodes = '/podcasts/:setId/episodes';

  /// Returns the concrete path for the podcast-episodes screen of [setId].
  ///
  /// [setName] is optionally passed as a URL query parameter so the app bar
  /// can show it without an extra API call.
  static String podcastEpisodesPath(int setId, {String? setName}) {
    final base = '/podcasts/$setId/episodes';
    if (setName == null || setName.isEmpty) return base;
    return '$base?name=${Uri.encodeComponent(setName)}';
  }

  /// Route for the admin user management screen.
  ///
  /// Only accessible when the authenticated user has admin privileges.
  /// Shows a list of all registered users and allows creating/deleting accounts.
  static const adminUsers = '/admin/users';
}