Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
# API_URL=https://app-dbef67eb-9a2e-44fa-abff-3e8b83204d9c.cleverapps.io/
# API_URL=https://node.shoy.publicvm.com/
API_URL=https://node.shoy.publicvm.com/
# API_URL=https://0ec88db618e2.ngrok-free.app/
# API_URL=https://67ee79b6365d.ngrok-free.app/
API_test_URL=https://example.com/
# API_URL=https://wanita-hypernormal-cherise.ngrok-free.dev/
# API_URL=https://avah-pollinical-randal.ngrok-free.dev/
# API_URL=https://0f9eef01f130.ngrok-free.app/
# API_URL=https://ingeborg-untrammed-leo.ngrok-free.dev/
# Socket_Url=https://app-dbef67eb-9a2e-44fa-abff-3e8b83204d9c.cleverapps.io
# serverClientId=1096363232606-2fducjadk56bt4nsreqkj2jna7oiomga.apps.googleusercontent.com

44 changes: 32 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,43 @@
# Dockerfile.ci
# ---------------- BASE STAGE ----------------
FROM ghcr.io/cirruslabs/flutter:3.35.5 AS base

WORKDIR /app

# copy pubspec first so we can cache dependencies
# Copy pubspec first for dependency caching
COPY pubspec.* ./
RUN flutter pub get

# copy source
# Copy source code
COPY . .

# LINTING
FROM base AS lint
# ---------------- TEST STAGE ----------------
FROM base AS test
WORKDIR /app

# Run unit/widget tests
RUN flutter test --no-pub

# ---------------- LINT STAGE ----------------
FROM ghcr.io/cirruslabs/flutter:3.35.5 AS lint
WORKDIR /app

COPY pubspec.* ./
RUN flutter pub get

COPY . .
RUN flutter analyze

# UNIT TETSING
FROM base AS test
RUN flutter test

# BUILDING
FROM test AS build-apk
RUN flutter build apk --release
# ---------------- BUILD APK STAGE ----------------
FROM base AS build-apk

# Install Android SDK components early for caching
RUN yes | sdkmanager --licenses

RUN sdkmanager \
"platform-tools" \
"platforms;android-34" \
"build-tools;34.0.0" \
"cmdline-tools;latest"

# Now build release APK
RUN flutter build apk --release
17 changes: 1 addition & 16 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,7 @@ EOF
}
}

stage('Kaniko: Test image') {
steps {
container('kaniko') {
script {
sh '''
echo "Kaniko building test target (no push)..."
/kaniko/executor \
--context=. \
--dockerfile=Dockerfile \
--no-push \
--target=test
'''
}
}
}
}



