Skip to content
Merged

... #21

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
2 changes: 1 addition & 1 deletion .github/workflows/google_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.27.2' # Specify your Flutter version
flutter-version: '3.35.1'
channel: 'stable'

- name: Create .env file
Expand Down
2 changes: 1 addition & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
4 changes: 2 additions & 2 deletions android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ pluginManagement {

plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.2.1" apply false
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
id "com.android.application" version "8.9.1" apply false
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
}

include ":app"
Binary file removed assets/fonts/LoveYaLikeASister-Regular.ttf
Binary file not shown.
3 changes: 3 additions & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
8 changes: 6 additions & 2 deletions lib/features/auth/cubit/auth_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ part 'auth_state.dart';
class AuthCubit extends Cubit<AuthState> {
AuthCubit() : super(AuthInitial());

Future<void> signIn({required String email, required String pwd}) async {
Future<void> signIn({
required String email,
required String pwd,
required String url,
}) async {
emit(AuthLoading());
try {
await AuthRepo().signIn(email: email, pwd: pwd);
await AuthRepo().signIn(email: email, pwd: pwd, url: url);
emit(AuthLoaded());
} catch (e) {
emit(AuthError(message: e.toString()));
Expand Down
9 changes: 6 additions & 3 deletions lib/features/auth/repo/auth_repo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ import 'package:pulse/utils/utils.dart';

class AuthRepo {
Future<Map<String, dynamic>?> signIn(
{required String email, required String pwd}) async {
{required String email, required String pwd, required String url}) async {
String userKey = url == umamiUrl ? 'email' : 'username';
var res = await Requests.post(
endpoint: Endpoints.authLogin,
endpoint: '$url/auth/login',
body: {
'email': email,
userKey: email,
'password': pwd,
},
noAuth: true,
);
if (res == null) {
throw Exception('Failed to sign in');
}
baseUrl = url;
prefs.setString(LocalStorage.jwt, res['token']);
prefs.setString(LocalStorage.host, url);
return res;
}

Expand Down
74 changes: 52 additions & 22 deletions lib/features/auth/screens/sign_in_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:pulse/utils/colors.dart';
import 'package:pulse/utils/text.dart';
import 'package:pulse/widgets/custom_button.dart';
import 'package:pulse/widgets/custom_text_field.dart';
import 'package:pulse/widgets/drop_down.dart';
import 'package:pulse/widgets/mordern_btn.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
Expand All @@ -24,8 +25,10 @@ class _SignInScreenState extends State<SignInScreen> {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
TextEditingController email = TextEditingController();
TextEditingController pwd = TextEditingController();
TextEditingController hostUrl = TextEditingController(text: umamiUrl);
bool isAutoValidate = false;
bool isHidden = true;
String hostType = 'Umami Cloud';

bool isValidEmail(String email) {
RegExp emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
Expand Down Expand Up @@ -66,36 +69,60 @@ class _SignInScreenState extends State<SignInScreen> {
style: kTitleTextStyle.copyWith(fontSize: 30)),
Text('Please sign in to continue',
style: kBodyTitleTextStyle),
CustomDropDown(
removePadding: true,
selectedItem: hostType,
items: [
'Umami Cloud',
'Self Hosting',
],
hint: 'Select host type',
title: 'Host Type',
onChanged: (val) {
setState(() {
hostType = val!;
hostUrl = TextEditingController(
text: hostType == 'Umami Cloud' ? umamiUrl : '');
});
},
),
CustomTextField(
data: hostUrl,
preIcon: Icons.link_rounded,
hint: Endpoints.baseUrl,
hint: 'Self-hosted url',
title: 'Host URL',
type: TextInputType.emailAddress,
disabled: true,
// validator: (val) {
// if (val!.isEmpty) {
// return 'This field is required';
// } else if (!isValidEmail(val)) {
// return 'Enter a valid email address';
// }
// return null;
// },
),
CustomTextField(
data: email,
preIcon: Icons.email_rounded,
hint: 'Email',
title: 'Email Address',
type: TextInputType.emailAddress,
disabled: hostType == 'Umami Cloud',
validator: (val) {
if (val!.isEmpty) {
return 'This field is required';
} else if (!isValidEmail(val)) {
return 'Enter a valid email address';
if (!(val!.startsWith('https://'))) {
return 'The url should start with https://';
}
return null;
},
),
CustomTextField(
data: email,
preIcon: hostType == 'Umami Cloud'
? Icons.email_rounded
: Icons.person_2_rounded,
hint: hostType == 'Umami Cloud' ? 'Email' : 'Username',
title: hostType == 'Umami Cloud'
? 'Email Address'
: 'Username',
type: hostType == 'Umami Cloud'
? TextInputType.emailAddress
: TextInputType.name,
validator: hostType == 'Umami Cloud'
? (val) {
if (val!.isEmpty) {
return 'This field is required';
} else if (!isValidEmail(val)) {
return 'Enter a valid email address';
}
return null;
}
: null,
),
CustomTextField(
data: pwd,
preIcon: Icons.lock_rounded,
Expand Down Expand Up @@ -134,7 +161,10 @@ class _SignInScreenState extends State<SignInScreen> {
bool isValid = formKey.currentState!.validate();
if (isValid) {
context.read<AuthCubit>().signIn(
email: email.text.trim(), pwd: pwd.text.trim());
email: email.text.trim(),
pwd: pwd.text.trim(),
url: hostUrl.text.trim(),
);
} else {
setState(() => isAutoValidate = true);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/features/auth/screens/splash_screen.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:in_app_update/in_app_update.dart';
import 'package:pulse/features/websites/screens/websites_screen.dart';
import 'package:pulse/utils/endpoints.dart';
import 'package:pulse/utils/local_storage.dart';
import 'package:pulse/utils/navigation.dart';
import '../../../utils/text.dart';
Expand All @@ -20,7 +21,7 @@ class _SplashScreenState extends State<SplashScreen> {
void initState() {
Future.delayed(Duration(seconds: 2)).then((_) async {
checkForUpdate();

baseUrl = prefs.getString(LocalStorage.host) ?? umamiUrl;
String? token = prefs.getString(LocalStorage.jwt);

Navigation.go(
Expand All @@ -39,7 +40,6 @@ class _SplashScreenState extends State<SplashScreen> {
message: 'New App Update Available! 🎉', context: context);
}
}).catchError((e) {
logger.e(e);
Toast.showToast(message: e.toString(), context: context);
});
}
Expand Down
6 changes: 3 additions & 3 deletions lib/features/sessions/repo/sessions_repo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ class SessionsRepo {
var res = await Requests.get(
useKey: true,
endpoint:
'${Endpoints.websites.replaceAll(Endpoints.baseUrl, 'https://api.umami.is/v1')}/$id/sessions?startAt=$startAt&endAt=$endAt&pageSize=20&page=${pageNumber ?? 1}');
'${Endpoints.websites.replaceAll(umamiUrl, 'https://api.umami.is/v1')}/$id/sessions?startAt=$startAt&endAt=$endAt&pageSize=20&page=${pageNumber ?? 1}');
return Session.toList(res['data']);
}

Future<Session?> getSession(String websiteId, String id) async {
var res = await Requests.get(
useKey: true,
endpoint:
'${Endpoints.websites.replaceAll(Endpoints.baseUrl, 'https://api.umami.is/v1')}/$websiteId/sessions/$id');
'${Endpoints.websites.replaceAll(umamiUrl, 'https://api.umami.is/v1')}/$websiteId/sessions/$id');

return Session.fromJson(res);
}
Expand All @@ -43,7 +43,7 @@ class SessionsRepo {
var res = await Requests.get(
useKey: true,
endpoint:
'${Endpoints.websites.replaceAll(Endpoints.baseUrl, 'https://api.umami.is/v1')}/$websiteId/sessions/$id/activity?startAt=$startAt&endAt=$endAt');
'${Endpoints.websites.replaceAll(umamiUrl, 'https://api.umami.is/v1')}/$websiteId/sessions/$id/activity?startAt=$startAt&endAt=$endAt');
return Event.toList(res);
}
}
8 changes: 5 additions & 3 deletions lib/utils/endpoints.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
late String baseUrl;
String umamiUrl = 'https://cloud.umami.is/api';

class Endpoints {
static const String baseUrl = 'https://cloud.umami.is/api';
static const String authLogin = '$baseUrl/auth/login';
static const String websites = '$baseUrl/websites';
static final String authLogin = '$baseUrl/auth/login';
static final String websites = '$baseUrl/websites';
}
2 changes: 1 addition & 1 deletion lib/utils/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ extension StringExtensions on String {
String get toBrowserIcon {
if (contains('chrome')) {
return Brands.chrome;
} else if (contains('ios')) {
} else if (contains('ios') || contains('safari')) {
return Brands.safari;
} else if (contains('opera')) {
return Brands.opera;
Expand Down
1 change: 1 addition & 0 deletions lib/utils/local_storage.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
class LocalStorage {
static String jwt = 'jwt_token';
static String host = 'host_url';
}
5 changes: 4 additions & 1 deletion lib/utils/requests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ class Requests {
try {
http.Response res;
res = await fn.timeout(const Duration(seconds: 10));

logger.i(endpoint);
if (endpoint.contains('auth/login') && res.statusCode == 404) {
return throw Exception('Invalid host url');
}
if (res.statusCode != okStatusCode) {
return throw Exception(
json.decode(res.body)?['error'] ?? 'Error occurred');
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/drop_down.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class CustomDropDown extends StatelessWidget {
),
Container(
margin: EdgeInsets.only(
bottom: removePadding ? 0 : 15.0, top: title == null ? 0.0 : 8.0),
bottom: removePadding ? 0 : 15.0, top: title == null ? 0.0 : 0.0),
padding: const EdgeInsets.symmetric(horizontal: 10),
height: maxHeight,
decoration: BoxDecoration(
Expand Down
Loading
Loading