Skip to content

Commit

Permalink
feat: Implement activity log #2895
Browse files Browse the repository at this point in the history
  • Loading branch information
bibash28 committed Oct 3, 2024
1 parent 1a1edf5 commit a67e04c
Show file tree
Hide file tree
Showing 32 changed files with 534 additions and 9 deletions.
3 changes: 3 additions & 0 deletions lib/activity_log/activity_log.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export 'activity_log_manager.dart';
export 'log_class.dart';
export 'log_enum.dart';
119 changes: 119 additions & 0 deletions lib/activity_log/activity_log_manager.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import 'dart:convert';

import 'package:altme/activity_log/activity_log.dart';
import 'package:secure_storage/secure_storage.dart';

class ActivityLogManager {
ActivityLogManager(SecureStorageProvider secureStorageProvider)
: _secureStorageProvider = secureStorageProvider;

final SecureStorageProvider _secureStorageProvider;

static const int maxLogsPerBatch = 100;

/// Get logs from single batch
Future<List<LogData>> _getLogsForCurrentBatch(int index) async {
final logDataJson = await _secureStorageProvider.get('log_batch_$index');

if (logDataJson != null) {
final logStrings = logDataJson.split('\n');
return logStrings
.map(
(logString) => LogData.fromJson(
jsonDecode(logString) as Map<String, dynamic>,
),
)
.toList();
}
return [];
}

/// Set log entry
Future<void> writeLog(LogData log) async {
final currentBatchIndex = await _getCurrentBatchIndex();

List<LogData> logs = await _getLogsForCurrentBatch(currentBatchIndex);

// If the batch is full, increment the batch index and start a new batch
if (logs.length >= maxLogsPerBatch) {
await _incrementBatchIndex();
logs = [];
}

logs.add(log);

final logJsonList = logs.map((log) => jsonEncode(log.toJson())).toList();

await _secureStorageProvider.set(
'log_batch_$currentBatchIndex',
logJsonList.join('\n'),
);
}

/// Get logs from all batches
Future<List<LogData>> readAllLogs() async {
final currentBatchIndex = await _getCurrentBatchIndex();
final allLogs = <LogData>[];

for (int i = currentBatchIndex; i == 0; i--) {
final logData = await _getLogsForCurrentBatch(i);
allLogs.addAll(logData.reversed.toList());
}
return allLogs;
}

// /// Get paginated logs
// Future<List<LogData>> readLogs(int limit, int offset) async {
// final allLogs = <LogData>[];

// final currentBatchIndex = await _getCurrentBatchIndex();
// // Retrieve logs from all batches
// for (int i = 0; i <= currentBatchIndex; i++) {
// final logData = await _secureStorageProvider.get('log_batch_$i');
// if (logData != null) {
// final logStrings = logData.split('\n');
// final logs = logStrings
// .map(
// (logString) => LogData.fromJson(
// jsonDecode(logString) as Map<String, dynamic>,
// ),
// )
// .toList();
// allLogs.addAll(logs);
// }
// }

// // Apply pagination logic (limit and offset)
// // but this logic needs to be improved.. it is paginating later..
// // we need to consider while fetching
// return allLogs.skip(offset).take(limit).toList();
// }

/// Clear all logs
Future<void> clearLogs() async {
final currentBatchIndex = await _getCurrentBatchIndex();
for (int i = 0; i <= currentBatchIndex; i++) {
await _secureStorageProvider.delete('log_batch_$i');
}
await _secureStorageProvider.delete('currentBatchIndex');
}

/// get Current Batch
Future<int> _getCurrentBatchIndex() async {
final index = await _secureStorageProvider.get('currentBatchIndex');
if (index != null) {
return int.parse(index);
}

return 0;
}

Future<void> _incrementBatchIndex() async {
final currentBatchIndex = await _getCurrentBatchIndex();
final newBatchIndex = currentBatchIndex + 1;
await _secureStorageProvider.set(
'currentBatchIndex',
newBatchIndex.toString(),
);
}
}
33 changes: 33 additions & 0 deletions lib/activity_log/log_class.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:altme/activity_log/activity_log.dart';
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';

part 'log_class.g.dart';

