1547 level indicator for all users (#1722)
* feat: publicly viewable target language and level indicator --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>pull/1593/head
parent
5347b4764f
commit
b98f2d3283
@ -0,0 +1,75 @@
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/language_list_util.dart';
|
||||
|
||||
class PublicProfileModel {
|
||||
LanguageModel? targetLanguage;
|
||||
Map<LanguageModel, LanguageAnalyticsProfileEntry>? languageAnalytics;
|
||||
|
||||
PublicProfileModel({this.targetLanguage, this.languageAnalytics});
|
||||
|
||||
factory PublicProfileModel.fromJson(Map<String, dynamic> json) {
|
||||
if (!json.containsKey(PangeaEventTypes.profileAnalytics)) {
|
||||
return PublicProfileModel();
|
||||
}
|
||||
|
||||
final profileJson = json[PangeaEventTypes.profileAnalytics];
|
||||
|
||||
final targetLanguage = profileJson[ModelKey.userTargetLanguage] != null
|
||||
? PangeaLanguage.byLangCode(profileJson[ModelKey.userTargetLanguage])
|
||||
: null;
|
||||
|
||||
final languageAnalytics = <LanguageModel, LanguageAnalyticsProfileEntry>{};
|
||||
if (profileJson[ModelKey.analytics] != null &&
|
||||
profileJson[ModelKey.analytics]!.isNotEmpty) {
|
||||
for (final entry in profileJson[ModelKey.analytics].entries) {
|
||||
final lang = PangeaLanguage.byLangCode(entry.key);
|
||||
if (lang == null) continue;
|
||||
final level = entry.value[ModelKey.level];
|
||||
languageAnalytics[lang] = LanguageAnalyticsProfileEntry(level);
|
||||
}
|
||||
}
|
||||
|
||||
final profile = PublicProfileModel(
|
||||
targetLanguage: targetLanguage,
|
||||
languageAnalytics: languageAnalytics,
|
||||
);
|
||||
return profile;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (targetLanguage != null) {
|
||||
json[ModelKey.userTargetLanguage] = targetLanguage!.langCode;
|
||||
}
|
||||
|
||||
final analytics = {};
|
||||
if (languageAnalytics != null && languageAnalytics!.isNotEmpty) {
|
||||
for (final entry in languageAnalytics!.entries) {
|
||||
analytics[entry.key.langCode] = {ModelKey.level: entry.value.level};
|
||||
}
|
||||
}
|
||||
|
||||
json[ModelKey.analytics] = analytics;
|
||||
return json;
|
||||
}
|
||||
|
||||
bool get isEmpty =>
|
||||
targetLanguage == null &&
|
||||
(languageAnalytics == null || languageAnalytics!.isEmpty);
|
||||
|
||||
void setLevel(LanguageModel language, int level) {
|
||||
languageAnalytics ??= {};
|
||||
languageAnalytics![language] ??= LanguageAnalyticsProfileEntry(0);
|
||||
languageAnalytics![language]!.level = level;
|
||||
}
|
||||
|
||||
int? get level => languageAnalytics?[targetLanguage]?.level;
|
||||
}
|
||||
|
||||
class LanguageAnalyticsProfileEntry {
|
||||
int level;
|
||||
|
||||
LanguageAnalyticsProfileEntry(this.level);
|
||||
}
|
||||
@ -0,0 +1,150 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/user/models/profile_model.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class PublicLevelIndicator extends StatelessWidget {
|
||||
final String userId;
|
||||
const PublicLevelIndicator({
|
||||
required this.userId,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final profileFuture =
|
||||
MatrixState.pangeaController.userController.getPublicProfile(userId);
|
||||
|
||||
return FutureBuilder<PublicProfileModel>(
|
||||
future: profileFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: LinearProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.hasError) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Theme.of(context).colorScheme.surfaceBright,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
size: 14,
|
||||
Icons.error_outline,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
weight: 1000,
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
L10n.of(context).oopsSomethingWentWrong,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.hasData &&
|
||||
snapshot.data!.targetLanguage == null &&
|
||||
snapshot.data!.level == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (snapshot.data?.targetLanguage != null)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Theme.of(context).colorScheme.surfaceBright,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
size: 14,
|
||||
Icons.language,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
weight: 1000,
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
snapshot.data!.targetLanguage!.displayName,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
if (snapshot.data?.level != null)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Theme.of(context).colorScheme.surfaceBright,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4,
|
||||
vertical: 2,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: AppConfig.gold,
|
||||
radius: 8,
|
||||
child: Icon(
|
||||
size: 12,
|
||||
Icons.star,
|
||||
color: Theme.of(context).colorScheme.surfaceBright,
|
||||
weight: 1000,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
L10n.of(context).levelShort(snapshot.data!.level!),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue