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
15 changes: 14 additions & 1 deletion lib/providers/ability_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ class AbilityProvider extends Notifier<List<PlacedAbility>> {
state = [...state, placedAbility];
}

void removeAbilityAsAction(String id) {
if (!state.any((ability) => ability.id == id)) return;

ref.read(actionProvider.notifier).addAction(
UserAction(
type: ActionType.deletion,
id: id,
group: ActionGroup.ability,
),
);
removeAbility(id);
}

void updatePosition(Offset position, String id) {
final newState = [...state];

Expand All @@ -70,7 +83,7 @@ class AbilityProvider extends Notifier<List<PlacedAbility>> {
Offset(position.dx + centerOffset.dx, position.dy + centerOffset.dy);

if (coordinateSystem.isOutOfBounds(centerPosition)) {
removeAbility(id);
removeAbilityAsAction(id);
return;
}

Expand Down
15 changes: 14 additions & 1 deletion lib/providers/agent_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ class AgentProvider extends Notifier<List<PlacedAgentNode>> {
state = newState;
}

void removeAgentAsAction(String id) {
if (!state.any((agent) => agent.id == id)) return;

ref.read(actionProvider.notifier).addAction(
UserAction(
type: ActionType.deletion,
id: id,
group: ActionGroup.agent,
),
);
removeAgent(id);
}

void toggleAgentState(String id) {
final newState = [...state];
final index = PlacedWidget.getIndexByID(id, newState);
Expand Down Expand Up @@ -82,7 +95,7 @@ class AgentProvider extends Notifier<List<PlacedAgentNode>> {
final coordinateSystem = CoordinateSystem.instance;

if (coordinateSystem.isOutOfBounds(centerPosition)) {
removeAgent(id);
removeAgentAsAction(id);
return;
}
if (index < 0) return;
Expand Down
13 changes: 13 additions & 0 deletions lib/providers/image_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,19 @@ class PlacedImageProvider extends Notifier<ImageState> {
state = state.copyWith(images: [...state.images, placedImage]);
}

void removeImageAsAction(String id) {
if (!state.images.any((image) => image.id == id)) return;

ref.read(actionProvider.notifier).addAction(
UserAction(
type: ActionType.deletion,
id: id,
group: ActionGroup.image,
),
);
removeImage(id);
}

void removeImage(String id) {
final newImages = [...state.images];
final index = PlacedWidget.getIndexByID(id, newImages);
Expand Down
96 changes: 91 additions & 5 deletions lib/providers/in_app_debug_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,35 @@ final inAppDebugProvider =
enum DebugLogLevel { info, warning, error }

class DebugLogEntry {
const DebugLogEntry({
DebugLogEntry({
required this.timestamp,
required this.level,
required this.message,
this.source,
this.errorText,
this.stackTrace,
});
String? dedupeKey,
this.repeatCount = 1,
DateTime? lastOccurredAt,
}) : dedupeKey = dedupeKey ??
_buildDebugLogDedupeKey(
level: level,
message: message,
source: source,
errorText: errorText,
stackTrace: stackTrace,
),
lastOccurredAt = lastOccurredAt ?? timestamp;

final DateTime timestamp;
final DebugLogLevel level;
final String message;
final String? source;
final String? errorText;
final String? stackTrace;
final String dedupeKey;
final int repeatCount;
final DateTime lastOccurredAt;

String get levelLabel => switch (level) {
DebugLogLevel.info => 'INFO',
Expand All @@ -35,10 +49,41 @@ class DebugLogEntry {
return '[${formatDebugLogTimestamp(timestamp)}] $levelLabel$sourceLabel';
}

bool canMergeWith(DebugLogEntry other) => dedupeKey == other.dedupeKey;

String get repeatLabel => 'Repeated ${repeatCount}x';

DebugLogEntry mergeRepeat(DebugLogEntry other) {
final currentErrorText = errorText?.trim();
final currentStackTrace = stackTrace?.trim();

return DebugLogEntry(
timestamp: timestamp,
level: level,
message: message,
source: source,
errorText: currentErrorText != null && currentErrorText.isNotEmpty
? errorText
: other.errorText,
stackTrace: currentStackTrace != null && currentStackTrace.isNotEmpty
? stackTrace
: other.stackTrace,
dedupeKey: dedupeKey,
repeatCount: repeatCount + other.repeatCount,
lastOccurredAt: other.lastOccurredAt,
);
}

String toClipboardText() {
final buffer = StringBuffer()
..writeln(headline)
..writeln(message);
final buffer = StringBuffer()..writeln(headline);

if (repeatCount > 1) {
buffer.writeln(
'$repeatLabel (latest: ${formatDebugLogTimestamp(lastOccurredAt)})',
);
}

buffer.writeln(message);

if (errorText != null && errorText!.trim().isNotEmpty) {
buffer
Expand Down Expand Up @@ -68,6 +113,41 @@ String formatDebugLogTimestamp(DateTime timestamp) {
'${twoDigits(timestamp.second)}';
}

String _buildDebugLogDedupeKey({
required DebugLogLevel level,
required String message,
String? source,
String? errorText,
String? stackTrace,
}) {
return [
level.name,
_normalizeDebugLogFragment(source),
_normalizeDebugLogFragment(message),
_normalizeDebugLogFragment(errorText),
_normalizeDebugLogFragment(_firstStackTraceLine(stackTrace)),
].join('|');
}

String _normalizeDebugLogFragment(String? value) {
if (value == null) return '';

return value.replaceAll(RegExp(r'\s+'), ' ').trim();
}

String? _firstStackTraceLine(String? stackTrace) {
if (stackTrace == null) return null;

for (final line in stackTrace.split('\n')) {
final trimmed = line.trim();
if (trimmed.isNotEmpty) {
return trimmed;
}
}

return null;
}

class InAppDebugProvider extends Notifier<List<DebugLogEntry>> {
static const int _maxEntries = 500;

Expand All @@ -77,6 +157,12 @@ class InAppDebugProvider extends Notifier<List<DebugLogEntry>> {
}

void addEntry(DebugLogEntry entry) {
if (state.isNotEmpty && state.last.canMergeWith(entry)) {
final mergedEntry = state.last.mergeRepeat(entry);
state = [...state.sublist(0, state.length - 1), mergedEntry];
return;
}

final nextState = [...state, entry];
if (nextState.length <= _maxEntries) {
state = nextState;
Expand Down
13 changes: 13 additions & 0 deletions lib/providers/text_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ class TextProvider extends Notifier<List<PlacedText>> {
state = [...state, text];
}

void removeTextAsAction(String id) {
if (!state.any((text) => text.id == id)) return;

ref.read(actionProvider.notifier).addAction(
UserAction(
type: ActionType.deletion,
id: id,
group: ActionGroup.text,
),
);
removeText(id);
}

void updatePosition(Offset position, String id) {
final newState = [...state];

Expand Down
13 changes: 13 additions & 0 deletions lib/providers/utility_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ class UtilityProvider extends Notifier<List<PlacedUtility>> {
state = [...state, utility];
}

void removeUtilityAsAction(String id) {
if (!state.any((utility) => utility.id == id)) return;

ref.read(actionProvider.notifier).addAction(
UserAction(
type: ActionType.deletion,
id: id,
group: ActionGroup.utility,
),
);
removeUtility(id);
}

void updatePosition(Offset position, String id) {
final newState = [...state];
final index = PlacedWidget.getIndexByID(id, newState);
Expand Down
Loading
Loading