Skip to content

Commit 2ffeba0

Browse files
authored
Merge pull request #503 from OpenPecha/chore/SubplansLinkShare
Chore/subplans link share
2 parents dc39bf1 + 7462ec3 commit 2ffeba0

25 files changed

Lines changed: 259 additions & 29 deletions

ios/Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -404,11 +404,11 @@ SPEC CHECKSUMS:
404404
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
405405
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
406406
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
407-
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
408-
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
409-
permission_handler_apple: ee2fe0fd04551b304eb002714ff067c371c822ed
407+
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
408+
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
409+
permission_handler_apple: 92d754bbaa7361d436db2d6c3c1c2a0fdcec462e
410410
PostHog: 82eee1ba92efc8144b06b6e4412abba5438c8fda
411-
posthog_flutter: 70c295afe8f9eb52e99eefe031af0001bdf9b735
411+
posthog_flutter: 343db84a70d44b12fa0fe2fc35bbb58098074bd4
412412
PromisesObjC: 752c3227f599e3467650e47ea36f433eeb10c273
413413
PromisesSwift: 217dea0fd5d2ad65222a109c48698add13cc1c5b
414414
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf

lib/core/config/router/app_router.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,10 +464,15 @@ final appRouterProvider = Provider<GoRouter>((ref) {
464464
final extra = state.extra as Map<String, dynamic>?;
465465
final plan = extra?['plan'] as Plan?;
466466
final seriesId = extra?['seriesId'] as String?;
467+
final selectedDay = extra?['selectedDay'] as int?;
467468
if (plan == null) {
468469
throw Exception('Missing required parameters');
469470
}
470-
return PlanPreviewDetails(plan: plan, seriesId: seriesId);
471+
return PlanPreviewDetails(
472+
plan: plan,
473+
seriesId: seriesId,
474+
initialDay: selectedDay,
475+
);
471476
},
472477
),
473478
GoRoute(

lib/core/deep_linking/app_links_deep_link_service.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class AppLinksDeepLinkService {
2121
DateTime? _lastDispatchedAt;
2222
bool _initialized = false;
2323
void Function(int tabIndex)? _tabSetter;
24+
void Function(String planId, int? dayNumber, String? planLanguage)?
25+
_planNavigator;
2426

2527
static const Duration _duplicateDispatchWindow = Duration(seconds: 5);
2628

@@ -59,6 +61,13 @@ class AppLinksDeepLinkService {
5961
_tabSetter = setter;
6062
}
6163

64+
void setPlanNavigator(
65+
void Function(String planId, int? dayNumber, String? planLanguage)
66+
navigator,
67+
) {
68+
_planNavigator = navigator;
69+
}
70+
6271
bool drainPendingLink() {
6372
final router = _router;
6473
if (router == null) return false;
@@ -152,6 +161,7 @@ class AppLinksDeepLinkService {
152161
source: 'app_links',
153162
baseLocation: baseLocation,
154163
tabSetter: _tabSetter,
164+
planNavigator: _planNavigator,
155165
);
156166
if (routed) {
157167
_lastDispatchedUri = uri;

lib/core/deep_linking/deep_link_router.dart

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class DeepLinkRouter {
1515
required String source,
1616
String? baseLocation,
1717
void Function(int tabIndex)? tabSetter,
18+
void Function(String planId, int? dayNumber, String? planLanguage)?
19+
planNavigator,
1820
}) {
1921
try {
2022
final destination = _resolveRoute(uri);
@@ -23,6 +25,20 @@ class DeepLinkRouter {
2325
return false;
2426
}
2527

28+
// Plan links can't be expressed as a plain location: opening a specific
29+
// plan requires resolving the plan model from its id, which the injected
30+
// [planNavigator] handles (mirroring the PLAN push-notification path).
31+
// When no navigator is wired (e.g. the Airbridge path), fall through to
32+
// the destination's fallback location instead.
33+
final planId = destination.planId;
34+
if (planId != null && planNavigator != null) {
35+
_logger.info(
36+
'Deep link from $source -> plan $planId day ${destination.dayNumber} lang ${destination.planLanguage} ($uri)',
37+
);
38+
planNavigator(planId, destination.dayNumber, destination.planLanguage);
39+
return true;
40+
}
41+
2642
_logger.info('Deep link from $source -> ${destination.location} ($uri)');
2743
if (destination.opensOnTop && baseLocation != null) {
2844
router.go(baseLocation);
@@ -140,6 +156,29 @@ class DeepLinkRouter {
140156
return const _DeepLinkDestination(AppRoutes.home, tabIndex: _meTabIndex);
141157
}
142158

159+
if (segments.length >= 3 &&
160+
segments[0] == 'open' &&
161+
segments[1] == 'plan') {
162+
final planId = segments[2];
163+
// /open/plan/{planId}/day/{dayNumber}?lang={language} — specific day deep link
164+
int? dayNumber;
165+
if (segments.length >= 5 && segments[3] == 'day') {
166+
dayNumber = int.tryParse(segments[4]);
167+
}
168+
// lang carries the content language the plan was enrolled in, so the
169+
// recipient's app can find the enrollment even across locale differences.
170+
final planLanguage = uri.queryParameters['lang'];
171+
// Fallback location (My Practices) is used only when no planNavigator is
172+
// wired; otherwise the navigator resolves and opens the specific plan.
173+
return _DeepLinkDestination(
174+
AppRoutes.practiceMyPractices,
175+
planId: planId,
176+
dayNumber: dayNumber,
177+
planLanguage: planLanguage,
178+
opensOnTop: true,
179+
);
180+
}
181+
143182
if (segments.length >= 3 &&
144183
segments[0] == 'open' &&
145184
segments[1] == 'group') {
@@ -208,10 +247,26 @@ class _DeepLinkDestination {
208247
/// the `/home` route (e.g. the Me tab).
209248
final int? tabIndex;
210249

250+
/// When non-null, this destination targets a specific plan that must be
251+
/// resolved from its id by the injected `planNavigator`. [location] then
252+
/// acts only as a fallback for callers without a navigator.
253+
final String? planId;
254+
255+
/// When non-null, the plan navigator should open this specific day instead
256+
/// of computing today's day from the plan start date.
257+
final int? dayNumber;
258+
259+
/// Content language of the shared plan (e.g. 'en', 'bo'). Passed to the
260+
/// plan navigator so it can find the enrollment across locale differences.
261+
final String? planLanguage;
262+
211263
const _DeepLinkDestination(
212264
this.location, {
213265
this.extra,
214266
this.opensOnTop = false,
215267
this.tabIndex,
268+
this.planId,
269+
this.dayNumber,
270+
this.planLanguage,
216271
});
217272
}

lib/core/deep_linking/deep_link_url_builder.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,37 @@ class DeepLinkUrlBuilder {
2525
);
2626
}
2727

28+
static Uri planLink({required String planId}) {
29+
return Uri(
30+
scheme: 'https',
31+
host: _host,
32+
pathSegments: ['open', 'plan', planId],
33+
);
34+
}
35+
36+
/// Returns a link that opens a specific day inside a plan.
37+
///
38+
/// Format: https://webuddhist.com/open/plan/{planId}/day/{dayNumber}?lang={language}
39+
///
40+
/// [language] is the content language of the plan (e.g. 'en', 'bo'). It is
41+
/// included so the recipient's app can locate the correct enrollment even when
42+
/// their app locale differs from the sharer's.
43+
static Uri planDayLink({
44+
required String planId,
45+
required int dayNumber,
46+
String? language,
47+
}) {
48+
final queryParameters = <String, String>{
49+
if (language != null && language.isNotEmpty) 'lang': language,
50+
};
51+
return Uri(
52+
scheme: 'https',
53+
host: _host,
54+
pathSegments: ['open', 'plan', planId, 'day', dayNumber.toString()],
55+
queryParameters: queryParameters.isNotEmpty ? queryParameters : null,
56+
);
57+
}
58+
2859
static Uri readerSegmentLink({
2960
required String textId,
3061
required String segmentId,

lib/core/l10n/app_bo.arb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,5 +825,6 @@
825825
"license_with_value": "ཆོག་མཆན། {value}",
826826
"loading_previous_pages": "སྔོན་མ་འཇུག་བཞིན་པ།.. (ཤོག་གྲངས་ {count})",
827827
"loading_more_pages": "མང་བ་འཇུག་བཞིན་པ།.. (ཤོག་གྲངས་ {count})",
828-
"drag_to_resize": "ཆེ་ཆུང་བསྒྱུར་བར་འདྲུད་རོགས།"
828+
"drag_to_resize": "ཆེ་ཆུང་བསྒྱུར་བར་འདྲུད་རོགས།",
829+
"day_completion_share_message": "ངས་ WeBuddhist ཐོག་ཉམས་ལེན་གྱི་ཉིན་གཅིག་མཐར་ཕྱིན་སོང་། ང་དང་ལྷན་དུ་ཉིན་རེའི་ཉམས་ལེན་གོམ་པ་འཇོག་རོགས།"
829830
}

lib/core/l10n/app_en.arb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -994,5 +994,6 @@
994994
}
995995
}
996996
},
997-
"drag_to_resize": "Drag to resize"
997+
"drag_to_resize": "Drag to resize",
998+
"day_completion_share_message": "I just completed a day of my practice on WeBuddhist. Join me and build a daily practice habit together."
998999
}

lib/core/l10n/app_hi.arb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,5 +891,6 @@
891891
"license_with_value": "लाइसेंस: {value}",
892892
"loading_previous_pages": "पिछला लोड हो रहा है... ({count} पृष्ठ)",
893893
"loading_more_pages": "और लोड हो रहा है... ({count} पृष्ठ)",
894-
"drag_to_resize": "आकार बदलने के लिए खींचें"
894+
"drag_to_resize": "आकार बदलने के लिए खींचें",
895+
"day_completion_share_message": "मैंने WeBuddhist पर अपने अभ्यास का एक दिन पूरा किया। मेरे साथ जुड़ें और साथ मिलकर रोज़ाना अभ्यास की आदत बनाएं।"
895896
}

lib/core/l10n/app_mn.arb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,5 +891,6 @@
891891
"license_with_value": "Лиценз: {value}",
892892
"loading_previous_pages": "Өмнөхийг ачаалж байна... ({count} хуудас)",
893893
"loading_more_pages": "Илүү ихийг ачаалж байна... ({count} хуудас)",
894-
"drag_to_resize": "Хэмжээг өөрчлөхийн тулд чирнэ үү"
894+
"drag_to_resize": "Хэмжээг өөрчлөхийн тулд чирнэ үү",
895+
"day_completion_share_message": "Би WeBuddhist дээр өөрийн дадлагын нэг өдрийг дуусгалаа. Надтай нэгдэж, хамтдаа өдөр тутмын дадлагын зуршил бий болгоцгооё."
895896
}

lib/core/l10n/app_ne.arb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,5 +891,6 @@
891891
"license_with_value": "इजाजतपत्र: {value}",
892892
"loading_previous_pages": "अघिल्लो लोड हुँदैछ... ({count} पृष्ठ)",
893893
"loading_more_pages": "थप लोड हुँदैछ... ({count} पृष्ठ)",
894-
"drag_to_resize": "आकार परिवर्तन गर्न तान्नुहोस्"
894+
"drag_to_resize": "आकार परिवर्तन गर्न तान्नुहोस्",
895+
"day_completion_share_message": "मैले WeBuddhist मा आफ्नो अभ्यासको एक दिन पूरा गरें। मसँगै सामेल हुनुस् र एकसाथ दैनिक अभ्यास बानी निर्माण गरौं।"
895896
}

0 commit comments

Comments
 (0)