stage('Kaniko: Build-APK image (build & push)') {
Expand Down
3 changes: 2 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
android:usesCleartextTraffic="true">
<activity android:name=".MainActivity"
android:exported="true"
android:screenOrientation="portrait"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
Expand All @@ -26,7 +27,7 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
Expand Down
21 changes: 12 additions & 9 deletions lib/core/classes/PickedImage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ class PickedImage {
PickedImage({this.file, this.bytes, required this.name, this.path});
}

Future<PickedImage?> pickImage() async {
Future<PickedImage?> pickImage({ImagePicker? picker}) async {
try {
final ImagePicker picker = ImagePicker();
final XFile? image = await picker.pickImage(source: ImageSource.gallery);
final ImagePicker _picker = picker ?? ImagePicker();
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);

if (image != null) {
return PickedImage(
Expand All @@ -28,10 +28,13 @@ Future<PickedImage?> pickImage() async {
}
}

Future<List<PickedImage>> pickImages({int maxImages = 4}) async {
Future<List<PickedImage>> pickImages({
int maxImages = 4,
ImagePicker? picker,
}) async {
try {
final ImagePicker picker = ImagePicker();
final XFile? image = await picker.pickImage(source: ImageSource.gallery);
final ImagePicker _picker = picker ?? ImagePicker();
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);

if (image == null) return [];

Expand All @@ -43,10 +46,10 @@ Future<List<PickedImage>> pickImages({int maxImages = 4}) async {
}
}

Future<PickedImage?> pickVideo() async {
Future<PickedImage?> pickVideo({ImagePicker? picker}) async {
try {
final ImagePicker picker = ImagePicker();
final XFile? video = await picker.pickVideo(source: ImageSource.gallery);
final ImagePicker _picker = picker ?? ImagePicker();
final XFile? video = await _picker.pickVideo(source: ImageSource.gallery);

if (video != null) {
return PickedImage(
Expand Down
3 changes: 2 additions & 1 deletion lib/core/models/usermodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class UserModel {
final bool? tfaVerified;

@HiveField(10)
final Set<String> interests;
final Set<String> interests; // stores only categories names og selected

@HiveField(11)
final String? localProfilePhotoPath; // path of local profile photo

Expand Down
2 changes: 2 additions & 0 deletions lib/core/providers/dio_interceptor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ class AuthInterceptor extends Interceptor {
);

await authLocalRepository.saveTokens(newTokens);
await Future.delayed(const Duration(milliseconds: 5000)); //

print("AuthInterceptor: Token refreshed successfully");
return true;
} on DioException catch (e) {
Expand Down
4 changes: 4 additions & 0 deletions lib/core/services/deep_link_service.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ignore_for_file: unnecessary_null_comparison

import 'dart:async';
import 'dart:developer';
import 'package:app_links/app_links.dart';
import 'package:flutter/widgets.dart';

Expand All @@ -16,7 +17,10 @@ class DeepLinkService {

static void init() {
_appLinks.uriLinkStream.listen((uri) {
log("🔵 DeepLinkService received URI: $uri");

if (uri != null && _completer != null && !_completer!.isCompleted) {
log("🟢 Completing deep link future with: $uri");
_completer!.complete(uri);
}
});
Expand Down
1 change: 0 additions & 1 deletion lib/core/theme/app_theme.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// ignore_for_file: deprecated_member_use
import 'package:flutter/material.dart';
import 'Palette.dart';

Expand Down
10 changes: 10 additions & 0 deletions lib/features/auth/models/ExploreCategory.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class ExploreCategory {
final String id;
final String name;

ExploreCategory({required this.id, required this.name});

factory ExploreCategory.fromMap(Map<String, dynamic> map) {
return ExploreCategory(id: map["id"], name: map["name"]);
}
}
4 changes: 4 additions & 0 deletions lib/features/auth/repositories/auth_local_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,8 @@ class AuthLocalRepository {
]);
_tokenStreamController.add(null);
}

void dispose() {
_tokenStreamController.close();
}
}
71 changes: 61 additions & 10 deletions lib/features/auth/repositories/auth_remote_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:lite_x/core/models/TokensModel.dart';
import 'package:lite_x/core/models/usermodel.dart';
import 'package:lite_x/core/providers/dio_interceptor.dart';
import 'package:lite_x/core/services/deep_link_service.dart';
import 'package:lite_x/features/auth/models/ExploreCategory.dart';
import 'package:path_provider/path_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:dio/dio.dart';
Expand All @@ -27,11 +28,12 @@ class AuthRemoteRepository {
final Dio _dio;
AuthRemoteRepository({required Dio dio}) : _dio = dio;
//---------------------------------------------------github------------------------------------------------------//
Future<Either<AppFailure, (UserModel, TokensModel)>> loginWithGithub() async {

Future<Either<AppFailure, (UserModel user, TokensModel tokens, bool newuser)>>
loginWithGithub() async {
try {
final baseUrl = dotenv.env["API_URL"]!;
final authUrl = "${baseUrl}oauth2/authorize/github";

final opened = await launchUrl(
Uri.parse(authUrl),
mode: LaunchMode.externalApplication,
Expand All @@ -40,7 +42,6 @@ class AuthRemoteRepository {
if (!opened) {
return left(AppFailure(message: "Could not open browser"));
}

final uri = await DeepLinkService.waitForLink();

if (uri == null) {
Expand All @@ -56,7 +57,9 @@ class AuthRemoteRepository {
}

final decodedUser = Uri.decodeComponent(userRaw);
final user = UserModel.fromJson(decodedUser);
final Map<String, dynamic> userJson = jsonDecode(decodedUser);
final bool newuser = userJson["newuser"];
final user = UserModel.fromMap(userJson);

final tokens = TokensModel(
accessToken: token,
Expand All @@ -65,20 +68,21 @@ class AuthRemoteRepository {
refreshTokenExpiry: DateTime.now().add(const Duration(days: 30)),
);

return right((user, tokens));
return right((user, tokens, newuser));
} catch (e) {
return left(AppFailure(message: e.toString()));
}
}

//--------------------------------------------------------google-------------------------------------------------------------------//

final _googleSignIn = signIn.GoogleSignIn(
serverClientId:
"https://1096363232606-2fducjadk56bt4nsreqkj2jna7oiomga.apps.googleusercontent.com",
"1096363232606-2fducjadk56bt4nsreqkj2jna7oiomga.apps.googleusercontent.com",
scopes: ['email', 'https://www.googleapis.com/auth/userinfo.profile'],
);

Future<Either<AppFailure, (UserModel, TokensModel)>>
Future<Either<AppFailure, (UserModel user, TokensModel tokens, bool newuser)>>
signInWithGoogleAndroid() async {
try {
final String apiUrl = dotenv.env["API_URL"]!;
Expand All @@ -87,10 +91,14 @@ class AuthRemoteRepository {
if (googleUser == null) {
return left(AppFailure(message: "Google login canceled"));
}

final googleAuth = await googleUser.authentication;
final idToken = googleAuth.idToken;
final email = googleUser.email;
print("GOOGLE EMAIL = $email\n");

// final existsResult = await check_email(email: email);
// final exists = existsResult.fold((_) => false, (v) => v);

final idToken = googleAuth.idToken;
debugPrint("GOOGLE ID TOKEN = $idToken");

final resp = await http.post(
Expand All @@ -112,13 +120,55 @@ class AuthRemoteRepository {
accessTokenExpiry: DateTime.now().add(const Duration(hours: 1)),
refreshTokenExpiry: DateTime.now().add(const Duration(days: 30)),
);
final newuser = data["user"]["newuser"]; //

return right((user, tokens));
return right((user, tokens, newuser));
} catch (e) {
return left(AppFailure(message: e.toString()));
}
}

//-------------------------------------------------- categories------------------------------------------------------------//
Future<Either<AppFailure, List<ExploreCategory>>> getCategories() async {
try {
final response = await _dio.get("api/explore/categories");

final List data = response.data['data'];
final categories = data.map((e) => ExploreCategory.fromMap(e)).toList();

return right(categories);
} catch (e) {
return left(AppFailure(message: "Failed to load categories"));
}
}

Future<Either<AppFailure, String>> saveUserInterests(
Set<String> categories,
) async {
try {
final response = await _dio.post(
"api/explore/preferred-categories",
data: {"categories": categories.toList()},
);

return right(response.data['message'] ?? "Interests saved");
} catch (e) {
return left(AppFailure(message: "Failed to save interests"));
}
}

Future<Either<AppFailure, List<String>>> getUserInterests() async {
try {
final response = await _dio.get("api/explore/preferred-categories");
final list = response.data['preferredCategories'] as List;
final names = list.map((e) => e['name'].toString()).toList();

return right(names);
} catch (e) {
return left(AppFailure(message: "Failed to load interests"));
}
}

//--------------------------------------------SignUp---------------------------------------------------------//
// Register new user
Future<Either<AppFailure, String>> create({
Expand Down Expand Up @@ -293,6 +343,7 @@ class AuthRemoteRepository {
);
final newUsername = response.data['user']['username'] as String;
final updatedUser = currentUser.copyWith(username: newUsername);
print("asermohamed after update username${response.data['tokens']}");
final newtokens = TokensModel.fromMap_update(response.data['tokens']);
return right((updatedUser, newtokens));
} on DioException {
Expand Down
Loading
Loading