@JsonSerializable()
class LogData extends Equatable {
const LogData({
required this.type,
required this.timestamp,
this.credentialId,
this.data,
});

factory LogData.fromJson(Map<String, dynamic> json) =>
_$LogDataFromJson(json);

final LogType type;
final DateTime timestamp;
final String? credentialId;
final String? data;

Map<String, dynamic> toJson() => _$LogDataToJson(this);

@override
List<Object?> get props => [
type,
timestamp,
credentialId,
data,
];
}
9 changes: 9 additions & 0 deletions lib/activity_log/log_enum.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
enum LogType {
walletInit,
backupData,
restoreWallet,
addVC,
deleteVC,
presentVC,
importKey,
}
3 changes: 3 additions & 0 deletions lib/app/view/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'package:altme/activity_log/activity_log.dart';
import 'package:altme/app/app.dart';
import 'package:altme/chat_room/chat_room.dart';
import 'package:altme/connection_bridge/connection_bridge.dart';
Expand Down Expand Up @@ -149,6 +150,7 @@ class App extends StatelessWidget {
jwtDecode: JWTDecode(),
profileCubit: context.read<ProfileCubit>(),
walletCubit: context.read<WalletCubit>(),
activityLogManager: ActivityLogManager(secureStorageProvider),
),
),
BlocProvider<ManageNetworkCubit>(
Expand Down Expand Up @@ -184,6 +186,7 @@ class App extends StatelessWidget {
walletCubit: context.read<WalletCubit>(),
oidc4vc: OIDC4VC(),
jwtDecode: JWTDecode(),
activityLogManager: ActivityLogManager(secureStorageProvider),
),
),
BlocProvider<AltmeChatSupportCubit>(
Expand Down
25 changes: 25 additions & 0 deletions lib/credentials/cubit/credentials_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';

import 'package:altme/activity_log/activity_log.dart';
import 'package:altme/app/app.dart';
import 'package:altme/dashboard/dashboard.dart';
import 'package:altme/dashboard/home/tab_bar/credentials/models/activity/activity.dart';
Expand Down Expand Up @@ -36,6 +37,7 @@ class CredentialsCubit extends Cubit<CredentialsState> {
required this.profileCubit,
required this.oidc4vc,
required this.walletCubit,
required this.activityLogManager,
}) : super(const CredentialsState());

final CredentialsRepository credentialsRepository;
Expand All @@ -48,6 +50,7 @@ class CredentialsCubit extends Cubit<CredentialsState> {
final ProfileCubit profileCubit;
final OIDC4VC oidc4vc;
final WalletCubit walletCubit;
final ActivityLogManager activityLogManager;

final log = getLogger('CredentialsCubit');

Expand Down Expand Up @@ -196,13 +199,26 @@ class CredentialsCubit extends Cubit<CredentialsState> {
bool showMessage = true,
}) async {
emit(state.loading());

final credential =
state.credentials.where((element) => element.id == id).first;

await credentialsRepository.deleteById(id);
final credentials = List.of(state.credentials)
..removeWhere((element) => element.id == id);
final dummies = _getAvalaibleDummyCredentials(
credentials: credentials,
blockchainType: blockchainType,
);

await activityLogManager.writeLog(
LogData(
type: LogType.deleteVC,
timestamp: DateTime.now(),
credentialId: id,
data: credential.getName,
),
);
emit(
state.copyWith(
status: CredentialsStatus.delete,
Expand Down Expand Up @@ -325,6 +341,15 @@ class CredentialsCubit extends Cubit<CredentialsState> {
blockchainType: blockchainType,
);

await activityLogManager.writeLog(
LogData(
type: LogType.addVC,
timestamp: DateTime.now(),
credentialId: credential.id,
data: credential.getName,
),
);

emit(
state.copyWith(
status: showStatus ? CredentialsStatus.insert : CredentialsStatus.idle,
Expand Down
2 changes: 2 additions & 0 deletions lib/dashboard/drawer/activity_log/activity_log.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'cubit/activity_log_cubit.dart';
export 'view/activity_log_page.dart';
28 changes: 28 additions & 0 deletions lib/dashboard/drawer/activity_log/cubit/activity_log_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:altme/activity_log/activity_log.dart';
import 'package:altme/app/app.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';

part 'activity_log_cubit.g.dart';
part 'activity_log_state.dart';

class ActivityLogCubit extends Cubit<ActivityLogState> {
ActivityLogCubit({
required this.activityLogManager,
}) : super(ActivityLogState());

final ActivityLogManager activityLogManager;

final log = getLogger('ActivityLogCubit');

Future<void> getAllLogs() async {
final logs = await activityLogManager.readAllLogs();
emit(
state.copyWith(
logDatas: logs,
status: AppStatus.populate,
),
);
}
}
34 changes: 34 additions & 0 deletions lib/dashboard/drawer/activity_log/cubit/activity_log_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
part of 'activity_log_cubit.dart';

@JsonSerializable()
class ActivityLogState extends Equatable {
ActivityLogState({
this.status = AppStatus.init,
List<LogData>? logDatas,
}) : logDatas = logDatas ?? [];

factory ActivityLogState.fromJson(Map<String, dynamic> json) =>
_$ActivityLogStateFromJson(json);

final AppStatus status;
final List<LogData> logDatas;

ActivityLogState loading() {
return copyWith(status: AppStatus.loading);
}

ActivityLogState copyWith({
AppStatus? status,
List<LogData>? logDatas,
}) {
return ActivityLogState(
status: status ?? this.status,
logDatas: logDatas ?? this.logDatas,
);
}

Map<String, dynamic> toJson() => _$ActivityLogStateToJson(this);

@override
List<Object?> get props => [status, logDatas];
}
Loading

0 comments on commit a67e04c

Please sign in to comment.