generated from KhalidWar/flutter_project_template
-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #69 from KhalidWar/dev
What's new: 1. Fixed offline data 2. Fixed several UI bug. 3. Overhauled Offline data caching mechanism. 4. Improved date and time format. 5. Improved error messages. 6. Several under the hood improvements. 7. Too many other improvements to list.
- Loading branch information
Showing
167 changed files
with
3,120 additions
and
2,958 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import 'package:anonaddy/notifiers/account/account_state.dart'; | ||
import 'package:anonaddy/services/account/account_service.dart'; | ||
import 'package:anonaddy/utilities/utilities.dart'; | ||
import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||
|
||
final accountStateNotifier = | ||
StateNotifierProvider<AccountNotifier, AccountState>((ref) { | ||
return AccountNotifier(accountService: ref.read(accountServiceProvider)); | ||
}); | ||
|
||
class AccountNotifier extends StateNotifier<AccountState> { | ||
AccountNotifier({ | ||
required this.accountService, | ||
AccountState? initialState, | ||
}) : super(initialState ?? AccountState.initialState()); | ||
|
||
final AccountService accountService; | ||
|
||
/// Updates UI to the newState | ||
void _updateState(AccountState newState) { | ||
if (mounted) state = newState; | ||
} | ||
|
||
Future<void> fetchAccount() async { | ||
try { | ||
_updateState(state.copyWith(status: AccountStatus.loading)); | ||
|
||
final account = await accountService.fetchAccount(); | ||
|
||
/// Construct new state | ||
final newState = | ||
state.copyWith(status: AccountStatus.loaded, account: account); | ||
|
||
/// Update UI with the latest state | ||
_updateState(newState); | ||
} catch (error) { | ||
_updateState(state.copyWith( | ||
status: AccountStatus.failed, | ||
errorMessage: error.toString(), | ||
)); | ||
|
||
/// Retry after an error | ||
_retryOnError(); | ||
} | ||
} | ||
|
||
/// Silently fetches the latest account data and displays them | ||
Future<void> refreshAccount() async { | ||
try { | ||
/// Only trigger fetch API when app is Foreground to avoid API spamming | ||
final account = await accountService.fetchAccount(); | ||
|
||
/// Update UI with the latest state | ||
_updateState(state.copyWith( | ||
status: AccountStatus.loaded, | ||
account: account, | ||
)); | ||
} catch (error) { | ||
Utilities.showToast(error.toString()); | ||
} | ||
} | ||
|
||
/// Triggers [fetchAccount] when it fails after a certain amount of time | ||
Future _retryOnError() async { | ||
if (state.isFailed) { | ||
await Future.delayed(const Duration(seconds: 5)); | ||
await fetchAccount(); | ||
} | ||
} | ||
|
||
/// Loads [Account] data from disk | ||
Future<void> loadAccountFromDisk() async { | ||
try { | ||
final account = await accountService.loadAccountFromDisk(); | ||
_updateState(state.copyWith( | ||
status: AccountStatus.loaded, | ||
account: account, | ||
)); | ||
} catch (_) { | ||
return; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import 'package:anonaddy/models/alias/alias.dart'; | ||
import 'package:anonaddy/notifiers/alias_state/alias_screen_state.dart'; | ||
import 'package:anonaddy/notifiers/alias_state/alias_tab_notifier.dart'; | ||
import 'package:anonaddy/services/alias/alias_service.dart'; | ||
import 'package:anonaddy/shared_components/constants/app_strings.dart'; | ||
import 'package:anonaddy/shared_components/constants/toast_message.dart'; | ||
import 'package:anonaddy/utilities/utilities.dart'; | ||
import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||
|
||
final aliasScreenStateNotifier = | ||
StateNotifierProvider.autoDispose<AliasScreenNotifier, AliasScreenState>( | ||
(ref) { | ||
final aliasTab = ref.read(aliasTabStateNotifier.notifier); | ||
ref.onDispose(() => aliasTab.refreshAliases()); | ||
|
||
return AliasScreenNotifier( | ||
aliasService: ref.read(aliasServiceProvider), | ||
aliasTabNotifier: aliasTab, | ||
); | ||
}); | ||
|
||
class AliasScreenNotifier extends StateNotifier<AliasScreenState> { | ||
AliasScreenNotifier({ | ||
required this.aliasService, | ||
required this.aliasTabNotifier, | ||
AliasScreenState? initialState, | ||
}) : super(initialState ?? AliasScreenState.initialState()); | ||
|
||
final AliasService aliasService; | ||
final AliasTabNotifier aliasTabNotifier; | ||
|
||
/// Update AliasScreen state | ||
void _updateState(AliasScreenState newState) { | ||
if (mounted) state = newState; | ||
} | ||
|
||
Future<void> fetchSpecificAlias(String aliasId) async { | ||
try { | ||
/// Initially set AliasScreen to loading | ||
_updateState(state.copyWith(status: AliasScreenStatus.loading)); | ||
|
||
final updatedAlias = await aliasService.fetchSpecificAlias(aliasId); | ||
|
||
/// Assign newly fetched alias data to AliasScreen state | ||
final newState = | ||
state.copyWith(status: AliasScreenStatus.loaded, alias: updatedAlias); | ||
_updateState(newState); | ||
} catch (error) { | ||
final newState = state.copyWith( | ||
status: AliasScreenStatus.failed, | ||
errorMessage: error.toString(), | ||
); | ||
_updateState(newState); | ||
} | ||
} | ||
|
||
Future<void> editDescription(String newDesc) async { | ||
try { | ||
final updatedAlias = | ||
await aliasService.updateAliasDescription(state.alias.id, newDesc); | ||
Utilities.showToast(ToastMessage.editDescriptionSuccess); | ||
_updateState(state.copyWith(alias: updatedAlias)); | ||
} catch (error) { | ||
Utilities.showToast(error.toString()); | ||
} | ||
} | ||
|
||
Future<void> deactivateAlias() async { | ||
try { | ||
_updateState(state.copyWith(isToggleLoading: true)); | ||
await aliasService.deactivateAlias(state.alias.id); | ||
final updatedAlias = state.alias.copyWith(active: false); | ||
_updateState(state.copyWith(isToggleLoading: false, alias: updatedAlias)); | ||
} catch (error) { | ||
Utilities.showToast(error.toString()); | ||
_updateState(state.copyWith(isToggleLoading: false)); | ||
} | ||
} | ||
|
||
Future<void> activateAlias() async { | ||
try { | ||
_updateState(state.copyWith(isToggleLoading: true)); | ||
final newAlias = await aliasService.activateAlias(state.alias.id); | ||
final updateAlias = state.alias.copyWith(active: newAlias.active); | ||
_updateState(state.copyWith(isToggleLoading: false, alias: updateAlias)); | ||
} catch (error) { | ||
Utilities.showToast(error.toString()); | ||
_updateState(state.copyWith(isToggleLoading: false)); | ||
} | ||
} | ||
|
||
Future<void> deleteAlias(Alias alias) async { | ||
try { | ||
_updateState(state.copyWith(deleteAliasLoading: true)); | ||
await aliasService.deleteAlias(alias.id); | ||
Utilities.showToast(ToastMessage.deleteAliasSuccess); | ||
final updatedAlias = state.alias.copyWith(deletedAt: ''); | ||
aliasTabNotifier.removeDeletedAlias(alias); | ||
|
||
final newState = | ||
state.copyWith(deleteAliasLoading: false, alias: updatedAlias); | ||
_updateState(newState); | ||
} catch (error) { | ||
Utilities.showToast(error.toString()); | ||
_updateState(state.copyWith(deleteAliasLoading: false)); | ||
rethrow; | ||
} | ||
} | ||
|
||
Future<void> restoreAlias(Alias alias) async { | ||
try { | ||
_updateState(state.copyWith(deleteAliasLoading: true)); | ||
final newAlias = await aliasService.restoreAlias(alias.id); | ||
Utilities.showToast(ToastMessage.restoreAliasSuccess); | ||
aliasTabNotifier.removeRestoredAlias(alias); | ||
|
||
final newState = | ||
state.copyWith(deleteAliasLoading: false, alias: newAlias); | ||
_updateState(newState); | ||
} catch (error) { | ||
Utilities.showToast(error.toString()); | ||
_updateState(state.copyWith(deleteAliasLoading: false)); | ||
} | ||
} | ||
|
||
Future<void> updateAliasDefaultRecipient(List<String> recipients) async { | ||
try { | ||
_updateState(state.copyWith(updateRecipientLoading: true)); | ||
final updatedAlias = await aliasService.updateAliasDefaultRecipient( | ||
state.alias.id, recipients); | ||
final newState = | ||
state.copyWith(updateRecipientLoading: false, alias: updatedAlias); | ||
_updateState(newState); | ||
} catch (error) { | ||
Utilities.showToast(error.toString()); | ||
_updateState(state.copyWith(updateRecipientLoading: false)); | ||
} | ||
} | ||
|
||
Future<void> forgetAlias() async { | ||
try { | ||
await aliasService.forgetAlias(state.alias.id); | ||
Utilities.showToast(ToastMessage.forgetAliasSuccess); | ||
} catch (error) { | ||
Utilities.showToast(error.toString()); | ||
} | ||
} | ||
|
||
Future<void> sendFromAlias(String destinationEmail) async { | ||
try { | ||
/// https://anonaddy.com/help/sending-email-from-an-alias/ | ||
final leftPartOfAlias = state.alias.email.split('@')[0]; | ||
final rightPartOfAlias = state.alias.email.split('@')[1]; | ||
final recipientEmail = destinationEmail.replaceAll('@', '='); | ||
final generatedAddress = | ||
'$leftPartOfAlias+$recipientEmail@$rightPartOfAlias'; | ||
await Utilities.copyOnTap(generatedAddress); | ||
Utilities.showToast(ToastMessage.sendFromAliasSuccess); | ||
} catch (error) { | ||
Utilities.showToast(AppStrings.somethingWentWrong); | ||
} | ||
} | ||
} |
File renamed without changes.
Oops, something went wrong.