fix:购买商品饰品
This commit is contained in:
1 parent
1f9a8b67a2
commit
e29ed0a2c8
5 files changed
+88
-4
No files matched your search
@@ -14,6 +14,7 @@ class MallEntryController {
|
|||||||
|
|
||||||
Future<MallEntryOpenResult> open(
|
Future<MallEntryOpenResult> open(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
|
MallEntryRequest request = MallEntryRequest.home,
|
||||||
MallTabConfig? initialConfig,
|
MallTabConfig? initialConfig,
|
||||||
}) async {
|
}) async {
|
||||||
if (_opening) return MallEntryOpenResult.alreadyOpening;
|
if (_opening) return MallEntryOpenResult.alreadyOpening;
|
||||||
@@ -42,6 +43,7 @@ class MallEntryController {
|
|||||||
final uri = config.attributedUri(
|
final uri = config.attributedUri(
|
||||||
clickId: clickId,
|
clickId: clickId,
|
||||||
isDark: Theme.of(context).brightness == Brightness.dark,
|
isDark: Theme.of(context).brightness == Brightness.dark,
|
||||||
|
entry: request,
|
||||||
);
|
);
|
||||||
if (uri == null) return MallEntryOpenResult.unavailable;
|
if (uri == null) return MallEntryOpenResult.unavailable;
|
||||||
// The bottom mall entry is always resident. Server open-mode metadata is
|
// The bottom mall entry is always resident. Server open-mode metadata is
|
||||||
|
|||||||
@@ -11,6 +11,33 @@ import '../../../common/network/dio_provider.dart';
|
|||||||
/// 金值商城广告位的公共契约;商城是否展示完全由服务端配置决定。
|
/// 金值商城广告位的公共契约;商城是否展示完全由服务端配置决定。
|
||||||
const mallSourceApp = 'jinzhi';
|
const mallSourceApp = 'jinzhi';
|
||||||
|
|
||||||
|
/// The native entry that opened the resident mall. H5 uses this context to
|
||||||
|
/// choose the appropriate landing flow while sharing the same WebView host.
|
||||||
|
class MallEntryRequest {
|
||||||
|
const MallEntryRequest({
|
||||||
|
required this.sourceEntry,
|
||||||
|
required this.hookType,
|
||||||
|
this.entryScene,
|
||||||
|
this.entryMetal,
|
||||||
|
});
|
||||||
|
|
||||||
|
static const home = MallEntryRequest(
|
||||||
|
sourceEntry: 'mall_tab',
|
||||||
|
hookType: 'mall_tab',
|
||||||
|
);
|
||||||
|
static const quoteRetailGold = MallEntryRequest(
|
||||||
|
sourceEntry: 'market_quote',
|
||||||
|
hookType: 'quote_cta',
|
||||||
|
entryScene: 'retail',
|
||||||
|
entryMetal: 'au',
|
||||||
|
);
|
||||||
|
|
||||||
|
final String sourceEntry;
|
||||||
|
final String hookType;
|
||||||
|
final String? entryScene;
|
||||||
|
final String? entryMetal;
|
||||||
|
}
|
||||||
|
|
||||||
class MallTabConfig {
|
class MallTabConfig {
|
||||||
const MallTabConfig({
|
const MallTabConfig({
|
||||||
required this.enabled,
|
required this.enabled,
|
||||||
@@ -49,7 +76,11 @@ class MallTabConfig {
|
|||||||
|
|
||||||
Uri? get initialUri => attributedUri(clickId: '');
|
Uri? get initialUri => attributedUri(clickId: '');
|
||||||
|
|
||||||
Uri? attributedUri({required String clickId, bool isDark = false}) {
|
Uri? attributedUri({
|
||||||
|
required String clickId,
|
||||||
|
bool isDark = false,
|
||||||
|
MallEntryRequest entry = MallEntryRequest.home,
|
||||||
|
}) {
|
||||||
final uri = Uri.tryParse(url.trim());
|
final uri = Uri.tryParse(url.trim());
|
||||||
if (!_isAllowed(uri)) return null;
|
if (!_isAllowed(uri)) return null;
|
||||||
return uri!.replace(
|
return uri!.replace(
|
||||||
@@ -59,9 +90,13 @@ class MallTabConfig {
|
|||||||
? uri.queryParameters['from']!
|
? uri.queryParameters['from']!
|
||||||
: mallSourceApp,
|
: mallSourceApp,
|
||||||
'source_app': mallSourceApp,
|
'source_app': mallSourceApp,
|
||||||
'source_entry': 'mall_tab',
|
'source_entry': entry.sourceEntry,
|
||||||
'placement': 'mall_tab',
|
'placement': 'mall_tab',
|
||||||
'hook_type': 'mall_tab',
|
'hook_type': entry.hookType,
|
||||||
|
if (entry.entryScene?.isNotEmpty == true)
|
||||||
|
'entry_scene': entry.entryScene!,
|
||||||
|
if (entry.entryMetal?.isNotEmpty == true)
|
||||||
|
'entry_metal': entry.entryMetal!,
|
||||||
'target_app': 'mall',
|
'target_app': 'mall',
|
||||||
if (adId.isNotEmpty) 'ad_id': adId,
|
if (adId.isNotEmpty) 'ad_id': adId,
|
||||||
if (trackingId.isNotEmpty) 'tracking_id': trackingId,
|
if (trackingId.isNotEmpty) 'tracking_id': trackingId,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import 'package:shadcn_ui/shadcn_ui.dart';
|
|||||||
import '../../../common/domain/metal_type.dart';
|
import '../../../common/domain/metal_type.dart';
|
||||||
import '../../../common/domain/money.dart';
|
import '../../../common/domain/money.dart';
|
||||||
import '../../../common/widgets/adaptive.dart';
|
import '../../../common/widgets/adaptive.dart';
|
||||||
|
import '../../mall/application/mall_entry_controller.dart';
|
||||||
|
import '../../mall/application/mall_tab_config.dart';
|
||||||
import '../application/exchange_calculator_controller.dart';
|
import '../application/exchange_calculator_controller.dart';
|
||||||
import '../application/market_controller.dart';
|
import '../application/market_controller.dart';
|
||||||
import '../data/market_models.dart';
|
import '../data/market_models.dart';
|
||||||
@@ -93,6 +95,28 @@ class MarketPage extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
FilledButton.icon(
|
||||||
|
key: const ValueKey('market-mall-retail-cta'),
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await ref
|
||||||
|
.read(mallEntryControllerProvider)
|
||||||
|
.open(
|
||||||
|
context,
|
||||||
|
request: MallEntryRequest.quoteRetailGold,
|
||||||
|
);
|
||||||
|
if (!context.mounted ||
|
||||||
|
(result != MallEntryOpenResult.unavailable &&
|
||||||
|
result != MallEntryOpenResult.failed)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('商城暂时无法打开,请稍后重试')),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.shopping_bag_outlined),
|
||||||
|
label: const Text('买商城饰品(全款)'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
_Card(
|
_Card(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ void main() {
|
|||||||
final arguments = buildEnterWebMallBridgeArguments(
|
final arguments = buildEnterWebMallBridgeArguments(
|
||||||
currentUri: Uri.parse(
|
currentUri: Uri.parse(
|
||||||
'https://mall.example.com/?from=jinzhi&source_app=jinzhi'
|
'https://mall.example.com/?from=jinzhi&source_app=jinzhi'
|
||||||
'&source_entry=mall_tab&placement=mall_tab&hook_type=mall_tab'
|
'&source_entry=market_quote&placement=mall_tab&hook_type=quote_cta'
|
||||||
|
'&entry_scene=retail&entry_metal=au'
|
||||||
'&tracking_id=tracking&click_id=click-1&ts=123&isNight=0',
|
'&tracking_id=tracking&click_id=click-1&ts=123&isNight=0',
|
||||||
),
|
),
|
||||||
logged: true,
|
logged: true,
|
||||||
@@ -23,6 +24,10 @@ void main() {
|
|||||||
|
|
||||||
expect(arguments['from'], 'jinzhi');
|
expect(arguments['from'], 'jinzhi');
|
||||||
expect(arguments['source_app'], 'jinzhi');
|
expect(arguments['source_app'], 'jinzhi');
|
||||||
|
expect(arguments['source_entry'], 'market_quote');
|
||||||
|
expect(arguments['hook_type'], 'quote_cta');
|
||||||
|
expect(arguments['entry_scene'], 'retail');
|
||||||
|
expect(arguments['entry_metal'], 'au');
|
||||||
expect(arguments['ad_id'], 'latest-ad');
|
expect(arguments['ad_id'], 'latest-ad');
|
||||||
expect(arguments['tracking_id'], 'latest-tracking');
|
expect(arguments['tracking_id'], 'latest-tracking');
|
||||||
expect(arguments['click_id'], 'latest-click');
|
expect(arguments['click_id'], 'latest-click');
|
||||||
|
|||||||
@@ -44,6 +44,24 @@ void main() {
|
|||||||
expect(external.opensInApp, isFalse);
|
expect(external.opensInApp, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('adds retail quote context to the mall URL for H5', () {
|
||||||
|
const config = MallTabConfig(
|
||||||
|
enabled: true,
|
||||||
|
url: 'https://mall.example.com/home',
|
||||||
|
);
|
||||||
|
|
||||||
|
final uri = config.attributedUri(
|
||||||
|
clickId: 'quote-click',
|
||||||
|
entry: MallEntryRequest.quoteRetailGold,
|
||||||
|
)!;
|
||||||
|
|
||||||
|
expect(uri.queryParameters['source_entry'], 'market_quote');
|
||||||
|
expect(uri.queryParameters['hook_type'], 'quote_cta');
|
||||||
|
expect(uri.queryParameters['entry_scene'], 'retail');
|
||||||
|
expect(uri.queryParameters['entry_metal'], 'au');
|
||||||
|
expect(uri.queryParameters['placement'], 'mall_tab');
|
||||||
|
});
|
||||||
|
|
||||||
test('shows the first-install promo only for a configured mall entry', () {
|
test('shows the first-install promo only for a configured mall entry', () {
|
||||||
const configured = MallTabConfig(
|
const configured = MallTabConfig(
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|||||||
Reference in new issue
Block a user