Merge branch 'main' of https://github.com/pangeachat/client into remove-duplicate-push
This commit is contained in:
commit
c7de9bb1bf
11 changed files with 464 additions and 30 deletions
|
|
@ -4937,6 +4937,8 @@
|
|||
"spaceChildPermission": "Who can add new chats and subspaces to this space",
|
||||
"addEnvironmentOverride": "Add environment override",
|
||||
"defaultOption": "Default",
|
||||
"deleteChatDesc": "Are you sure you want to delete this chat? It will be deleted for all participants and all messages within the chat will no longer be available for practice or learning analytics.",
|
||||
"deleteSpaceDesc": "The space and any selected chats and/or subspaces will be deleted for all participants and all messages within the chat will no longer be available for practice or learning analytics. This action cannot be undone.",
|
||||
"chatWithActivities": "Chat with activities",
|
||||
"findYourPeople": "Find your people",
|
||||
"launch": "Launch",
|
||||
|
|
|
|||
|
|
@ -32,10 +32,16 @@ class ReplyContent extends StatelessWidget {
|
|||
timeline != null ? replyEvent.getDisplayEvent(timeline) : replyEvent;
|
||||
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
|
||||
final color = theme.brightness == Brightness.dark
|
||||
? theme.colorScheme.onTertiaryContainer
|
||||
: ownMessage
|
||||
// Pangea#
|
||||
? ownMessage
|
||||
? theme.colorScheme.tertiaryContainer
|
||||
: theme.colorScheme.tertiary;
|
||||
: theme.colorScheme.onTertiaryContainer
|
||||
: theme.colorScheme.tertiary;
|
||||
// ? theme.colorScheme.onTertiaryContainer
|
||||
// : ownMessage
|
||||
// ? theme.colorScheme.tertiaryContainer
|
||||
// : theme.colorScheme.tertiary;
|
||||
// Pangea#
|
||||
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
|
|
@ -69,7 +75,9 @@ class ReplyContent extends StatelessWidget {
|
|||
fontWeight: FontWeight.bold,
|
||||
// #Pangea
|
||||
// color: color,
|
||||
color: theme.colorScheme.onSurface,
|
||||
color: ownMessage && theme.brightness == Brightness.dark
|
||||
? theme.colorScheme.tertiaryContainer
|
||||
: theme.colorScheme.onSurface,
|
||||
// Pangea#
|
||||
fontSize: fontSize,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import 'package:fluffychat/pages/chat_list/chat_list_view.dart';
|
|||
import 'package:fluffychat/pangea/chat_list/utils/app_version_util.dart';
|
||||
import 'package:fluffychat/pangea/chat_list/utils/chat_list_handle_space_tap.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/utils/delete_room.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/delete_space_dialog.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
|
|
@ -886,7 +888,10 @@ class ChatListController extends State<ChatList>
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.delete_outlined,
|
||||
// #Pangea
|
||||
// Icons.delete_outlined,
|
||||
Icons.logout_outlined,
|
||||
// Pangea#
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
|
|
@ -901,6 +906,28 @@ class ChatListController extends State<ChatList>
|
|||
],
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
if (room.isRoomAdmin)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.delete,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.delete_outlined,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
L10n.of(context).delete,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -1022,6 +1049,37 @@ class ChatListController extends State<ChatList>
|
|||
},
|
||||
);
|
||||
return;
|
||||
case ChatContextAction.delete:
|
||||
if (room.isSpace) {
|
||||
final resp = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (_) => DeleteSpaceDialog(space: room),
|
||||
);
|
||||
if (resp == true && mounted) {
|
||||
context.go("/rooms?spaceId=clear");
|
||||
}
|
||||
} else {
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).delete,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
isDestructive: true,
|
||||
message: room.isSpace
|
||||
? L10n.of(context).deleteSpaceDesc
|
||||
: L10n.of(context).deleteChatDesc,
|
||||
);
|
||||
if (confirmed != OkCancelResult.ok) return;
|
||||
if (!mounted) return;
|
||||
|
||||
final resp = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.delete,
|
||||
);
|
||||
if (resp.isError) return;
|
||||
if (mounted) context.go("/rooms?spaceId=clear");
|
||||
}
|
||||
return;
|
||||
// Pangea#
|
||||
case ChatContextAction.block:
|
||||
final userId =
|
||||
|
|
@ -1296,5 +1354,6 @@ enum ChatContextAction {
|
|||
block,
|
||||
// #Pangea
|
||||
removeFromSpace,
|
||||
delete,
|
||||
// Pangea#
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/pages/chat_details/chat_details.dart';
|
||||
import 'package:fluffychat/pages/chat_details/participant_list_item.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/utils/delete_room.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/utils/download_chat.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/utils/download_file.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/class_name_header.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/conversation_bot/conversation_bot_settings.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/delete_space_dialog.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/download_space_analytics_button.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/room_capacity_button.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/visibility_toggle.dart';
|
||||
|
|
@ -426,6 +428,58 @@ class PangeaChatDetailsView extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
Divider(color: theme.dividerColor, height: 1),
|
||||
if (room.isRoomAdmin)
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).delete,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: iconColor,
|
||||
child: Icon(
|
||||
Icons.delete_outline,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
onTap: () async {
|
||||
if (room.isSpace) {
|
||||
final resp = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
DeleteSpaceDialog(space: room),
|
||||
);
|
||||
|
||||
if (resp == true) {
|
||||
context.go("/rooms?spaceId=clear");
|
||||
}
|
||||
} else {
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).delete,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
isDestructive: true,
|
||||
message: room.isSpace
|
||||
? L10n.of(context).deleteSpaceDesc
|
||||
: L10n.of(context).deleteChatDesc,
|
||||
);
|
||||
if (confirmed != OkCancelResult.ok) return;
|
||||
|
||||
final resp = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.delete,
|
||||
);
|
||||
if (resp.isError) return;
|
||||
context.go("/rooms?spaceId=clear");
|
||||
}
|
||||
},
|
||||
),
|
||||
Divider(color: theme.dividerColor, height: 1),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).countParticipants(
|
||||
|
|
|
|||
59
lib/pangea/chat_settings/utils/delete_room.dart
Normal file
59
lib/pangea/chat_settings/utils/delete_room.dart
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/matrix_api_lite/generated/api.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
|
||||
extension on Api {
|
||||
// Send a POST request to /_synapse/client/pangea/v1/delete_room with JSON body {room_id: string}.
|
||||
// Response 200 OK format: { message: "Deleted" }.
|
||||
// Requester must be member of the room and have the highest power level of the room to perform this request.
|
||||
Future<void> delete(String roomId) async {
|
||||
final requestUri = Uri(
|
||||
path: '_synapse/client/pangea/v1/delete_room',
|
||||
);
|
||||
final request = Request('POST', baseUri!.resolveUri(requestUri));
|
||||
request.headers['content-type'] = 'application/json';
|
||||
request.headers['authorization'] = 'Bearer ${bearerToken!}';
|
||||
request.bodyBytes = utf8.encode(
|
||||
jsonEncode({
|
||||
'room_id': roomId,
|
||||
}),
|
||||
);
|
||||
final response = await httpClient.send(request);
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('http error response');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DeleteRoom on Room {
|
||||
Future<void> delete() async {
|
||||
await client.delete(id);
|
||||
}
|
||||
|
||||
Future<List<SpaceRoomsChunk>> getSpaceChildrenToDelete() async {
|
||||
final List<SpaceRoomsChunk> rooms = [];
|
||||
String? nextBatch;
|
||||
int calls = 0;
|
||||
|
||||
while ((nextBatch != null || calls == 0) && calls < 10) {
|
||||
final resp = await client.getSpaceHierarchy(
|
||||
id,
|
||||
from: nextBatch,
|
||||
limit: 100,
|
||||
);
|
||||
rooms.addAll(resp.rooms);
|
||||
nextBatch = resp.nextBatch;
|
||||
calls++;
|
||||
}
|
||||
|
||||
return rooms
|
||||
.where(
|
||||
(r) => r.roomType != PangeaRoomTypes.analytics && r.roomId != id,
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
260
lib/pangea/chat_settings/widgets/delete_space_dialog.dart
Normal file
260
lib/pangea/chat_settings/widgets/delete_space_dialog.dart
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/utils/delete_room.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
|
||||
class DeleteSpaceDialog extends StatefulWidget {
|
||||
final Room space;
|
||||
const DeleteSpaceDialog({
|
||||
super.key,
|
||||
required this.space,
|
||||
});
|
||||
|
||||
@override
|
||||
State<DeleteSpaceDialog> createState() => DeleteSpaceDialogState();
|
||||
}
|
||||
|
||||
class DeleteSpaceDialogState extends State<DeleteSpaceDialog> {
|
||||
List<SpaceRoomsChunk> _rooms = [];
|
||||
final List<SpaceRoomsChunk> _roomsToDelete = [];
|
||||
|
||||
bool _loadingRooms = true;
|
||||
String? _roomLoadError;
|
||||
|
||||
bool _deleting = false;
|
||||
String? _deleteError;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getSpaceChildrenToDelete();
|
||||
}
|
||||
|
||||
Future<void> _getSpaceChildrenToDelete() async {
|
||||
setState(() {
|
||||
_loadingRooms = true;
|
||||
_roomLoadError = null;
|
||||
});
|
||||
|
||||
try {
|
||||
_rooms = await widget.space.getSpaceChildrenToDelete();
|
||||
} catch (e, s) {
|
||||
_roomLoadError = L10n.of(context).oopsSomethingWentWrong;
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"roomID": widget.space.id,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
setState(() {
|
||||
_loadingRooms = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onRoomSelected(
|
||||
bool? selected,
|
||||
SpaceRoomsChunk room,
|
||||
) {
|
||||
if (selected == null ||
|
||||
(selected && _roomsToDelete.contains(room)) ||
|
||||
(!selected && !_roomsToDelete.contains(room))) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
selected ? _roomsToDelete.add(room) : _roomsToDelete.remove(room);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _deleteSpace() async {
|
||||
setState(() {
|
||||
_deleting = true;
|
||||
_deleteError = null;
|
||||
});
|
||||
|
||||
try {
|
||||
final List<Future<void>> deleteFutures = [];
|
||||
for (final room in _roomsToDelete) {
|
||||
final roomInstance = widget.space.client.getRoomById(room.roomId);
|
||||
if (roomInstance != null) {
|
||||
deleteFutures.add(roomInstance.delete());
|
||||
}
|
||||
}
|
||||
await Future.wait(deleteFutures);
|
||||
await widget.space.delete();
|
||||
Navigator.of(context).pop(true);
|
||||
} catch (e, s) {
|
||||
_deleteError = L10n.of(context).oopsSomethingWentWrong;
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"roomID": widget.space.id,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_deleting = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 400,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(32.0),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).areYouSure,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
child: Text(
|
||||
L10n.of(context).deleteSpaceDesc,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 300,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (_loadingRooms) {
|
||||
return const Center(
|
||||
child: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (_roomLoadError != null) {
|
||||
return Center(
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.error_outline,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
Text(L10n.of(context).oopsSomethingWentWrong),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: _rooms.length,
|
||||
itemBuilder: (context, index) {
|
||||
final chunk = _rooms[index];
|
||||
|
||||
final room =
|
||||
widget.space.client.getRoomById(chunk.roomId);
|
||||
final isMember = room != null &&
|
||||
room.membership == Membership.join &&
|
||||
room.isRoomAdmin;
|
||||
|
||||
final displayname = chunk.name ??
|
||||
chunk.canonicalAlias ??
|
||||
L10n.of(context).emptyChat;
|
||||
|
||||
return AnimatedOpacity(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
opacity: isMember ? 1 : 0.5,
|
||||
child: CheckboxListTile(
|
||||
value: _roomsToDelete.contains(chunk),
|
||||
onChanged: isMember
|
||||
? (value) => _onRoomSelected(value, chunk)
|
||||
: null,
|
||||
title: Text(displayname),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8.0, 16.0, 8.0, 8.0),
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
child: OutlinedButton(
|
||||
onPressed: _deleting ? null : _deleteSpace,
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.error,
|
||||
side: BorderSide(
|
||||
color: _deleting
|
||||
? Theme.of(context).disabledColor
|
||||
: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
child: _deleting
|
||||
? const SizedBox(
|
||||
height: 10,
|
||||
width: 100,
|
||||
child: LinearProgressIndicator(),
|
||||
)
|
||||
: Text(L10n.of(context).delete),
|
||||
),
|
||||
),
|
||||
OutlinedButton(
|
||||
onPressed: Navigator.of(context).pop,
|
||||
child: Text(L10n.of(context).cancel),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
child: _deleteError != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(L10n.of(context).oopsSomethingWentWrong),
|
||||
)
|
||||
: const SizedBox(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -15,25 +15,6 @@ enum ActivityTypeEnum {
|
|||
}
|
||||
|
||||
extension ActivityTypeExtension on ActivityTypeEnum {
|
||||
String get string {
|
||||
switch (this) {
|
||||
case ActivityTypeEnum.wordMeaning:
|
||||
return 'word_meaning';
|
||||
case ActivityTypeEnum.wordFocusListening:
|
||||
return 'word_focus_listening';
|
||||
case ActivityTypeEnum.hiddenWordListening:
|
||||
return 'hidden_word_listening';
|
||||
case ActivityTypeEnum.lemmaId:
|
||||
return 'lemma_id';
|
||||
case ActivityTypeEnum.emoji:
|
||||
return 'emoji';
|
||||
case ActivityTypeEnum.morphId:
|
||||
return 'morph_id';
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return 'message_meaning'; // TODO: Add to L10n
|
||||
}
|
||||
}
|
||||
|
||||
bool get hiddenType {
|
||||
switch (this) {
|
||||
case ActivityTypeEnum.wordMeaning:
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ class MessageActivityRequest {
|
|||
'message_tokens': messageTokens.map((e) => e.toJson()).toList(),
|
||||
'activity_quality_feedback': activityQualityFeedback?.toJson(),
|
||||
'target_tokens': targetTokens.map((e) => e.toJson()).toList(),
|
||||
'target_type': targetType.string,
|
||||
'target_type': targetType.name,
|
||||
'target_morph_feature': targetMorphFeature,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ class PracticeActivityModel {
|
|||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'lang_code': langCode,
|
||||
'activity_type': activityType.string,
|
||||
'activity_type': activityType.name,
|
||||
'content': multipleChoiceContent?.toJson(),
|
||||
'target_tokens': targetTokens.map((e) => e.toJson()).toList(),
|
||||
'match_content': matchContent?.toJson(),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import 'dart:developer';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
||||
|
|
@ -60,13 +62,22 @@ class PracticeTarget {
|
|||
userL2.hashCode;
|
||||
|
||||
static PracticeTarget fromJson(Map<String, dynamic> json) {
|
||||
final type = ActivityTypeEnum.values.firstWhereOrNull(
|
||||
(v) => json['activityType'] == v.name,
|
||||
);
|
||||
if (type == null) {
|
||||
throw Exception(
|
||||
"ActivityTypeEnum ${json['activityType']} not found in enum",
|
||||
);
|
||||
}
|
||||
|
||||
return PracticeTarget(
|
||||
tokens:
|
||||
(json['tokens'] as List).map((e) => PangeaToken.fromJson(e)).toList(),
|
||||
activityType: ActivityTypeEnum.values[json['activityType']],
|
||||
activityType: type,
|
||||
morphFeature: json['morphFeature'] == null
|
||||
? null
|
||||
: MorphFeaturesEnum.values[json['morphFeature']],
|
||||
: MorphFeaturesEnumExtension.fromString(json['morphFeature']),
|
||||
userL2: json['userL2'],
|
||||
);
|
||||
}
|
||||
|
|
@ -83,7 +94,7 @@ class PracticeTarget {
|
|||
//unique condensed deterministic key for local storage
|
||||
String get storageKey {
|
||||
return tokens.map((e) => e.text.content).join() +
|
||||
activityType.string +
|
||||
activityType.name +
|
||||
(morphFeature?.name ?? "");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ description: Learn a language while texting your friends.
|
|||
# Pangea#
|
||||
publish_to: none
|
||||
# On version bump also increase the build number for F-Droid
|
||||
version: 4.1.10+1
|
||||
version: 4.1.10+2
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue