update
This commit is contained in:
No files matched your search
@@ -1,11 +1,21 @@
|
|||||||
|
import java.util.Properties
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||||
id("dev.flutter.flutter-gradle-plugin")
|
id("dev.flutter.flutter-gradle-plugin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val keystoreProperties =
|
||||||
|
Properties().apply {
|
||||||
|
val keystorePropertiesFile = rootProject.file("key.properties")
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystorePropertiesFile.inputStream().use { load(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.example.mylis"
|
namespace = "com.duidev.mylis"
|
||||||
compileSdk = flutter.compileSdkVersion
|
compileSdk = flutter.compileSdkVersion
|
||||||
ndkVersion = flutter.ndkVersion
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
@@ -16,7 +26,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId = "com.example.mylis"
|
applicationId = "com.duidev.mylis"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
minSdk = flutter.minSdkVersion
|
minSdk = flutter.minSdkVersion
|
||||||
@@ -25,11 +35,20 @@ android {
|
|||||||
versionName = flutter.versionName
|
versionName = flutter.versionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
create("release") {
|
||||||
|
keyAlias = keystoreProperties["keyAlias"] as String?
|
||||||
|
keyPassword = keystoreProperties["keyPassword"] as String?
|
||||||
|
storeFile = (keystoreProperties["storeFile"] as String?)?.let {
|
||||||
|
file(it)
|
||||||
|
}
|
||||||
|
storePassword = keystoreProperties["storePassword"] as String?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// TODO: Add your own signing config for the release build.
|
signingConfig = signingConfigs.getByName("release")
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"project_info": {
|
||||||
|
"project_number": "732589737888",
|
||||||
|
"project_id": "mylis-a4983",
|
||||||
|
"storage_bucket": "mylis-a4983.firebasestorage.app"
|
||||||
|
},
|
||||||
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:732589737888:android:3f782e07e62ace55a446c5",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "com.duidev.mylis"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyAibwJgxVnkVOOIGsjbf2SpW_isJOFTtAg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration_version": "1"
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.example.mylis
|
package com.duidev.mylis
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -383,7 +383,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.mylis;
|
PRODUCT_BUNDLE_IDENTIFIER = com.duidev.mylis;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@@ -399,7 +399,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.mylis.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.duidev.mylis.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
@@ -416,7 +416,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.mylis.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.duidev.mylis.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
@@ -431,7 +431,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.mylis.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.duidev.mylis.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
@@ -563,7 +563,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.mylis;
|
PRODUCT_BUNDLE_IDENTIFIER = com.duidev.mylis;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
@@ -586,7 +586,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.mylis;
|
PRODUCT_BUNDLE_IDENTIFIER = com.duidev.mylis;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
SessionStore.baseUrl().then((value) {
|
SessionStore.session().then((value) {
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_baseUrl.text = value;
|
_baseUrl.text = value.baseUrl;
|
||||||
|
_username.text = value.username ?? '';
|
||||||
|
_password.text = value.password ?? '';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +45,6 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
setState(() => _loading = true);
|
setState(() => _loading = true);
|
||||||
try {
|
try {
|
||||||
final serverUrl = normalizeBaseUrl(_baseUrl.text);
|
final serverUrl = normalizeBaseUrl(_baseUrl.text);
|
||||||
await SessionStore.saveBaseUrl(serverUrl);
|
|
||||||
final api = ApiClient(baseUrl: serverUrl);
|
final api = ApiClient(baseUrl: serverUrl);
|
||||||
final data = await api.post('api/mobile/login', {
|
final data = await api.post('api/mobile/login', {
|
||||||
'username': _username.text.trim(),
|
'username': _username.text.trim(),
|
||||||
@@ -53,7 +54,12 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
if (token == null || token.isEmpty) {
|
if (token == null || token.isEmpty) {
|
||||||
throw ApiException('Token login tidak diterima dari server.');
|
throw ApiException('Token login tidak diterima dari server.');
|
||||||
}
|
}
|
||||||
await SessionStore.saveToken(token);
|
await SessionStore.saveLogin(
|
||||||
|
baseUrl: serverUrl,
|
||||||
|
token: token,
|
||||||
|
username: _username.text.trim(),
|
||||||
|
password: _password.text,
|
||||||
|
);
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class ApiClient {
|
|||||||
ApiClient({required this.baseUrl, this.token});
|
ApiClient({required this.baseUrl, this.token});
|
||||||
|
|
||||||
final String baseUrl;
|
final String baseUrl;
|
||||||
final String? token;
|
String? token;
|
||||||
|
|
||||||
Uri _uri(String path, [Map<String, String>? query]) {
|
Uri _uri(String path, [Map<String, String>? query]) {
|
||||||
final normalizedBase = normalizeBaseUrl(baseUrl);
|
final normalizedBase = normalizeBaseUrl(baseUrl);
|
||||||
@@ -25,12 +25,55 @@ class ApiClient {
|
|||||||
String path,
|
String path,
|
||||||
Map<String, dynamic> body,
|
Map<String, dynamic> body,
|
||||||
) async {
|
) async {
|
||||||
final uri = _uri(path);
|
return _sendWithRenew(
|
||||||
|
path,
|
||||||
|
(client, uri) => client
|
||||||
|
.post(uri, headers: _headers, body: jsonEncode(body))
|
||||||
|
.timeout(const Duration(seconds: 15)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> get(
|
||||||
|
String path, [
|
||||||
|
Map<String, String>? query,
|
||||||
|
]) async {
|
||||||
|
return _sendWithRenew(
|
||||||
|
path,
|
||||||
|
(client, uri) => client
|
||||||
|
.get(uri, headers: _headers)
|
||||||
|
.timeout(const Duration(seconds: 15)),
|
||||||
|
query,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> _sendWithRenew(
|
||||||
|
String path,
|
||||||
|
Future<http.Response> Function(http.Client client, Uri uri) send, [
|
||||||
|
Map<String, String>? query,
|
||||||
|
]) async {
|
||||||
|
final uri = _uri(path, query);
|
||||||
|
try {
|
||||||
|
return await _sendOnce(uri, send);
|
||||||
|
} on ApiException catch (error) {
|
||||||
|
if (!_canRenew(path, error)) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
final renewed = await _renewToken(uri);
|
||||||
|
if (!renewed) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
debugPrint('MyLIS session diperbarui, mengulang request: $uri');
|
||||||
|
return _sendOnce(uri, send);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> _sendOnce(
|
||||||
|
Uri uri,
|
||||||
|
Future<http.Response> Function(http.Client client, Uri uri) send,
|
||||||
|
) async {
|
||||||
final client = _clientFor(uri);
|
final client = _clientFor(uri);
|
||||||
try {
|
try {
|
||||||
final response = await client
|
final response = await send(client, uri);
|
||||||
.post(uri, headers: _headers, body: jsonEncode(body))
|
|
||||||
.timeout(const Duration(seconds: 15));
|
|
||||||
return _decode(response, uri);
|
return _decode(response, uri);
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
throw _connectionException(uri, error, stackTrace);
|
throw _connectionException(uri, error, stackTrace);
|
||||||
@@ -39,19 +82,64 @@ class ApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> get(
|
bool _canRenew(String path, ApiException error) {
|
||||||
String path, [
|
return token != null &&
|
||||||
Map<String, String>? query,
|
error.statusCode == 401 &&
|
||||||
]) async {
|
path != 'api/mobile/login' &&
|
||||||
final uri = _uri(path, query);
|
!path.endsWith('/login');
|
||||||
final client = _clientFor(uri);
|
}
|
||||||
|
|
||||||
|
Future<bool> _renewToken(Uri failedUri) async {
|
||||||
|
final credentials = await SessionStore.credentials();
|
||||||
|
if (credentials == null) {
|
||||||
|
debugPrint(
|
||||||
|
'MyLIS session expired untuk $failedUri; kredensial tersimpan tidak ada.',
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final loginUri = _uri('api/mobile/login');
|
||||||
|
final client = _clientFor(loginUri);
|
||||||
try {
|
try {
|
||||||
|
debugPrint(
|
||||||
|
'MyLIS session expired untuk $failedUri; mencoba renew token.',
|
||||||
|
);
|
||||||
final response = await client
|
final response = await client
|
||||||
.get(uri, headers: _headers)
|
.post(
|
||||||
|
loginUri,
|
||||||
|
headers: const {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: jsonEncode({
|
||||||
|
'username': credentials.username,
|
||||||
|
'password': credentials.password,
|
||||||
|
}),
|
||||||
|
)
|
||||||
.timeout(const Duration(seconds: 15));
|
.timeout(const Duration(seconds: 15));
|
||||||
return _decode(response, uri);
|
final data = _decode(response, loginUri);
|
||||||
|
final token = data['token']?.toString() ?? '';
|
||||||
|
if (token.isEmpty) {
|
||||||
|
debugPrint(
|
||||||
|
'MyLIS renew token gagal: respons login tidak berisi token.',
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.token = token;
|
||||||
|
await SessionStore.saveLogin(
|
||||||
|
baseUrl: baseUrl,
|
||||||
|
token: token,
|
||||||
|
username: credentials.username,
|
||||||
|
password: credentials.password,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} on ApiException catch (error) {
|
||||||
|
debugPrint('MyLIS renew token gagal: ${error.message}');
|
||||||
|
return false;
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
throw _connectionException(uri, error, stackTrace);
|
debugPrint('MyLIS renew token gagal tidak terduga: $error');
|
||||||
|
debugPrintStack(stackTrace: stackTrace);
|
||||||
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
@@ -95,6 +183,7 @@ class ApiClient {
|
|||||||
'Server mengembalikan HTTP ${response.statusCode}. '
|
'Server mengembalikan HTTP ${response.statusCode}. '
|
||||||
'URL: $uri. Kemungkinan request masuk ke virtual host/proxy yang salah. '
|
'URL: $uri. Kemungkinan request masuk ke virtual host/proxy yang salah. '
|
||||||
'Detail awal respons: $bodyPreview',
|
'Detail awal respons: $bodyPreview',
|
||||||
|
statusCode: response.statusCode,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +201,7 @@ class ApiClient {
|
|||||||
throw ApiException(
|
throw ApiException(
|
||||||
'Respons server tidak bisa dibaca. '
|
'Respons server tidak bisa dibaca. '
|
||||||
'URL: $uri. Status HTTP: ${response.statusCode}.',
|
'URL: $uri. Status HTTP: ${response.statusCode}.',
|
||||||
|
statusCode: response.statusCode,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +284,7 @@ class ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ApiException implements Exception {
|
class ApiException implements Exception {
|
||||||
ApiException(this.message);
|
ApiException(this.message, {this.statusCode});
|
||||||
final String message;
|
final String message;
|
||||||
|
final int? statusCode;
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,8 @@ part of '../app/app.dart';
|
|||||||
class SessionStore {
|
class SessionStore {
|
||||||
static const _tokenKey = 'mylis_token';
|
static const _tokenKey = 'mylis_token';
|
||||||
static const _baseUrlKey = 'mylis_base_url';
|
static const _baseUrlKey = 'mylis_base_url';
|
||||||
|
static const _usernameKey = 'mylis_username';
|
||||||
|
static const _passwordKey = 'mylis_password';
|
||||||
|
|
||||||
static Future<String?> token() async {
|
static Future<String?> token() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
@@ -19,6 +21,8 @@ class SessionStore {
|
|||||||
return SessionData(
|
return SessionData(
|
||||||
token: prefs.getString(_tokenKey),
|
token: prefs.getString(_tokenKey),
|
||||||
baseUrl: prefs.getString(_baseUrlKey) ?? kDefaultBaseUrl,
|
baseUrl: prefs.getString(_baseUrlKey) ?? kDefaultBaseUrl,
|
||||||
|
username: prefs.getString(_usernameKey),
|
||||||
|
password: prefs.getString(_passwordKey),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +31,32 @@ class SessionStore {
|
|||||||
await prefs.setString(_tokenKey, token);
|
await prefs.setString(_tokenKey, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> saveLogin({
|
||||||
|
required String baseUrl,
|
||||||
|
required String token,
|
||||||
|
required String username,
|
||||||
|
required String password,
|
||||||
|
}) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setString(_baseUrlKey, normalizeBaseUrl(baseUrl));
|
||||||
|
await prefs.setString(_tokenKey, token);
|
||||||
|
await prefs.setString(_usernameKey, username);
|
||||||
|
await prefs.setString(_passwordKey, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<StoredCredentials?> credentials() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final username = prefs.getString(_usernameKey);
|
||||||
|
final password = prefs.getString(_passwordKey);
|
||||||
|
if (username == null ||
|
||||||
|
username.trim().isEmpty ||
|
||||||
|
password == null ||
|
||||||
|
password.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return StoredCredentials(username: username, password: password);
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> saveBaseUrl(String baseUrl) async {
|
static Future<void> saveBaseUrl(String baseUrl) async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await prefs.setString(_baseUrlKey, normalizeBaseUrl(baseUrl));
|
await prefs.setString(_baseUrlKey, normalizeBaseUrl(baseUrl));
|
||||||
@@ -39,10 +69,24 @@ class SessionStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SessionData {
|
class SessionData {
|
||||||
const SessionData({this.token, this.baseUrl = kDefaultBaseUrl});
|
const SessionData({
|
||||||
|
this.token,
|
||||||
|
this.baseUrl = kDefaultBaseUrl,
|
||||||
|
this.username,
|
||||||
|
this.password,
|
||||||
|
});
|
||||||
|
|
||||||
final String? token;
|
final String? token;
|
||||||
final String baseUrl;
|
final String baseUrl;
|
||||||
|
final String? username;
|
||||||
|
final String? password;
|
||||||
|
}
|
||||||
|
|
||||||
|
class StoredCredentials {
|
||||||
|
const StoredCredentials({required this.username, required this.password});
|
||||||
|
|
||||||
|
final String username;
|
||||||
|
final String password;
|
||||||
}
|
}
|
||||||
|
|
||||||
String normalizeBaseUrl(String value) {
|
String normalizeBaseUrl(String value) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
|
|||||||
set(BINARY_NAME "mylis")
|
set(BINARY_NAME "mylis")
|
||||||
# The unique GTK application identifier for this application. See:
|
# The unique GTK application identifier for this application. See:
|
||||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||||
set(APPLICATION_ID "com.example.mylis")
|
set(APPLICATION_ID "com.duidev.mylis")
|
||||||
|
|
||||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||||
# versions of CMake.
|
# versions of CMake.
|
||||||
|
|||||||
@@ -395,7 +395,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.mylis.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.duidev.mylis.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mylis.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mylis";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mylis.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mylis";
|
||||||
@@ -409,7 +409,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.mylis.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.duidev.mylis.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mylis.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mylis";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mylis.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mylis";
|
||||||
@@ -423,7 +423,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.mylis.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.duidev.mylis.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mylis.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mylis";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mylis.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mylis";
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
PRODUCT_NAME = MyLIS
|
PRODUCT_NAME = MyLIS
|
||||||
|
|
||||||
// The application's bundle identifier
|
// The application's bundle identifier
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.mylis
|
PRODUCT_BUNDLE_IDENTIFIER = com.duidev.mylis
|
||||||
|
|
||||||
// The copyright displayed in application information
|
// The copyright displayed in application information
|
||||||
PRODUCT_COPYRIGHT = Copyright © 2026 Rumah Sakit Umum Daerah Dr. Saiful Anwar. All rights reserved.
|
PRODUCT_COPYRIGHT = Copyright © 2026 Rumah Sakit Umum Daerah Dr. Saiful Anwar. All rights reserved.
|
||||||
Reference in New Issue
Block a user