first final test

This commit is contained in:
Mohamad Mahdi Jebeli 2026-02-14 09:46:34 +03:30
parent 8cde87b914
commit 575243e959
22 changed files with 1315 additions and 669 deletions

View File

@ -76,8 +76,13 @@ android {
shrinkResources false shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
debug {
signingConfig signingConfigs.release
} }
} }
}
flutter { flutter {
source '../..' source '../..'

View File

@ -7,17 +7,113 @@
"client": [ "client": [
{ {
"client_info": { "client_info": {
"mobilesdk_app_id": "1:581103504002:android:896e4d41aad9180318829b", "mobilesdk_app_id": "1:581103504002:android:d12a150d3d54570418829b",
"android_client_info": {
"package_name": "com.example.hoshan"
}
},
"oauth_client": [
{
"client_id": "581103504002-kb0klg3vds4qcnt5cc348d402l803jh8.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.example.hoshan",
"certificate_hash": "4fea29f5e9872705f5d86af453b1d77fd4134260"
}
},
{
"client_id": "581103504002-dhn875p11up9ae7k6l5r1kdqs71ap3qf.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyDi2WRiOSEws1alpLitxX0zsX14rT71aPk"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "581103504002-dhn875p11up9ae7k6l5r1kdqs71ap3qf.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "581103504002-2e454pgr7fes6b94ptbt82rpfldehedq.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "com.example.hoshan"
}
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:581103504002:android:8983465e8b4f637d18829b",
"android_client_info": { "android_client_info": {
"package_name": "com.houshan.basa" "package_name": "com.houshan.basa"
} }
}, },
"oauth_client": [
{
"client_id": "581103504002-mugog6e0d224snhmoen4t01g91m4corc.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.houshan.basa",
"certificate_hash": "5f7bdab9d1978fa6ba4bb89ba17cfddb41aa7bdb"
}
},
{
"client_id": "581103504002-qc5f48octc7274mr3n4hgam3fr5pejd3.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.houshan.basa",
"certificate_hash": "df8e828db30cabed5c68362c0a67f760610046ee"
}
},
{
"client_id": "581103504002-dhn875p11up9ae7k6l5r1kdqs71ap3qf.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyDi2WRiOSEws1alpLitxX0zsX14rT71aPk"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "581103504002-dhn875p11up9ae7k6l5r1kdqs71ap3qf.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "581103504002-2e454pgr7fes6b94ptbt82rpfldehedq.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "com.example.hoshan"
}
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:581103504002:android:896e4d41aad9180318829b",
"android_client_info": {
"package_name": "com.houshan.hoshan"
}
},
"oauth_client": [ "oauth_client": [
{ {
"client_id": "581103504002-jqptfhs4cpgqngckh8g6kisd25jhfp35.apps.googleusercontent.com", "client_id": "581103504002-jqptfhs4cpgqngckh8g6kisd25jhfp35.apps.googleusercontent.com",
"client_type": 1, "client_type": 1,
"android_info": { "android_info": {
"package_name": "com.houshan.basa", "package_name": "com.houshan.hoshan",
"certificate_hash": "b64c77112af5000772d60fbe05138e873f6fc118" "certificate_hash": "b64c77112af5000772d60fbe05138e873f6fc118"
} }
}, },
@ -25,7 +121,7 @@
"client_id": "581103504002-t6a9nuq2nnbdjj71s2inc70p4fikl921.apps.googleusercontent.com", "client_id": "581103504002-t6a9nuq2nnbdjj71s2inc70p4fikl921.apps.googleusercontent.com",
"client_type": 1, "client_type": 1,
"android_info": { "android_info": {
"package_name": "com.houshan.basa", "package_name": "com.houshan.hoshan",
"certificate_hash": "5f7bdab9d1978fa6ba4bb89ba17cfddb41aa7bdb" "certificate_hash": "5f7bdab9d1978fa6ba4bb89ba17cfddb41aa7bdb"
} }
}, },

View File

@ -35,6 +35,15 @@
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<meta-data android:name="flutter_deeplinking_enabled" android:value="true"/> <meta-data android:name="flutter_deeplinking_enabled" android:value="true"/>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https" android:host="basaweb.houshan.ai" />
<data android:scheme="http" android:host="basaweb.houshan.ai" />
</intent-filter>
<intent-filter android:autoVerify="true"> <intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>

View File

@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.04492 12.375V8.625" stroke="#3D3D3D" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.875 10.5H7.125" stroke="#3D3D3D" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16.5 8.25V12.75C16.5 15.75 15.75 16.5 12.75 16.5H5.25C2.25 16.5 1.5 15.75 1.5 12.75V5.25C1.5 2.25 2.25 1.5 5.25 1.5H6.375C7.5 1.5 7.7475 1.83 8.175 2.4L9.3 3.9C9.585 4.275 9.75 4.5 10.5 4.5H12.75C15.75 4.5 16.5 5.25 16.5 8.25Z" stroke="#3D3D3D" stroke-miterlimit="10"/>
</svg>

After

Width:  |  Height:  |  Size: 622 B

View File

@ -362,6 +362,10 @@ class $AssetsIconBulkGen {
class $AssetsIconGifGen { class $AssetsIconGifGen {
const $AssetsIconGifGen(); const $AssetsIconGifGen();
/// File path: assets/icon/gif/33.gif
AssetGenImage get a33 =>
const AssetGenImage('assets/icon/gif/33.gif', size: Size(640.0, 360.0));
/// File path: assets/icon/gif/alpha.gif /// File path: assets/icon/gif/alpha.gif
AssetGenImage get alpha => const AssetGenImage('assets/icon/gif/alpha.gif'); AssetGenImage get alpha => const AssetGenImage('assets/icon/gif/alpha.gif');
@ -445,6 +449,7 @@ class $AssetsIconGifGen {
/// List of all assets /// List of all assets
List<AssetGenImage> get values => [ List<AssetGenImage> get values => [
a33,
alpha, alpha,
bell, bell,
beta, beta,
@ -534,6 +539,18 @@ class $AssetsIconNavbarsGen {
class $AssetsIconOutlineGen { class $AssetsIconOutlineGen {
const $AssetsIconOutlineGen(); const $AssetsIconOutlineGen();
/// File path: assets/icon/outline/Houshan features.svg
SvgGenImage get houshanFeatures => const SvgGenImage(
'assets/icon/outline/Houshan features.svg',
size: Size(32.0, 33.0),
);
/// File path: assets/icon/outline/add family.svg
SvgGenImage get addFamily => const SvgGenImage(
'assets/icon/outline/add family.svg',
size: Size(32.0, 33.0),
);
/// File path: assets/icon/outline/add.svg /// File path: assets/icon/outline/add.svg
SvgGenImage get add => SvgGenImage get add =>
const SvgGenImage('assets/icon/outline/add.svg', size: Size(24.0, 24.0)); const SvgGenImage('assets/icon/outline/add.svg', size: Size(24.0, 24.0));
@ -550,6 +567,12 @@ class $AssetsIconOutlineGen {
size: Size(24.0, 24.0), size: Size(24.0, 24.0),
); );
/// File path: assets/icon/outline/arrow-left.svg
SvgGenImage get arrowLeft => const SvgGenImage(
'assets/icon/outline/arrow-left.svg',
size: Size(24.0, 24.0),
);
/// File path: assets/icon/outline/arrow-right.svg /// File path: assets/icon/outline/arrow-right.svg
SvgGenImage get arrowRight => const SvgGenImage( SvgGenImage get arrowRight => const SvgGenImage(
'assets/icon/outline/arrow-right.svg', 'assets/icon/outline/arrow-right.svg',
@ -596,6 +619,12 @@ class $AssetsIconOutlineGen {
size: Size(19.0, 19.0), size: Size(19.0, 19.0),
); );
/// File path: assets/icon/outline/carbon_pedestrian-family.svg
SvgGenImage get carbonPedestrianFamily => const SvgGenImage(
'assets/icon/outline/carbon_pedestrian-family.svg',
size: Size(32.0, 32.0),
);
/// File path: assets/icon/outline/card-add.svg /// File path: assets/icon/outline/card-add.svg
SvgGenImage get cardAdd => const SvgGenImage( SvgGenImage get cardAdd => const SvgGenImage(
'assets/icon/outline/card-add.svg', 'assets/icon/outline/card-add.svg',
@ -630,6 +659,12 @@ class $AssetsIconOutlineGen {
size: Size(16.0, 17.0), size: Size(16.0, 17.0),
); );
/// File path: assets/icon/outline/close-circle.svg
SvgGenImage get closeCircle => const SvgGenImage(
'assets/icon/outline/close-circle.svg',
size: Size(24.0, 24.0),
);
/// File path: assets/icon/outline/coin.svg /// File path: assets/icon/outline/coin.svg
SvgGenImage get coin => SvgGenImage get coin =>
const SvgGenImage('assets/icon/outline/coin.svg', size: Size(25.0, 25.0)); const SvgGenImage('assets/icon/outline/coin.svg', size: Size(25.0, 25.0));
@ -692,6 +727,10 @@ class $AssetsIconOutlineGen {
size: Size(16.0, 16.0), size: Size(16.0, 16.0),
); );
/// File path: assets/icon/outline/edit.svg
SvgGenImage get edit =>
const SvgGenImage('assets/icon/outline/edit.svg', size: Size(24.0, 24.0));
/// File path: assets/icon/outline/element-plus.svg /// File path: assets/icon/outline/element-plus.svg
SvgGenImage get elementPlus => const SvgGenImage( SvgGenImage get elementPlus => const SvgGenImage(
'assets/icon/outline/element-plus.svg', 'assets/icon/outline/element-plus.svg',
@ -722,6 +761,18 @@ class $AssetsIconOutlineGen {
size: Size(16.0, 17.0), size: Size(16.0, 17.0),
); );
/// File path: assets/icon/outline/family.svg
SvgGenImage get family => const SvgGenImage(
'assets/icon/outline/family.svg',
size: Size(328.0, 115.0),
);
/// File path: assets/icon/outline/familyMembers.svg
SvgGenImage get familyMembers => const SvgGenImage(
'assets/icon/outline/familyMembers.svg',
size: Size(30.0, 29.0),
);
/// File path: assets/icon/outline/filter.svg /// File path: assets/icon/outline/filter.svg
SvgGenImage get filter => const SvgGenImage( SvgGenImage get filter => const SvgGenImage(
'assets/icon/outline/filter.svg', 'assets/icon/outline/filter.svg',
@ -734,6 +785,12 @@ class $AssetsIconOutlineGen {
size: Size(16.0, 16.0), size: Size(16.0, 16.0),
); );
/// File path: assets/icon/outline/folder-add.svg
SvgGenImage get folderAdd => const SvgGenImage(
'assets/icon/outline/folder-add.svg',
size: Size(18.0, 18.0),
);
/// File path: assets/icon/outline/gallery-add.svg /// File path: assets/icon/outline/gallery-add.svg
SvgGenImage get galleryAdd => const SvgGenImage( SvgGenImage get galleryAdd => const SvgGenImage(
'assets/icon/outline/gallery-add.svg', 'assets/icon/outline/gallery-add.svg',
@ -746,6 +803,12 @@ class $AssetsIconOutlineGen {
size: Size(18.0, 18.0), size: Size(18.0, 18.0),
); );
/// File path: assets/icon/outline/gift 2.svg
SvgGenImage get gift2 => const SvgGenImage(
'assets/icon/outline/gift 2.svg',
size: Size(86.0, 88.0),
);
/// File path: assets/icon/outline/gift.svg /// File path: assets/icon/outline/gift.svg
SvgGenImage get gift => SvgGenImage get gift =>
const SvgGenImage('assets/icon/outline/gift.svg', size: Size(24.0, 24.0)); const SvgGenImage('assets/icon/outline/gift.svg', size: Size(24.0, 24.0));
@ -786,6 +849,12 @@ class $AssetsIconOutlineGen {
size: Size(16.0, 16.0), size: Size(16.0, 16.0),
); );
/// File path: assets/icon/outline/ion_infinite.svg
SvgGenImage get ionInfinite => const SvgGenImage(
'assets/icon/outline/ion_infinite.svg',
size: Size(32.0, 32.0),
);
/// File path: assets/icon/outline/lamp-charge.svg /// File path: assets/icon/outline/lamp-charge.svg
SvgGenImage get lampCharge => const SvgGenImage( SvgGenImage get lampCharge => const SvgGenImage(
'assets/icon/outline/lamp-charge.svg', 'assets/icon/outline/lamp-charge.svg',
@ -918,6 +987,10 @@ class $AssetsIconOutlineGen {
size: Size(24.0, 24.0), size: Size(24.0, 24.0),
); );
/// File path: assets/icon/outline/send.svg
SvgGenImage get send =>
const SvgGenImage('assets/icon/outline/send.svg', size: Size(16.0, 16.0));
/// File path: assets/icon/outline/setting.svg /// File path: assets/icon/outline/setting.svg
SvgGenImage get setting => const SvgGenImage( SvgGenImage get setting => const SvgGenImage(
'assets/icon/outline/setting.svg', 'assets/icon/outline/setting.svg',
@ -952,6 +1025,12 @@ class $AssetsIconOutlineGen {
SvgGenImage get stop => SvgGenImage get stop =>
const SvgGenImage('assets/icon/outline/stop.svg', size: Size(24.0, 24.0)); const SvgGenImage('assets/icon/outline/stop.svg', size: Size(24.0, 24.0));
/// File path: assets/icon/outline/streamline_coins-stack.svg
SvgGenImage get streamlineCoinsStack => const SvgGenImage(
'assets/icon/outline/streamline_coins-stack.svg',
size: Size(32.0, 32.0),
);
/// File path: assets/icon/outline/sun.svg /// File path: assets/icon/outline/sun.svg
SvgGenImage get sun => SvgGenImage get sun =>
const SvgGenImage('assets/icon/outline/sun.svg', size: Size(18.0, 18.0)); const SvgGenImage('assets/icon/outline/sun.svg', size: Size(18.0, 18.0));
@ -992,6 +1071,12 @@ class $AssetsIconOutlineGen {
size: Size(16.0, 16.0), size: Size(16.0, 16.0),
); );
/// File path: assets/icon/outline/trashPopup.svg
SvgGenImage get trashPopup => const SvgGenImage(
'assets/icon/outline/trashPopup.svg',
size: Size(48.0, 48.0),
);
/// File path: assets/icon/outline/verify.svg /// File path: assets/icon/outline/verify.svg
SvgGenImage get verify => const SvgGenImage( SvgGenImage get verify => const SvgGenImage(
'assets/icon/outline/verify.svg', 'assets/icon/outline/verify.svg',
@ -1010,17 +1095,32 @@ class $AssetsIconOutlineGen {
size: Size(16.0, 16.0), size: Size(16.0, 16.0),
); );
/// File path: assets/icon/outline/wallet.svg
SvgGenImage get wallet => const SvgGenImage(
'assets/icon/outline/wallet.svg',
size: Size(32.0, 33.0),
);
/// File path: assets/icon/outline/warning-2.svg /// File path: assets/icon/outline/warning-2.svg
SvgGenImage get warning2 => const SvgGenImage( SvgGenImage get warning2 => const SvgGenImage(
'assets/icon/outline/warning-2.svg', 'assets/icon/outline/warning-2.svg',
size: Size(24.0, 25.0), size: Size(24.0, 25.0),
); );
/// File path: assets/icon/outline/warning.svg
SvgGenImage get warning => const SvgGenImage(
'assets/icon/outline/warning.svg',
size: Size(32.0, 33.0),
);
/// List of all assets /// List of all assets
List<SvgGenImage> get values => [ List<SvgGenImage> get values => [
houshanFeatures,
addFamily,
add, add,
archiveTick, archiveTick,
arrowFlashRight, arrowFlashRight,
arrowLeft,
arrowRight, arrowRight,
assistant, assistant,
bitcoinRefresh, bitcoinRefresh,
@ -1029,12 +1129,14 @@ class $AssetsIconOutlineGen {
calendarEdit, calendarEdit,
call, call,
camera, camera,
carbonPedestrianFamily,
cardAdd, cardAdd,
cardPos, cardPos,
characters, characters,
chart, chart,
chat, chat,
clock, clock,
closeCircle,
coin, coin,
copy, copy,
courthouse, courthouse,
@ -1046,15 +1148,20 @@ class $AssetsIconOutlineGen {
documentText, documentText,
download, download,
edit2, edit2,
edit,
elementPlus, elementPlus,
emojiHappy, emojiHappy,
emptyWalletTick, emptyWalletTick,
emptyWallet, emptyWallet,
eraser, eraser,
family,
familyMembers,
filter, filter,
flag2, flag2,
folderAdd,
galleryAdd, galleryAdd,
ghost, ghost,
gift2,
gift, gift,
globalSearch, globalSearch,
hat, hat,
@ -1063,6 +1170,7 @@ class $AssetsIconOutlineGen {
idea, idea,
import, import,
infoCircle, infoCircle,
ionInfinite,
lampCharge, lampCharge,
library, library,
like, like,
@ -1087,12 +1195,14 @@ class $AssetsIconOutlineGen {
profileUserDoual, profileUserDoual,
profile, profile,
searchNormal, searchNormal,
send,
setting, setting,
share, share,
shieldTick, shieldTick,
smsTracking, smsTracking,
stopCircle, stopCircle,
stop, stop,
streamlineCoinsStack,
sun, sun,
tickCircle, tickCircle,
tickSquare, tickSquare,
@ -1100,10 +1210,13 @@ class $AssetsIconOutlineGen {
toolBox, toolBox,
translate, translate,
trash, trash,
trashPopup,
verify, verify,
videoPlay, videoPlay,
voiceCricle, voiceCricle,
wallet,
warning2, warning2,
warning,
]; ];
} }
@ -1130,6 +1243,12 @@ class $AssetsIconSocialGen {
class $AssetsImageBoardingsGen { class $AssetsImageBoardingsGen {
const $AssetsImageBoardingsGen(); const $AssetsImageBoardingsGen();
/// File path: assets/image/boardings/AI Houshan.svg
SvgGenImage get aIHoushan => const SvgGenImage(
'assets/image/boardings/AI Houshan.svg',
size: Size(328.0, 88.0),
);
/// File path: assets/image/boardings/drei.png /// File path: assets/image/boardings/drei.png
AssetGenImage get drei => const AssetGenImage( AssetGenImage get drei => const AssetGenImage(
'assets/image/boardings/drei.png', 'assets/image/boardings/drei.png',
@ -1167,7 +1286,7 @@ class $AssetsImageBoardingsGen {
); );
/// List of all assets /// List of all assets
List<AssetGenImage> get values => [drei, eins, funf, sechs, vier, zwei]; List<dynamic> get values => [aIHoushan, drei, eins, funf, sechs, vier, zwei];
} }
class $AssetsImageEmptyGen { class $AssetsImageEmptyGen {
@ -1191,6 +1310,12 @@ class $AssetsImageEmptyGen {
size: Size(347.0, 433.0), size: Size(347.0, 433.0),
); );
/// File path: assets/image/empty/empty state 1 1.png
AssetGenImage get emptyState11 => const AssetGenImage(
'assets/image/empty/empty state 1 1.png',
size: Size(328.0, 351.0),
);
/// File path: assets/image/empty/empty-text-underline.svg /// File path: assets/image/empty/empty-text-underline.svg
SvgGenImage get emptyTextUnderline => const SvgGenImage( SvgGenImage get emptyTextUnderline => const SvgGenImage(
'assets/image/empty/empty-text-underline.svg', 'assets/image/empty/empty-text-underline.svg',
@ -1226,6 +1351,7 @@ class $AssetsImageEmptyGen {
amount, amount,
assistant, assistant,
connection, connection,
emptyState11,
emptyTextUnderline, emptyTextUnderline,
inbox, inbox,
messages, messages,

View File

@ -180,20 +180,20 @@ class Routes {
navigatorKey: navigatorKey, navigatorKey: navigatorKey,
redirect: (context, state) async { redirect: (context, state) async {
final uri = state.uri; final uri = state.uri;
if (uri.scheme == 'houshan' && uri.host == 'auth') {
final token = uri.queryParameters['token']; final token = uri.queryParameters['token'];
if (token != null && token.isNotEmpty) { final hasToken = token != null && token.isNotEmpty;
final isCustomScheme = uri.scheme == 'houshan' && uri.host == 'auth';
if (hasToken) {
if (isCustomScheme || kIsWeb || uri.path == '/') {
if (kDebugMode) { if (kDebugMode) {
print('Deep link token received: ${token.substring(0, 20)}...'); print('✅ Login Token Detected: ${token.substring(0, 10)}...');
} }
await AuthTokenStorage.setToken(token); await AuthTokenStorage.setToken(token);
await OnBoardingStorage await OnBoardingStorage.setAsSeen();
.setAsSeen();
showOnBoarding = false; showOnBoarding = false;
if (kDebugMode) {
print('Token saved. Redirecting to main...');
}
return main; return main;
} }
} }
@ -211,7 +211,7 @@ class Routes {
return auth; return auth;
} }
if (state.matchedLocation == auth) { if (state.uri.path == auth) {
if (context.read<UserInfoCubit>().state is UserInfoFail) return null; if (context.read<UserInfoCubit>().state is UserInfoFail) return null;
if (AuthTokenStorage.getToken().isNotEmpty || if (AuthTokenStorage.getToken().isNotEmpty ||
(UserInfoCubit.userInfoModel.login != null && (UserInfoCubit.userInfoModel.login != null &&
@ -219,7 +219,7 @@ class Routes {
return main; return main;
} }
} }
if (state.matchedLocation == home && kIsWeb) { if (state.uri.path == home && kIsWeb) {
if (UserInfoCubit.userInfoModel.login == null) { if (UserInfoCubit.userInfoModel.login == null) {
return main; return main;
} else { } else {
@ -250,7 +250,6 @@ class Routes {
try { try {
showOnBoarding = state.extra as bool; showOnBoarding = state.extra as bool;
} catch (e) { } catch (e) {
// Check if onboarding was already seen
showOnBoarding = !OnBoardingStorage.hasSeen(); showOnBoarding = !OnBoardingStorage.hasSeen();
if (kDebugMode) { if (kDebugMode) {
print('Error get Args: $e'); print('Error get Args: $e');

View File

@ -1,5 +1,8 @@
// ignore_for_file: avoid_print
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hoshan/core/routes/route_generator.dart'; import 'package:hoshan/core/routes/route_generator.dart';
@ -16,27 +19,44 @@ class FirebasApi {
static Future<void> initialNotifications() async { static Future<void> initialNotifications() async {
await refreshToken(); await refreshToken();
try {
await _firebaseMessaging.subscribeToTopic('all_users').timeout(
const Duration(seconds: 10),
onTimeout: () {
print('⏰ Subscribe to topic timed out - continuing anyway');
},
);
print('✅ Subscribed to topic: all_users');
} catch (e) {
print('❌ Failed to subscribe to topic (app will continue): $e');
}
initPushNotification(); initPushNotification();
} }
static Future refreshToken() async { static Future refreshToken() async {
try {
print('🔄 Start getting FCM Token...');
await _firebaseMessaging.requestPermission(); await _firebaseMessaging.requestPermission();
fcmToken = await _firebaseMessaging.getToken(); fcmToken = await _firebaseMessaging.getToken().timeout(
const Duration(seconds: 10),
onTimeout: () {
print('⏰ FCM Token request timed out - continuing without token');
return null;
},
);
final packageInfo = await PackageInfo.fromPlatform(); final packageInfo = await PackageInfo.fromPlatform();
final packageName = packageInfo.packageName; final packageName = packageInfo.packageName;
if (kDebugMode) {
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
print('📦 Package Name: $packageName'); print('📦 Package Name: $packageName');
print('📱 FCM Token for Firebase:'); print('🔥 Firebase Initialized Successfully');
print('$fcmToken'); print('📱 FCM Token: $fcmToken');
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
print('');
print('✅ Copy this token and use it in Firebase Console:');
print(' Cloud Messaging → Send test message');
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
} catch (e) {
print('❌ ERROR Getting Token (app will continue): $e');
} }
} }
@ -67,5 +87,23 @@ class FirebasApi {
static Future initPushNotification() async { static Future initPushNotification() async {
FirebaseMessaging.instance.getInitialMessage().then(handleMessage); FirebaseMessaging.instance.getInitialMessage().then(handleMessage);
FirebaseMessaging.onMessageOpenedApp.listen(handleMessage); FirebaseMessaging.onMessageOpenedApp.listen(handleMessage);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
if (message.notification != null) {
if (navigatorKey.currentContext != null) {
ScaffoldMessenger.of(navigatorKey.currentContext!).showSnackBar(
SnackBar(
content: Text(
message.notification!.title != null
? '${message.notification!.title}\n${message.notification!.body}'
: '${message.notification!.body}',
textDirection: TextDirection.rtl,
),
behavior: SnackBarBehavior.floating,
showCloseIcon: true,
),
);
}
}
});
} }
} }

View File

@ -1,16 +1,19 @@
class CreditModel { class CreditModel {
final int? credit; final int? credit;
final int? freeCredit; final int? freeCredit;
final int? giftCredit;
CreditModel({this.credit, this.freeCredit}); CreditModel({this.credit, this.freeCredit, this.giftCredit});
CreditModel copyWith({ CreditModel copyWith({
int? credit, int? credit,
int? freeCredit, int? freeCredit,
int? giftCredit,
}) { }) {
return CreditModel( return CreditModel(
credit: credit ?? this.credit, credit: credit ?? this.credit,
freeCredit: freeCredit ?? this.freeCredit, freeCredit: freeCredit ?? this.freeCredit,
giftCredit: giftCredit ?? this.giftCredit,
); );
} }
} }

View File

@ -57,6 +57,22 @@ class DefaultFirebaseOptions {
storageBucket: 'hoshan-42d9f.appspot.com', storageBucket: 'hoshan-42d9f.appspot.com',
); );
static const FirebaseOptions androidHoushan = FirebaseOptions(
apiKey: 'AIzaSyDi2WRiOSEws1alpLitxX0zsX14rT71aPk',
appId: '1:581103504002:android:8983465e8b4f637d18829b',
messagingSenderId: '581103504002',
projectId: 'hoshan-42d9f',
storageBucket: 'hoshan-42d9f.appspot.com',
);
static const FirebaseOptions androidHoushanMain = FirebaseOptions(
apiKey: 'AIzaSyDi2WRiOSEws1alpLitxX0zsX14rT71aPk',
appId: '1:581103504002:android:896e4d41aad9180318829b',
messagingSenderId: '581103504002',
projectId: 'hoshan-42d9f',
storageBucket: 'hoshan-42d9f.appspot.com',
);
static const FirebaseOptions ios = FirebaseOptions( static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyAENpjFJ2VeVQEt3_Iud9gfqLvuffb7EUI', apiKey: 'AIzaSyAENpjFJ2VeVQEt3_Iud9gfqLvuffb7EUI',
appId: '1:581103504002:ios:4e51253f750a0fc818829b', appId: '1:581103504002:ios:4e51253f750a0fc818829b',

View File

@ -1,3 +1,5 @@
// ignore_for_file: avoid_print
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -30,50 +32,88 @@ import 'package:hoshan/ui/screens/tools/bloc/tools_bloc.dart';
import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart'; import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart';
import 'package:hoshan/ui/theme/my_custom_scroll_behavior.dart'; import 'package:hoshan/ui/theme/my_custom_scroll_behavior.dart';
import 'package:hoshan/ui/theme/theme.dart'; import 'package:hoshan/ui/theme/theme.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:url_strategy/url_strategy.dart'; import 'package:url_strategy/url_strategy.dart';
@pragma('vm:entry-point') @pragma('vm:entry-point')
Future<void> _onBackgroundMessage(RemoteMessage message) async {} Future<void> _onBackgroundMessage(RemoteMessage message) async {
debugPrint("Handling a background message: ${message.messageId}");
}
void main() async { void main() async {
setPathUrlStrategy(); setPathUrlStrategy();
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
// 1. Wrap SharedPreferences in try-catch
try { try {
await SharedPreferencesHelper.initial(); await SharedPreferencesHelper.initial();
} catch (e) { } catch (e) {
debugPrint("Error initializing SharedPreferences: $e"); debugPrint("Error initializing SharedPreferences: $e");
} }
// 2. Wrap Tapsell in try-catch (High probability of crash here)
try { try {
await TapsellService.initialize(); await TapsellService.initialize();
} catch (e) { } catch (e) {
debugPrint("Error initializing Tapsell: $e"); debugPrint("Error initializing Tapsell: $e");
} }
// 3. Firebase Initialization print('🚀 Starting Firebase Initialization...');
try { try {
FirebaseMessaging.onBackgroundMessage(_onBackgroundMessage); FirebaseMessaging.onBackgroundMessage(_onBackgroundMessage);
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform); FirebaseOptions options = DefaultFirebaseOptions.currentPlatform;
FirebasApi.initialNotifications();
} catch (e) { if (defaultTargetPlatform == TargetPlatform.android) {
debugPrint('Error initializing Firebase: $e'); print('🤖 Platform is Android, checking package info...');
final packageInfo = await PackageInfo.fromPlatform();
print("📦 RUNNING WITH PACKAGE: ${packageInfo.packageName}");
if (packageInfo.packageName == 'com.houshan.basa') {
options = DefaultFirebaseOptions.androidHoushan;
print("🔥 Using 'androidHoushan' options (Basa)");
} else if (packageInfo.packageName == 'com.houshan.hoshan') {
options = DefaultFirebaseOptions.androidHoushanMain;
print("🔥 Using 'androidHoushanMain' options (Main)");
} else {
print("⚠️ Unknown package name, using default options");
}
}
if (Firebase.apps.isEmpty) {
print('⏳ Calling Firebase.initializeApp...');
try {
await Firebase.initializeApp(options: options);
print('✅ Firebase.initializeApp completed.');
} catch (e) {
print('⚠️ Firebase.initializeApp failed: $e');
if (e.toString().contains('duplicate-app')) {
print(' Ignoring duplicate-app error and continuing...');
} else {
rethrow;
}
}
} else {
print(' Firebase already initialized. Skipping initialization.');
}
print('⏳ Calling FirebasApi.initialNotifications()...');
await FirebasApi.initialNotifications();
} catch (e, stack) {
print('❌❌ Error initializing Firebase: $e');
print(stack);
} }
// 4. Downloader Initialization
try { try {
if (!kIsWeb) { if (!kIsWeb) {
await FlutterDownloader.initialize(debug: true, ignoreSsl: true); await FlutterDownloader.initialize(debug: true, ignoreSsl: true);
print('✅ FlutterDownloader initialized');
} }
} catch (e) { } catch (e) {
debugPrint('Error initializing FlutterDownloader: $e'); debugPrint('Error initializing FlutterDownloader: $e');
} }
// Run App // Run App
print('✅✅ Main initialization completed! Starting app...');
if (kReleaseMode) { if (kReleaseMode) {
await SentryFlutter.init((options) { await SentryFlutter.init((options) {
options.dsn = options.dsn =
@ -81,11 +121,11 @@ void main() async {
options.sendDefaultPii = true; options.sendDefaultPii = true;
}, },
// Init your App.
appRunner: () => runApp(mainApp())); appRunner: () => runApp(mainApp()));
} else { } else {
runApp(mainApp()); runApp(mainApp());
} }
print('🚀🚀 App started successfully!');
} }
MultiBlocProvider mainApp() { MultiBlocProvider mainApp() {

View File

@ -51,11 +51,17 @@ class _AuthPageState extends State<AuthPage> {
if (!kIsWeb) { if (!kIsWeb) {
final appLinks = AppLinks(); final appLinks = AppLinks();
_linkSubscription = appLinks.uriLinkStream.listen((uri) async { _linkSubscription = appLinks.uriLinkStream.listen((uri) async {
if (uri.scheme == 'houshan' && uri.host == 'auth') { print('🔔 AuthPage Listener Received URI: $uri'); // Debug Log
final isCustomScheme = uri.scheme == 'houshan' && uri.host == 'auth';
final isWebLink = (uri.scheme == 'https' || uri.scheme == 'http') &&
uri.host == 'basaweb.houshan.ai';
if (isCustomScheme || isWebLink) {
final token = uri.queryParameters['token']; final token = uri.queryParameters['token'];
if (token != null && token.isNotEmpty) { if (token != null && token.isNotEmpty) {
if (kDebugMode) { if (kDebugMode) {
print('Deep link received while app open: $token'); print('Deep link received: $uri');
} }
await AuthTokenStorage.setToken(token); await AuthTokenStorage.setToken(token);
await OnBoardingStorage.setAsSeen(); await OnBoardingStorage.setAsSeen();

View File

@ -28,7 +28,7 @@ class _GiftCodeScreenState extends State<GiftCodeScreen> {
listener: (context, state) async { listener: (context, state) async {
if (state is CheckCodeSucceess) { if (state is CheckCodeSucceess) {
await context.read<UserInfoCubit>().getUserInfo(); await context.read<UserInfoCubit>().getUserInfo();
context.go(Routes.giftCredit, context.go(Routes.home,
extra: HomeArgs(message: state.message, freeCredit: 50)); extra: HomeArgs(message: state.message, freeCredit: 50));
} }
}, },
@ -105,7 +105,7 @@ class _GiftCodeScreenState extends State<GiftCodeScreen> {
onPressed: () async { onPressed: () async {
if (state is CheckCodeLoading) return; if (state is CheckCodeLoading) return;
await context.read<UserInfoCubit>().getUserInfo(); await context.read<UserInfoCubit>().getUserInfo();
context.go(Routes.giftCredit, context.go(Routes.home,
extra: HomeArgs(freeCredit: 50)); extra: HomeArgs(freeCredit: 50));
}, },
child: Text( child: Text(

View File

@ -96,7 +96,7 @@ class _VerificationScreenState extends State<VerificationScreen> {
context.read<AuthScreensCubit>().changeState(AuthScreens.code); context.read<AuthScreensCubit>().changeState(AuthScreens.code);
} else { } else {
await context.read<UserInfoCubit>().getUserInfo(); await context.read<UserInfoCubit>().getUserInfo();
context.go(Routes.giftCredit); context.go(Routes.home);
} }
} }
}, },

View File

@ -58,6 +58,9 @@ class MessagesBloc extends Bloc<MessagesEvent, MessagesState> {
if (event is AddMessage) { if (event is AddMessage) {
final updatedList = List<Messages>.from(state.messages); final updatedList = List<Messages>.from(state.messages);
updatedList.add(event.message); updatedList.add(event.message);
if (kDebugMode) {
print('📨 AddMessage: Total messages = ${updatedList.length}, Role = ${event.message.role}, File = ${event.message.file?.name}');
}
emit(MessagesSuccess(messages: updatedList)); // Copy the current state emit(MessagesSuccess(messages: updatedList)); // Copy the current state
} }

View File

@ -1,4 +1,4 @@
// ignore_for_file: deprecated_member_use_from_same_package, use_build_context_synchronously // ignore_for_file: deprecated_member_use_from_same_package, use_build_context_synchronously, avoid_print
import 'dart:math'; import 'dart:math';
@ -74,6 +74,8 @@ class _ChatPageState extends State<ChatPage> {
final ValueNotifier<XFile?> selectedFile = ValueNotifier(null); final ValueNotifier<XFile?> selectedFile = ValueNotifier(null);
final TextEditingController messageText = TextEditingController(); final TextEditingController messageText = TextEditingController();
ValueNotifier<int?> maxLines = ValueNotifier(5); ValueNotifier<int?> maxLines = ValueNotifier(5);
late final ValueNotifier<int> selectedBotId =
ValueNotifier(widget.chatArgs.bot.id ?? 1);
void sendRequest( void sendRequest(
{required final String? message, {required final String? message,
@ -82,9 +84,13 @@ class _ChatPageState extends State<ChatPage> {
final bool withOutNewMessage = false}) { final bool withOutNewMessage = false}) {
final creditState = CreditModel( final creditState = CreditModel(
credit: UserInfoCubit.userInfoModel.credit ?? 0, credit: UserInfoCubit.userInfoModel.credit ?? 0,
freeCredit: UserInfoCubit.userInfoModel.freeCredit ?? 0); freeCredit: UserInfoCubit.userInfoModel.freeCredit ?? 0,
int credit = (creditState.freeCredit ?? 0) + (creditState.credit ?? 0); giftCredit: UserInfoCubit.userInfoModel.gift_credit ?? 0);
int credit = (creditState.freeCredit ?? 0) +
(creditState.credit ?? 0) +
(creditState.giftCredit ?? 0);
if (credit < widget.chatArgs.bot.cost!) { if (credit < widget.chatArgs.bot.cost!) {
print("crdit= $credit, cost=${widget.chatArgs.bot.cost}");
DialogHandler(context: context).showUpgradeCredit(); DialogHandler(context: context).showUpgradeCredit();
messageText.text = message ?? ''; messageText.text = message ?? '';
@ -92,14 +98,26 @@ class _ChatPageState extends State<ChatPage> {
} }
if (!withOutNewMessage) { if (!withOutNewMessage) {
if (kDebugMode) {
print(
'📤 Sending message: text="${message ?? ""}", file=${file?.name}');
}
final messageContent = <Content>[];
if (message != null && message.isNotEmpty) {
messageContent.add(Content(type: 'text', text: message));
}
if (file != null && (message == null || message.isEmpty)) {
messageContent.add(Content(type: 'text', text: ''));
}
context.read<MessagesBloc>().add(AddMessage( context.read<MessagesBloc>().add(AddMessage(
message: Messages( message: Messages(
role: 'human', role: 'human',
id: 'hero', id: 'hero',
content: [ content: messageContent,
if (message != null && message.isNotEmpty)
Content(type: 'text', text: message)
],
query: message, query: message,
file: file, file: file,
retry: retry))); retry: retry)));
@ -107,7 +125,7 @@ class _ChatPageState extends State<ChatPage> {
context.read<ReceiveMessageCubit>().execute( context.read<ReceiveMessageCubit>().execute(
request: SendMessageModel( request: SendMessageModel(
botId: bot.id, botId: selectedBotId.value,
file: file, file: file,
id: chatId, id: chatId,
messageId: 'hero', messageId: 'hero',
@ -474,69 +492,209 @@ class _ChatPageState extends State<ChatPage> {
? const Radius.circular(0) ? const Radius.circular(0)
: const Radius.circular(10))), : const Radius.circular(10))),
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: message.content != null child:
(message.content != null &&
message.content!.isNotEmpty) ||
message.file != null
? Column( ? Column(
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.end, CrossAxisAlignment.end,
children: [ children: [
Builder(
builder: (context) {
if (kDebugMode &&
!message.fromBot!) {
print(
'👤 User message: id=${message.id}, file=${message.file?.name}, content=${message.content?.length}');
}
return const SizedBox.shrink();
},
),
if (message.file != null) if (message.file != null)
Stack(
children: [
message.file!.name.isVideo() message.file!.name.isVideo()
? GestureDetector( ? GestureDetector(
onTap: () => DialogHandler( onTap: () => DialogHandler(
context: context) context:
context)
.showVideoHero( .showVideoHero(
url: message url: message
.file!.path), .file!
.path),
child: Container( child: Container(
constraints: constraints:
const BoxConstraints( const BoxConstraints(
maxWidth: double.infinity, maxWidth: double
.infinity,
), ),
child: VideoThumbnailWidget( child: VideoThumbnailWidget(
videoUrl: videoUrl:
message.file!.path), message
.file!
.path),
), ),
) )
: message.file!.name.isImage() : message.file!.name
.isImage()
? GestureDetector( ? GestureDetector(
onTap: () => onTap: () => DialogHandler(
DialogHandler(
context: context:
context) context)
.showImageHero( .showImageHero(
image: message image: message
.file! .file!
.path), .path),
child: AspectRatio( child:
aspectRatio: 1 / 1, AspectRatio(
child: Container( aspectRatio:
1 / 1,
child:
Container(
constraints: constraints:
const BoxConstraints( const BoxConstraints(
maxWidth: maxWidth: double
double.infinity, .infinity,
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: borderRadius: BorderRadius.circular(8),
BorderRadius child: CustomeImage(
.circular(
8),
child:
CustomeImage(
src: message src: message
.file!.path, .file!
.path,
fit: BoxFit fit: BoxFit
.cover, .cover,
)), )),
), ),
), ),
) )
: message.file!.name.isAudio() : message.file!.name
.isAudio()
? Player( ? Player(
fileUrl: message fileUrl:
.file!.path, message
inMessages: true, .file!
.path,
inMessages:
true,
) )
: Container( : Container(
decoration: BoxDecoration(
color: context
.read<
ThemeModeCubit>()
.isDark()
? AppColors.black[
900]
: AppColors
.gray
.defaultShade,
borderRadius:
BorderRadius.circular(
10)),
padding:
const EdgeInsets
.all(
8),
constraints:
const BoxConstraints(
minHeight:
64),
child: Row(
children: [
SizedBox(
child: message.file!.name.isDocument()
? const Icon(CupertinoIcons.doc)
: const SizedBox.shrink(),
),
Expanded(
child:
Padding(
padding: const EdgeInsets
.symmetric(
horizontal:
12.0),
child:
Text(
message
.file!
.name,
textDirection: message.file!.name.startsWithEnglish()
? TextDirection.ltr
: TextDirection.rtl,
style:
const TextStyle(fontSize: 16),
overflow:
TextOverflow.ellipsis,
maxLines:
2,
),
)),
],
),
),
if (message.id == 'hero')
Positioned.fill(
child: Container(
decoration:
BoxDecoration(
color: Colors.black
.withOpacity(0.5),
borderRadius:
BorderRadius
.circular(8),
),
child: Center(
child:
CircularProgressIndicator(
color: Colors.white,
),
),
),
),
],
),
if (message.content != null &&
message.content!.isNotEmpty)
...List.generate(
message.content!.length,
(index) {
final content =
message.content![index];
return Column(
children: [
if (content.audioUrl !=
null)
Player(
fileUrl: content
.audioUrl!
.url ??
'',
inMessages: true,
),
if (content.imageUrl !=
null)
Container(
constraints:
const BoxConstraints(
maxWidth:
double.infinity,
),
child: AspectRatio(
aspectRatio: 1 / 1,
child: ImageNetwork(
url: content
.imageUrl
?.url ??
'',
showHero: true,
radius: 10,
),
),
),
if (content.pdfUrl !=
null)
Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: context color: context
.read< .read<
@ -561,16 +719,10 @@ class _ChatPageState extends State<ChatPage> {
64), 64),
child: Row( child: Row(
children: [ children: [
SizedBox( const SizedBox(
child: message child: Icon(
.file!
.name
.isDocument()
? const Icon(
CupertinoIcons CupertinoIcons
.doc) .doc),
: const SizedBox
.shrink(),
), ),
Expanded( Expanded(
child: child:
@ -580,11 +732,14 @@ class _ChatPageState extends State<ChatPage> {
horizontal: horizontal:
12.0), 12.0),
child: Text( child: Text(
message.file! content.pdfUrl!
.name, .url
textDirection: message ?.split(
.file! '/')
.name .last ??
'',
textDirection: (content.pdfUrl!.url?.split('/').last ??
'')
.startsWithEnglish() .startsWithEnglish()
? TextDirection ? TextDirection
.ltr .ltr
@ -602,130 +757,45 @@ class _ChatPageState extends State<ChatPage> {
], ],
), ),
), ),
...List.generate( if (content.audioUrl == null &&
message.content!.length, content.imageUrl ==
(index) { null &&
final content = content.pdfUrl ==
message.content![index]; null)
return Column( Padding(
children: [
if (content.audioUrl != null)
Player(
fileUrl:
content.audioUrl!.url ??
'',
inMessages: true,
),
if (content.imageUrl != null)
Container(
constraints:
const BoxConstraints(
maxWidth: double.infinity,
),
child: AspectRatio(
aspectRatio: 1 / 1,
child: ImageNetwork(
url: content.imageUrl
?.url ??
'',
showHero: true,
radius: 10,
),
),
),
if (content.pdfUrl != null)
Container(
decoration: BoxDecoration(
color: context
.read<
ThemeModeCubit>()
.isDark()
? AppColors
.black[900]
: AppColors.gray
.defaultShade,
borderRadius:
BorderRadius
.circular(10)),
padding:
const EdgeInsets.all(8),
constraints:
const BoxConstraints(
minHeight: 64),
child: Row(
children: [
const SizedBox(
child: Icon(
CupertinoIcons
.doc),
),
Expanded(
child: Padding(
padding: padding:
const EdgeInsets const EdgeInsets
.symmetric( .symmetric(
horizontal: horizontal:
12.0), 8.0),
child: Text(
content.pdfUrl!.url
?.split('/')
.last ??
'',
textDirection: (content
.pdfUrl!
.url
?.split(
'/')
.last ??
'')
.startsWithEnglish()
? TextDirection
.ltr
: TextDirection
.rtl,
style:
const TextStyle(
fontSize:
16),
overflow:
TextOverflow
.ellipsis,
maxLines: 2,
),
)),
],
),
),
if (content.audioUrl == null &&
content.imageUrl == null &&
content.pdfUrl == null)
Padding(
padding: const EdgeInsets
.symmetric(
horizontal: 8.0),
child: Builder( child: Builder(
builder: (context) { builder:
(context) {
directionName directionName
.value = content .value = content
.text != .text !=
null && null &&
content.text! content
.text!
.startsWithEnglish() .startsWithEnglish()
? "LTR" ? "LTR"
: 'RTL'; : 'RTL';
return DefaultMarkdownText( return DefaultMarkdownText(
key: markdownKey, key:
text: content.text ?? markdownKey,
text: content
.text ??
'', '',
fromBot: fromBot: message
message.fromBot!, .fromBot!,
color: message color: message
.fromBot! .fromBot!
? Theme.of( ? Theme.of(
context) context)
.colorScheme .colorScheme
.onSurface .onSurface
: Colors.white); : Colors
.white);
}), }),
) )
], ],
@ -744,7 +814,8 @@ class _ChatPageState extends State<ChatPage> {
ValueListenableBuilder( ValueListenableBuilder(
valueListenable: valueListenable:
directionName, directionName,
builder: (context, dir, _) { builder:
(context, dir, _) {
return InkWell( return InkWell(
onTap: () { onTap: () {
directionName directionName
@ -759,7 +830,8 @@ class _ChatPageState extends State<ChatPage> {
.symmetric( .symmetric(
horizontal: horizontal:
12, 12,
vertical: 4), vertical:
4),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: borderRadius:
BorderRadius BorderRadius
@ -800,8 +872,8 @@ class _ChatPageState extends State<ChatPage> {
child: BlocBuilder< child: BlocBuilder<
LikeMessageCubit, LikeMessageCubit,
LikeMessageState>( LikeMessageState>(
builder: builder: (context,
(context, state) { state) {
return DefaultPlaceHolder( return DefaultPlaceHolder(
enabled: state enabled: state
is LikeMessageLoading, is LikeMessageLoading,
@ -811,8 +883,7 @@ class _ChatPageState extends State<ChatPage> {
onTap: onTap:
() async { () async {
await context.read<LikeMessageCubit>().setLike( await context.read<LikeMessageCubit>().setLike(
like: state like: state is LikeMessageLiked
is LikeMessageLiked
? null ? null
: true, : true,
chatId: chatId:
@ -828,16 +899,12 @@ class _ChatPageState extends State<ChatPage> {
20.0), 20.0),
child: child:
SizedBox( SizedBox(
width: 16, width:
16,
height: height:
16, 16,
child: state child: state is LikeMessageLiked
is LikeMessageLiked ? Assets.icon.bold.like.svg(color: AppColors.green.defaultShade)
? Assets
.icon
.bold
.like
.svg(color: AppColors.green.defaultShade)
: Assets.icon.outline.like.svg(color: Theme.of(context).colorScheme.primary), : Assets.icon.outline.like.svg(color: Theme.of(context).colorScheme.primary),
), ),
), ),
@ -846,8 +913,7 @@ class _ChatPageState extends State<ChatPage> {
onTap: onTap:
() async { () async {
await context.read<LikeMessageCubit>().setLike( await context.read<LikeMessageCubit>().setLike(
like: state like: state is LikeMessageDisLiked
is LikeMessageDisLiked
? null ? null
: false, : false,
chatId: chatId:
@ -863,16 +929,12 @@ class _ChatPageState extends State<ChatPage> {
20.0), 20.0),
child: child:
SizedBox( SizedBox(
width: 16, width:
16,
height: height:
16, 16,
child: state child: state is LikeMessageDisLiked
is LikeMessageDisLiked ? Assets.icon.bold.dislike.svg(color: AppColors.red.defaultShade)
? Assets
.icon
.bold
.dislike
.svg(color: AppColors.red.defaultShade)
: Assets.icon.outline.dislike.svg(color: Theme.of(context).colorScheme.primary), : Assets.icon.outline.dislike.svg(color: Theme.of(context).colorScheme.primary),
), ),
), ),
@ -886,14 +948,15 @@ class _ChatPageState extends State<ChatPage> {
if (widget.chatArgs.bot if (widget.chatArgs.bot
.tool != .tool !=
null && null &&
!widget !widget.chatArgs.bot
.chatArgs.bot.tool!) .tool!)
MorePopupMenuHandler( MorePopupMenuHandler(
context: context) context:
context)
.morePopUpMenu( .morePopUpMenu(
child: Padding( child:
padding: Padding(
const EdgeInsets padding: const EdgeInsets
.only( .only(
right: right:
20.0), 20.0),
@ -907,8 +970,7 @@ class _ChatPageState extends State<ChatPage> {
.read< .read<
ThemeModeCubit>() ThemeModeCubit>()
.isDark() .isDark()
? AppColors ? AppColors.black[
.black[
900] 900]
: AppColors : AppColors
.primaryColor[50], .primaryColor[50],
@ -990,18 +1052,16 @@ class _ChatPageState extends State<ChatPage> {
.voiceCricle, .voiceCricle,
title: title:
'لحن نوشته را تغییر بده')), 'لحن نوشته را تغییر بده')),
click: () async { click:
() async {
await BottomSheetHandler( await BottomSheetHandler(
context) context)
.showStringList( .showStringList(
onSelect: onSelect:
(value) { (value) {
try { try {
refreshQuestions.value = refreshQuestions.value = false;
false; sendRequest(file: message.file, message: 'لحن نوشته را تغییر بده به $value');
sendRequest(
file: message.file,
message: 'لحن نوشته را تغییر بده به $value');
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
print('Error is: $e'); print('Error is: $e');
@ -1030,18 +1090,16 @@ class _ChatPageState extends State<ChatPage> {
.translate, .translate,
title: title:
'ترجمه کن')), 'ترجمه کن')),
click: () async { click:
() async {
await BottomSheetHandler( await BottomSheetHandler(
context) context)
.showStringList( .showStringList(
onSelect: onSelect:
(value) { (value) {
try { try {
refreshQuestions.value = refreshQuestions.value = false;
false; sendRequest(file: message.file, message: 'زبان نوشته را تغییر بده به $value');
sendRequest(
file: message.file,
message: 'زبان نوشته را تغییر بده به $value');
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
print('Error is: $e'); print('Error is: $e');
@ -1178,6 +1236,9 @@ class _ChatPageState extends State<ChatPage> {
Widget aNewMessage() { Widget aNewMessage() {
return BlocConsumer<ReceiveMessageCubit, ReceiveMessageState>( return BlocConsumer<ReceiveMessageCubit, ReceiveMessageState>(
builder: (context, state) { builder: (context, state) {
if (kDebugMode) {
print('🔄 aNewMessage state: ${state.runtimeType}');
}
if (state is ReceiveMessageOnResponsing) { if (state is ReceiveMessageOnResponsing) {
return Container( return Container(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
@ -1999,6 +2060,114 @@ class _ChatPageState extends State<ChatPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
if (bot.tool == null || !bot.tool!)
ValueListenableBuilder(
valueListenable: selectedBotId,
builder: (context, currentBotId, _) {
String botName = currentBotId == 1
? 'ChatGPT'
: currentBotId == 2
? 'Gemini'
: currentBotId == 4
? 'Grok'
: 'Claude';
return PopupMenuButton<int>(
color:
Theme.of(context).colorScheme.surface,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
offset: const Offset(0, -10),
onSelected: (int value) {
print('🤖 Bot selected: $value');
selectedBotId.value = value;
},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<int>>[
PopupMenuItem<int>(
value: 1,
child: Text(
'ChatGPT',
style: AppTextStyles.body5.copyWith(
fontWeight: currentBotId == 1
? FontWeight.bold
: FontWeight.normal,
),
),
),
PopupMenuItem<int>(
value: 2,
child: Text(
'Gemini',
style: AppTextStyles.body5.copyWith(
fontWeight: currentBotId == 2
? FontWeight.bold
: FontWeight.normal,
),
),
),
PopupMenuItem<int>(
value: 4,
child: Text(
'Grok',
style: AppTextStyles.body5.copyWith(
fontWeight: currentBotId == 4
? FontWeight.bold
: FontWeight.normal,
),
),
),
PopupMenuItem<int>(
value: 9,
child: Text(
'Claude',
style: AppTextStyles.body5.copyWith(
fontWeight: currentBotId == 9
? FontWeight.bold
: FontWeight.normal,
),
),
),
],
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
botName,
style: AppTextStyles.body6.copyWith(
color: Theme.of(context)
.colorScheme
.primary,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 4),
Icon(
Icons.keyboard_arrow_up,
size: 16,
color: Theme.of(context)
.colorScheme
.primary,
),
],
),
),
);
},
),
if (bot.tool == null || !bot.tool!)
const SizedBox(width: 12),
ValueListenableBuilder( ValueListenableBuilder(
valueListenable: webSearch, valueListenable: webSearch,
builder: (context, isWebSearchEnabled, _) { builder: (context, isWebSearchEnabled, _) {
@ -2163,8 +2332,8 @@ class _ChatPageState extends State<ChatPage> {
const EdgeInsets.only( const EdgeInsets.only(
right: 8.0), right: 8.0),
child: CircleIconBtn( child: CircleIconBtn(
icon: Assets icon: Assets.icon.outline
.icon.outline.cardAdd, .folderAdd,
color: Theme.of(context) color: Theme.of(context)
.colorScheme .colorScheme
.primary, .primary,

View File

@ -54,7 +54,6 @@ class ReceiveMessageCubit extends Cubit<ReceiveMessageState> {
AiResponseModel aiResponseModel = AiResponseModel(); AiResponseModel aiResponseModel = AiResponseModel();
await scrollToEnd(); await scrollToEnd();
try { try {
// Call your streaming message function and yield states accordingly
await for (String message in request.tool! await for (String message in request.tool!
? ChatbotRepository.sendMessageTool(request) ? ChatbotRepository.sendMessageTool(request)
: ChatbotRepository.sendMessage(request)) { : ChatbotRepository.sendMessage(request)) {
@ -78,7 +77,7 @@ class ReceiveMessageCubit extends Cubit<ReceiveMessageState> {
for (String json in jsonStrings) { for (String json in jsonStrings) {
json = json.replaceAll( json = json.replaceAll(
RegExp(r'[{}"]'), ''); // Remove braces and quotes RegExp(r'[{}"]'), '');
List<String> pairs = json.split(', '); List<String> pairs = json.split(', ');
for (String pair in pairs) { for (String pair in pairs) {
if (pair.contains('chat_id:')) { if (pair.contains('chat_id:')) {
@ -94,10 +93,10 @@ class ReceiveMessageCubit extends Cubit<ReceiveMessageState> {
pair.replaceAll('human_message_id: ', ''); pair.replaceAll('human_message_id: ', '');
} else if (pair.contains('credit:')) { } else if (pair.contains('credit:')) {
jsonMap['credit'] = pair.replaceAll('credit: ', ''); jsonMap['credit'] = pair.replaceAll('credit: ', '');
} else if (pair.contains('credit:')) {
jsonMap['credit'] = pair.replaceAll('credit: ', '');
} else if (pair.contains('free:')) { } else if (pair.contains('free:')) {
jsonMap['free'] = pair.replaceAll('free: ', ''); jsonMap['free'] = pair.replaceAll('free: ', '');
} else if (pair.contains('gift:')) {
jsonMap['gift'] = pair.replaceAll('gift: ', '');
} else if (pair.contains('human_message_created_at:')) { } else if (pair.contains('human_message_created_at:')) {
jsonMap['human_message_created_at'] = jsonMap['human_message_created_at'] =
pair.replaceAll('human_message_created_at: ', ''); pair.replaceAll('human_message_created_at: ', '');
@ -122,8 +121,16 @@ class ReceiveMessageCubit extends Cubit<ReceiveMessageState> {
final res = AiResponseModel.fromJson(jsonMap); final res = AiResponseModel.fromJson(jsonMap);
if (res.content != null) { if (res.content != null) {
result += res.content ?? ''; // Add each content to the list result += res.content ?? '';
if (!(res.content!.startsWith('http'))) { if (res.content!.startsWith('http')) {
if (res.content!.isImage()) {
emit(ReceiveMessageOnResponsing(text: 'در حال آماده‌سازی تصویر'));
} else if (res.content!.isAudio()) {
emit(ReceiveMessageOnResponsing(text: 'در حال آماده‌سازی صوت'));
} else if (res.content!.isDocument()) {
emit(ReceiveMessageOnResponsing(text: 'در حال آماده‌سازی فایل'));
}
} else {
emit(ReceiveMessageOnResponsing(text: result)); emit(ReceiveMessageOnResponsing(text: result));
} }
} }
@ -138,10 +145,14 @@ class ReceiveMessageCubit extends Cubit<ReceiveMessageState> {
content: result.isEmpty ? res.content : result, content: result.isEmpty ? res.content : result,
freeCredit: res.freeCredit, freeCredit: res.freeCredit,
humanMessageId: res.humanMessageId); humanMessageId: res.humanMessageId);
// Yield the received message line by line
} }
await scrollToEnd(); await scrollToEnd();
if (aiResponseModel.content != null &&
aiResponseModel.content!.startsWith('http')) {
await Future.delayed(const Duration(milliseconds: 500));
}
if (aiResponseModel.error ?? true) { if (aiResponseModel.error ?? true) {
emit(ReceiveMessageOnFail( emit(ReceiveMessageOnFail(
oldHumanMessageId: request.messageId ?? '', oldHumanMessageId: request.messageId ?? '',

View File

@ -126,6 +126,20 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
.state is MediaGResponseLoading) { .state is MediaGResponseLoading) {
return; return;
} }
context.read<MessagesBloc>().add(AddMessage(
message: Messages(
file: image.xFile,
content: [
Content(
imageUrl: FileUrl(
url: image.xFile.path))
],
createdAt: DateTime.now()
.toIso8601String(),
id: 'hero',
role: 'user')));
context.read<MediaGResponseCubit>().request( context.read<MediaGResponseCubit>().request(
SendMessageModel( SendMessageModel(
ghost: isGhost.value, ghost: isGhost.value,
@ -164,11 +178,30 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
.state is MediaGResponseLoading) { .state is MediaGResponseLoading) {
return; return;
} }
final query = _query.text;
if (query.isNotEmpty) {
context.read<MessagesBloc>().add(
AddMessage(
message: Messages(
query: query,
content: [
Content(
type: 'text',
text: query)
],
createdAt: DateTime.now()
.toIso8601String(),
id: 'hero',
role: 'user')));
}
context.read<MediaGResponseCubit>().request( context.read<MediaGResponseCubit>().request(
SendMessageModel( SendMessageModel(
messageId: DateTime.now() messageId: DateTime.now()
.toIso8601String(), .toIso8601String(),
query: _query.text, query: query,
botId: widget.chatArgs.bot.id, botId: widget.chatArgs.bot.id,
ghost: isGhost.value, ghost: isGhost.value,
id: chatId)); id: chatId));
@ -255,22 +288,50 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
); );
} }
// if (state is MessagesSuccess) {
final m = mState.messages; final m = mState.messages;
List<List<Messages>> allMessages = groupMessages(m); List<List<Messages>> allMessages = groupMessages(m);
// فیلتر کردن گروههایی که حداقل یک پیام با image_url دارند
List<List<Messages>> messages = allMessages.where((group) { List<List<Messages>> messages = ptp
? allMessages.where((group) {
return group.any((msg) => return group.any((msg) =>
msg.content?.any((c) => c.imageUrl != null) ?? false); msg.content?.any((c) => c.imageUrl != null) ??
}).toList(); false);
}).toList()
: allMessages;
return BlocConsumer<MediaGResponseCubit, MediaGResponseState>( return BlocConsumer<MediaGResponseCubit, MediaGResponseState>(
listener: (context, state) async { listener: (context, state) async {
if (state is MediaGResponseSucess) { if (state is MediaGResponseSucess) {
final messages =
context.read<MessagesBloc>().state.messages;
final heroMessage = messages.firstWhere(
(m) => m.id == 'hero',
orElse: () => Messages(id: null, role: 'user'),
);
if (heroMessage.id != null) {
context
.read<MessagesBloc>()
.add(DeleteMessageWithId(messageId: 'hero'));
}
context.read<MessagesBloc>().add(AddMessage( context.read<MessagesBloc>().add(AddMessage(
message: Messages( message: Messages(
query: state.query, query: state.query,
file: state.file, file: state.file,
content: state.file != null
? [
Content(
imageUrl:
FileUrl(url: state.file!.path))
]
: state.query != null &&
state.query!.isNotEmpty
? [
Content(
type: 'text', text: state.query)
]
: null,
createdAt: DateTime.now().toIso8601String(), createdAt: DateTime.now().toIso8601String(),
error: state.response.error, error: state.response.error,
id: state.response.humanMessageId, id: state.response.humanMessageId,
@ -300,11 +361,16 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
chatId = state.response.chatId; chatId = state.response.chatId;
} }
if (state is MediaGResponseFail) {
context
.read<MessagesBloc>()
.add(DeleteMessageWithId(messageId: 'hero'));
}
}, },
builder: (context, state) { builder: (context, state) {
final newMaxSize = messages.length + 1; final newMaxSize = messages.length + 1;
// استفاده از postFrameCallback برای جلوگیری از setState در build
if (maxSize.value != newMaxSize) { if (maxSize.value != newMaxSize) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) { if (mounted) {
@ -318,7 +384,6 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
return ListView( return ListView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
children: [ children: [
// Bot info section
Column( Column(
children: [ children: [
const SizedBox( const SizedBox(
@ -497,7 +562,6 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
), ),
], ],
), ),
// Messages list
...messages.asMap().entries.map((entry) { ...messages.asMap().entries.map((entry) {
final index = entry.key; final index = entry.key;
final ms = entry.value; final ms = entry.value;
@ -545,22 +609,28 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
16)), 16)),
child: AspectRatio( child: AspectRatio(
aspectRatio: 3 / 4, aspectRatio: 3 / 4,
child: user!.file != null child: Stack(
fit: StackFit.expand,
children: [
user!.file != null
? ClipRRect( ? ClipRRect(
borderRadius: borderRadius:
BorderRadius BorderRadius
.circular( .circular(
12), 12),
child: CustomeImage( child:
CustomeImage(
src: user src: user
.file!.path, .file!
fit: BoxFit.cover, .path,
fit: BoxFit
.cover,
)) ))
: ImageNetwork( : ImageNetwork(
width: width: double
double.infinity, .infinity,
height: height: double
double.infinity, .infinity,
radius: 12, radius: 12,
showHero: true, showHero: true,
url: user url: user
@ -569,6 +639,29 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
.imageUrl .imageUrl
?.url ?? ?.url ??
''), ''),
if (user.id == 'hero')
Container(
decoration:
BoxDecoration(
color: Colors
.black
.withOpacity(
0.5),
borderRadius:
BorderRadius
.circular(
12),
),
child: Center(
child:
CircularProgressIndicator(
color: Colors
.white,
),
),
),
],
),
), ),
), ),
after: Container( after: Container(
@ -684,21 +777,28 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
?.url ?? ?.url ??
''), ''),
) )
: Padding( : Stack(
children: [
Padding(
padding: padding:
const EdgeInsets.all( const EdgeInsets
16.0), .all(16.0),
child: Directionality( child: Directionality(
textDirection: textDirection:
TextDirection.rtl, TextDirection
.rtl,
child: Scrollbar( child: Scrollbar(
thumbVisibility: true, thumbVisibility:
trackVisibility: true, true,
trackVisibility:
true,
interactive: true, interactive: true,
controller: controller:
yourScrollController, yourScrollController,
radius: const Radius radius:
.circular(16), const Radius
.circular(
16),
child: child:
SingleChildScrollView( SingleChildScrollView(
controller: controller:
@ -716,8 +816,7 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
user.query ?? user.query ??
'', '',
style: style: AppTextStyles
AppTextStyles
.body4 .body4
.copyWith( .copyWith(
color: Colors color: Colors
@ -731,6 +830,31 @@ class _PhotoChatPageState extends State<PhotoChatPage> {
), ),
), ),
), ),
if (user.id == 'hero')
Positioned.fill(
child: Container(
decoration:
BoxDecoration(
color: Colors
.black
.withOpacity(
0.5),
borderRadius:
BorderRadius
.circular(
12),
),
child: Center(
child:
CircularProgressIndicator(
color: Colors
.white,
),
),
),
),
],
),
), ),
], ],
), ),

View File

@ -316,7 +316,6 @@ class _TtaPageState extends State<VideoChatPage> {
1 + 1 +
(state is MediaGResponseLoading ? 1 : 0); (state is MediaGResponseLoading ? 1 : 0);
// استفاده از postFrameCallback برای جلوگیری از setState در build
if (maxSize.value != newMaxSize) { if (maxSize.value != newMaxSize) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) { if (mounted) {
@ -653,7 +652,7 @@ class _TtaPageState extends State<VideoChatPage> {
BorderRadius.circular( BorderRadius.circular(
12), 12),
child: ChatVideoPlayer( child: ChatVideoPlayer(
src: ai?.content?.first src: ai.content?.first
.videoUrl?.url ?? .videoUrl?.url ??
'', '',
custome: custome:

View File

@ -15,7 +15,7 @@ class PostsCubit extends Cubit<PostsState> {
// Disabled enabled.json check - always show posts // Disabled enabled.json check - always show posts
// try { // try {
// final check = await CoursesServices.dio.get('/enabled.json'); // final check = await CoursesServices.dio.get('/enabled.json');
// print('🟢 enabled.json: ${check.data}'); // print('<EFBFBD> enabled.json: ${check.data}');
// if (check.data == false) { // if (check.data == false) {
// emit(PostsFail()); // emit(PostsFail());
// return; // return;

View File

@ -32,7 +32,6 @@ import 'package:hoshan/ui/theme/colors.dart';
import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart'; import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart';
import 'package:hoshan/ui/theme/responsive.dart'; import 'package:hoshan/ui/theme/responsive.dart';
import 'package:hoshan/ui/theme/text.dart'; import 'package:hoshan/ui/theme/text.dart';
import 'package:hoshan/ui/widgets/ai_Banner.dart';
import 'package:hoshan/ui/widgets/components/bot/bot_grid_card.dart'; import 'package:hoshan/ui/widgets/components/bot/bot_grid_card.dart';
import 'package:hoshan/ui/widgets/components/bot/bot_grid_card_placeholder.dart'; import 'package:hoshan/ui/widgets/components/bot/bot_grid_card_placeholder.dart';
import 'package:hoshan/ui/widgets/components/bot/tool_card.dart'; import 'package:hoshan/ui/widgets/components/bot/tool_card.dart';

View File

@ -31,8 +31,10 @@ class _SplashPageState extends State<SplashPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
print('🎯 SplashPage: initState called');
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
print('🎯 SplashPage: PostFrameCallback started');
bool hasDeepLink = false; bool hasDeepLink = false;
if (!kIsWeb) { if (!kIsWeb) {
try { try {
@ -92,11 +94,14 @@ class _SplashPageState extends State<SplashPage> {
if (!hasDeepLink) { if (!hasDeepLink) {
String authToken = AuthTokenStorage.getToken(); String authToken = AuthTokenStorage.getToken();
print('🎯 SplashPage: Auth token = ${authToken.isEmpty ? "EMPTY" : "EXISTS"}');
if (mounted) { if (mounted) {
if (authToken.isEmpty) { if (authToken.isEmpty) {
print('🎯 SplashPage: Navigating to auth page');
FirebasApi.deleteToken(); FirebasApi.deleteToken();
context.go(Routes.auth); context.go(Routes.auth);
} else { } else {
print('🎯 SplashPage: Getting user info');
context.read<UserInfoCubit>().getUserInfo(); context.read<UserInfoCubit>().getUserInfo();
} }
} }

View File

@ -10,23 +10,16 @@ class FamilyBanner extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final hasParent = UserInfoCubit.userInfoModel.parent != null;
if (hasParent) {
return const SizedBox.shrink();
}
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
final hasParent = UserInfoCubit.userInfoModel.parent != null;
if (hasParent) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'شما عضو یک خانواده هستید و نمی‌توانید خانواده جدید اضافه کنید',
),
),
);
return;
}
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(