Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
40 changes: 40 additions & 0 deletions assets/locales/en.po
Original file line number Diff line number Diff line change
Expand Up @@ -2054,3 +2054,43 @@ msgstr "Tap Edit, then Add Widget."

msgid "widget_step_3"
msgstr "Search for Lantern, choose a size, and tap add widget."

# Advanced stats: low-emphasis link at the bottom of the Unbounded status
# card and the title of the bottom sheet it opens. "Data relayed" counts
# bytes this device forwarded for the people it helps over Unbounded.
msgid "advanced_stats"
msgstr "Advanced stats"

msgid "advanced_stats_current_speed"
msgstr "Current speed"

msgid "advanced_stats_session"
msgstr "Data relayed this session"

msgid "advanced_stats_all_time"
msgstr "Data relayed all time"

# Legend for the ↓ / ↑ arrows next to the figures above.
msgid "advanced_stats_direction_legend"
msgstr "↓ sent to the people you help · ↑ received from them"

msgid "advanced_stats_countries"
msgstr "Countries you've helped"

msgid "advanced_stats_no_countries"
msgstr "No one helped yet. Countries will appear here once people connect through you."

# Country row for peers whose location could not be determined.
msgid "advanced_stats_unknown_country"
msgstr "Unknown"

msgid "advanced_stats_one_person"
msgstr "1 person"

# %s is a number greater than one.
msgid "advanced_stats_people"
msgstr "%s people"

# Shown instead of data figures while sharing via Share My Connection.
msgid "advanced_stats_smc_note"
msgstr "Data stats are available only when sharing via Unbounded."
17 changes: 17 additions & 0 deletions lib/core/extensions/int.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
extension ByteConversion on int {
/// Converts bytes to megabytes (MB)
double get toMB => this / 1e6;

/// Human-readable size in SI (powers of 1000) units, e.g. "1.2 GB".
/// One decimal below 10 units, none above.
String get siBytes {
const units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
if (this < 1000) return '${this < 0 ? 0 : this} B';
var value = toDouble();
var unit = 0;
while (value >= 999.5 && unit < units.length - 1) {
value /= 1000;
unit++;
}
final text = value < 9.95
? value.toStringAsFixed(1)
: value.toStringAsFixed(0);
return '$text ${units[unit]}';
}
}
50 changes: 50 additions & 0 deletions lib/core/models/app_setting.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:lantern/core/models/relay_stats.dart';

class AppSetting {
final String locale;
final String themeMode;
Expand All @@ -22,6 +24,17 @@ class AppSetting {
// Figma. ShareNotifier seeds totalCount from this on build, and
// writes back each time the count increments.
final int unboundedTotalHelped;
// Lifetime Unbounded bytes relayed (down: to the people helped, up: from
// them) and the per-country breakdown behind Advanced stats. Written by
// ShareNotifier on a throttle, not on every snapshot.
final int unboundedBytesDown;
final int unboundedBytesUp;
final Map<String, RelayCountryStats> unboundedCountryStats;
// The backend epoch and byte counters already folded into the totals
// above, so a restarted app resumes counting where it left off.
final String unboundedRelayEpoch;
final int unboundedRelayMarkDown;
final int unboundedRelayMarkUp;

const AppSetting({
this.themeMode = 'system',
Expand All @@ -37,6 +50,12 @@ class AppSetting {
this.unboundedHidden = false,
this.unboundedWelcomeSeen = false,
this.unboundedTotalHelped = 0,
this.unboundedBytesDown = 0,
this.unboundedBytesUp = 0,
this.unboundedCountryStats = const {},
this.unboundedRelayEpoch = '',
this.unboundedRelayMarkDown = 0,
this.unboundedRelayMarkUp = 0,
});

/// Whether the app points at the staging backend. The notifier writes
Expand All @@ -57,6 +76,12 @@ class AppSetting {
bool? unboundedHidden,
bool? unboundedWelcomeSeen,
int? unboundedTotalHelped,
int? unboundedBytesDown,
int? unboundedBytesUp,
Map<String, RelayCountryStats>? unboundedCountryStats,
String? unboundedRelayEpoch,
int? unboundedRelayMarkDown,
int? unboundedRelayMarkUp,
}) {
return AppSetting(
locale: newLocale ?? locale,
Expand All @@ -73,6 +98,14 @@ class AppSetting {
unboundedWelcomeSeen: unboundedWelcomeSeen ?? this.unboundedWelcomeSeen,
unboundedTotalHelped:
unboundedTotalHelped ?? this.unboundedTotalHelped,
unboundedBytesDown: unboundedBytesDown ?? this.unboundedBytesDown,
unboundedBytesUp: unboundedBytesUp ?? this.unboundedBytesUp,
unboundedCountryStats:
unboundedCountryStats ?? this.unboundedCountryStats,
unboundedRelayEpoch: unboundedRelayEpoch ?? this.unboundedRelayEpoch,
unboundedRelayMarkDown:
unboundedRelayMarkDown ?? this.unboundedRelayMarkDown,
unboundedRelayMarkUp: unboundedRelayMarkUp ?? this.unboundedRelayMarkUp,
);
}

Expand All @@ -90,6 +123,13 @@ class AppSetting {
'unboundedHidden': unboundedHidden,
'unboundedWelcomeSeen': unboundedWelcomeSeen,
'unboundedTotalHelped': unboundedTotalHelped,
'unboundedBytesDown': unboundedBytesDown,
'unboundedBytesUp': unboundedBytesUp,
'unboundedCountryStats':
RelayCountryStats.mapToJson(unboundedCountryStats),
'unboundedRelayEpoch': unboundedRelayEpoch,
'unboundedRelayMarkDown': unboundedRelayMarkDown,
'unboundedRelayMarkUp': unboundedRelayMarkUp,
};

factory AppSetting.fromJson(Map<String, dynamic> json) => AppSetting(
Expand All @@ -110,5 +150,15 @@ class AppSetting {
unboundedWelcomeSeen: json['unboundedWelcomeSeen'] == true,
unboundedTotalHelped:
(json['unboundedTotalHelped'] as num?)?.toInt() ?? 0,
unboundedBytesDown:
(json['unboundedBytesDown'] as num?)?.toInt() ?? 0,
unboundedBytesUp: (json['unboundedBytesUp'] as num?)?.toInt() ?? 0,
unboundedCountryStats:
RelayCountryStats.mapFromJson(json['unboundedCountryStats']),
unboundedRelayEpoch: (json['unboundedRelayEpoch'] ?? '').toString(),
unboundedRelayMarkDown:
(json['unboundedRelayMarkDown'] as num?)?.toInt() ?? 0,
unboundedRelayMarkUp:
(json['unboundedRelayMarkUp'] as num?)?.toInt() ?? 0,
);
}
Loading
Loading