Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • android/org.benoitharrault.scrobbles
1 result
Show changes
Showing
with 673 additions and 97 deletions
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:scrobbles/models/statistics_global.dart';
part 'data_statistics_global_state.dart';
class DataStatisticsGlobalCubit extends HydratedCubit<DataStatisticsGlobalState> {
DataStatisticsGlobalCubit() : super(const DataStatisticsGlobalState());
void getData(DataStatisticsGlobalState state) {
emit(state);
}
StatisticsGlobalData? getValue() {
return state.statisticsGlobal;
}
void update(StatisticsGlobalData? statisticsGlobal) {
if ((statisticsGlobal != null) &&
(state.statisticsGlobal.toString() != statisticsGlobal.toString())) {
setValue(statisticsGlobal);
}
}
void setValue(StatisticsGlobalData? statisticsGlobal) {
emit(DataStatisticsGlobalState(
statisticsGlobal: statisticsGlobal,
));
}
@override
DataStatisticsGlobalState? fromJson(Map<String, dynamic> json) {
return DataStatisticsGlobalState(
statisticsGlobal: StatisticsGlobalData.fromJson(json['statisticsGlobal']),
);
}
@override
Map<String, Object?>? toJson(DataStatisticsGlobalState state) {
return <String, Object?>{
'statisticsGlobal': state.statisticsGlobal?.toJson(),
};
}
}
part of 'data_statistics_global_cubit.dart';
@immutable
class DataStatisticsGlobalState extends Equatable {
const DataStatisticsGlobalState({
this.statisticsGlobal,
});
final StatisticsGlobalData? statisticsGlobal;
@override
List<Object?> get props => <Object?>[
statisticsGlobal,
];
}
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:scrobbles/models/statistics_recent.dart';
part 'data_statistics_recent_state.dart';
class DataStatisticsRecentCubit extends HydratedCubit<DataStatisticsRecentState> {
DataStatisticsRecentCubit() : super(const DataStatisticsRecentState());
void getData(DataStatisticsRecentState state) {
emit(state);
}
StatisticsRecentData? getValue() {
return state.statisticsRecent;
}
void update(StatisticsRecentData? statisticsRecent) {
if ((statisticsRecent != null) &&
(state.statisticsRecent.toString() != statisticsRecent.toString())) {
setValue(statisticsRecent);
}
}
void setValue(StatisticsRecentData? statisticsRecent) {
emit(DataStatisticsRecentState(
statisticsRecent: statisticsRecent,
));
}
@override
DataStatisticsRecentState? fromJson(Map<String, dynamic> json) {
return DataStatisticsRecentState(
statisticsRecent: StatisticsRecentData.fromJson(json['statisticsRecent']),
);
}
@override
Map<String, Object?>? toJson(DataStatisticsRecentState state) {
return <String, Object?>{
'statisticsRecent': state.statisticsRecent?.toJson(),
};
}
}
part of 'data_statistics_recent_cubit.dart';
@immutable
class DataStatisticsRecentState extends Equatable {
const DataStatisticsRecentState({
this.statisticsRecent,
});
final StatisticsRecentData? statisticsRecent;
@override
List<Object?> get props => <Object?>[
statisticsRecent,
];
}
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:scrobbles/models/timeline.dart';
part 'data_timeline_state.dart';
class DataTimelineCubit extends HydratedCubit<DataTimelineState> {
DataTimelineCubit() : super(const DataTimelineState());
void getData(DataTimelineState state) {
emit(state);
}
TimelineData? getValue() {
return state.timeline;
}
void update(TimelineData? timeline) {
if ((timeline != null) && (state.timeline.toString() != timeline.toString())) {
setValue(timeline);
}
}
void setValue(TimelineData? timeline) {
emit(DataTimelineState(
timeline: timeline,
));
}
@override
DataTimelineState? fromJson(Map<String, dynamic> json) {
return DataTimelineState(
timeline: TimelineData.fromJson(json['timeline']),
);
}
@override
Map<String, Object?>? toJson(DataTimelineState state) {
return <String, Object?>{
'timeline': state.timeline?.toJson(),
};
}
}
part of 'data_timeline_cubit.dart';
@immutable
class DataTimelineState extends Equatable {
const DataTimelineState({
this.timeline,
});
final TimelineData? timeline;
@override
List<Object?> get props => <Object?>[
timeline,
];
}
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:scrobbles/models/topartists.dart';
part 'data_top_artists_state.dart';
class DataTopArtistsCubit extends HydratedCubit<DataTopArtistsState> {
DataTopArtistsCubit() : super(const DataTopArtistsState());
void getData(DataTopArtistsState state) {
emit(state);
}
TopArtistsData? getValue(key) {
return state.topArtists;
}
void update(TopArtistsData? topArtists) {
if ((topArtists != null) && (state.topArtists.toString() != topArtists.toString())) {
setValue(topArtists);
}
}
void setValue(TopArtistsData? topArtists) {
emit(DataTopArtistsState(
topArtists: topArtists,
));
}
@override
DataTopArtistsState? fromJson(Map<String, dynamic> json) {
return DataTopArtistsState(
topArtists: TopArtistsData.fromJson(json['topArtists']),
);
}
@override
Map<String, Object?>? toJson(DataTopArtistsState state) {
return <String, Object?>{
'topArtists': state.topArtists?.toJson(),
};
}
}
part of 'data_top_artists_cubit.dart';
@immutable
class DataTopArtistsState extends Equatable {
const DataTopArtistsState({
this.topArtists,
});
final TopArtistsData? topArtists;
@override
List<Object?> get props => <Object?>[
topArtists,
];
}
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:scrobbles/config/default_settings.dart';
part 'settings_state.dart';
class SettingsCubit extends HydratedCubit<SettingsState> {
SettingsCubit() : super(const SettingsState());
String getUsername() {
return state.username ?? '';
}
String getSecurityToken() {
return state.securityToken ?? '';
}
int getCountsByDayDaysCount() {
return state.countsByDayDaysCount ?? DefaultSettings.defaultCountsByDayDaysCount;
}
int getCountsByHourDaysCount() {
return state.countsByHourDaysCount ?? DefaultSettings.defaultCountsByHourDaysCount;
}
int getDiscoveriesDaysCount() {
return state.discoveriesDaysCount ?? DefaultSettings.defaultDiscoveriesDaysCount;
}
int getStatisticsRecentDaysCount() {
return state.statisticsRecentDaysCount ?? DefaultSettings.defaultStatisticsRecentDaysCount;
}
int getTimelineDaysCount() {
return state.timelineDaysCount ?? DefaultSettings.defaultTimelineDaysCount;
}
int getTopArtistsDaysCount() {
return state.topArtistsDaysCount ?? DefaultSettings.defaultTopArtistsDaysCount;
}
void setValues({
String? username,
String? securityToken,
int? countsByDayDaysCount,
int? countsByHourDaysCount,
int? discoveriesDaysCount,
int? statisticsRecentDaysCount,
int? timelineDaysCount,
int? topArtistsDaysCount,
}) {
emit(SettingsState(
username: username ?? state.username,
securityToken: securityToken ?? state.securityToken,
countsByDayDaysCount: countsByDayDaysCount ?? state.countsByDayDaysCount,
countsByHourDaysCount: countsByHourDaysCount ?? state.countsByHourDaysCount,
discoveriesDaysCount: discoveriesDaysCount ?? state.discoveriesDaysCount,
statisticsRecentDaysCount: statisticsRecentDaysCount ?? state.statisticsRecentDaysCount,
timelineDaysCount: timelineDaysCount ?? state.timelineDaysCount,
topArtistsDaysCount: topArtistsDaysCount ?? state.topArtistsDaysCount,
));
}
@override
SettingsState? fromJson(Map<String, dynamic> json) {
String username = json['username'] as String;
String securityToken = json['securityToken'] as String;
int countsByDayDaysCount = json['countsByDayDaysCount'] as int;
int countsByHourDaysCount = json['countsByHourDaysCount'] as int;
int discoveriesDaysCount = json['discoveriesDaysCount'] as int;
int statisticsRecentDaysCount = json['statisticsRecentDaysCount'] as int;
int timelineDaysCount = json['timelineDaysCount'] as int;
int topArtistsDaysCount = json['topArtistsDaysCount'] as int;
return SettingsState(
username: username,
securityToken: securityToken,
countsByDayDaysCount: countsByDayDaysCount,
countsByHourDaysCount: countsByHourDaysCount,
discoveriesDaysCount: discoveriesDaysCount,
statisticsRecentDaysCount: statisticsRecentDaysCount,
timelineDaysCount: timelineDaysCount,
topArtistsDaysCount: topArtistsDaysCount,
);
}
@override
Map<String, dynamic>? toJson(SettingsState state) {
return <String, dynamic>{
'username': state.username ?? '',
'securityToken': state.securityToken ?? '',
'countsByDayDaysCount':
state.countsByDayDaysCount ?? DefaultSettings.defaultCountsByDayDaysCount,
'countsByHourDaysCount':
state.countsByHourDaysCount ?? DefaultSettings.defaultCountsByHourDaysCount,
'discoveriesDaysCount':
state.discoveriesDaysCount ?? DefaultSettings.defaultDiscoveriesDaysCount,
'statisticsRecentDaysCount':
state.statisticsRecentDaysCount ?? DefaultSettings.defaultStatisticsRecentDaysCount,
'timelineDaysCount': state.timelineDaysCount ?? DefaultSettings.defaultTimelineDaysCount,
'topArtistsDaysCount':
state.topArtistsDaysCount ?? DefaultSettings.defaultTopArtistsDaysCount,
};
}
}
part of 'settings_cubit.dart';
@immutable
class SettingsState extends Equatable {
const SettingsState({
this.username,
this.securityToken,
this.countsByDayDaysCount,
this.countsByHourDaysCount,
this.discoveriesDaysCount,
this.statisticsRecentDaysCount,
this.timelineDaysCount,
this.topArtistsDaysCount,
});
final String? username;
final String? securityToken;
final int? countsByDayDaysCount;
final int? countsByHourDaysCount;
final int? discoveriesDaysCount;
final int? statisticsRecentDaysCount;
final int? timelineDaysCount;
final int? topArtistsDaysCount;
@override
List<dynamic> get props => <dynamic>[
username,
securityToken,
countsByDayDaysCount,
countsByHourDaysCount,
discoveriesDaysCount,
statisticsRecentDaysCount,
timelineDaysCount,
topArtistsDaysCount,
];
Map<String, dynamic> get values => <String, dynamic>{
'username': username,
'securityToken': securityToken,
'countsByDayDaysCount': countsByDayDaysCount,
'countsByHourDaysCount': countsByHourDaysCount,
'discoveriesDaysCount': discoveriesDaysCount,
'statisticsRecentDaysCount': statisticsRecentDaysCount,
'timelineDaysCount': timelineDaysCount,
'topArtistsDaysCount': topArtistsDaysCount,
};
}
...@@ -2,13 +2,22 @@ import 'dart:io'; ...@@ -2,13 +2,22 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'config/theme.dart'; import 'package:scrobbles/config/theme.dart';
import 'package:scrobbles/cubit/bottom_nav_cubit.dart';
import 'ui/screens/skeleton_screen.dart'; import 'package:scrobbles/cubit/data_counts_by_day_cubit.dart';
import 'package:scrobbles/cubit/data_counts_by_hour_cubit.dart';
import 'package:scrobbles/cubit/data_discoveries_cubit.dart';
import 'package:scrobbles/cubit/data_statistics_global_cubit.dart';
import 'package:scrobbles/cubit/data_statistics_recent_cubit.dart';
import 'package:scrobbles/cubit/data_timeline_cubit.dart';
import 'package:scrobbles/cubit/data_top_artists_cubit.dart';
import 'package:scrobbles/cubit/settings_cubit.dart';
import 'package:scrobbles/ui/skeleton.dart';
void main() async { void main() async {
/// Initialize packages /// Initialize packages
...@@ -39,16 +48,31 @@ class MyApp extends StatelessWidget { ...@@ -39,16 +48,31 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MultiBlocProvider(
title: 'Scrobbles', providers: [
theme: appTheme, BlocProvider<SettingsCubit>(create: (context) => SettingsCubit()),
home: const SkeletonScreen(), BlocProvider<BottomNavCubit>(create: (context) => BottomNavCubit()),
BlocProvider<DataCountsByDayCubit>(create: (context) => DataCountsByDayCubit()),
BlocProvider<DataCountsByHourCubit>(create: (context) => DataCountsByHourCubit()),
BlocProvider<DataDiscoveriesCubit>(create: (context) => DataDiscoveriesCubit()),
BlocProvider<DataStatisticsGlobalCubit>(
create: (context) => DataStatisticsGlobalCubit()),
BlocProvider<DataStatisticsRecentCubit>(
create: (context) => DataStatisticsRecentCubit()),
BlocProvider<DataTimelineCubit>(create: (context) => DataTimelineCubit()),
BlocProvider<DataTopArtistsCubit>(create: (context) => DataTopArtistsCubit()),
],
child: MaterialApp(
title: 'Scrobbles',
theme: appTheme,
home: const SkeletonScreen(),
// Localization stuff // Localization stuff
localizationsDelegates: context.localizationDelegates, localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales, supportedLocales: context.supportedLocales,
locale: context.locale, locale: context.locale,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
),
); );
} }
} }
...@@ -7,11 +7,11 @@ class CountsByDayData { ...@@ -7,11 +7,11 @@ class CountsByDayData {
required this.data, required this.data,
}); });
factory CountsByDayData.fromJson(Map<String, dynamic> json) { factory CountsByDayData.fromJson(Map<String, dynamic>? json) {
Map<int, double> data = {}; Map<int, double> data = {};
if (json['counts-by-day'] != null) { if (json?['counts-by-day'] != null) {
json['counts-by-day'].keys.forEach((day) { json?['counts-by-day'].keys.forEach((day) {
data[int.parse(day)] = double.parse(json['counts-by-day'][day].toString()); data[int.parse(day)] = double.parse(json['counts-by-day'][day].toString());
}); });
} }
...@@ -19,11 +19,7 @@ class CountsByDayData { ...@@ -19,11 +19,7 @@ class CountsByDayData {
return CountsByDayData(data: data); return CountsByDayData(data: data);
} }
factory CountsByDayData.createEmpty() { Map<String, Object?>? toJson() {
return CountsByDayData.fromJson({});
}
String toString() {
Map<String, double> map = {}; Map<String, double> map = {};
this.data.keys.forEach((day) { this.data.keys.forEach((day) {
...@@ -31,6 +27,10 @@ class CountsByDayData { ...@@ -31,6 +27,10 @@ class CountsByDayData {
map[day.toString()] = value != null ? value.toDouble() : 0.0; map[day.toString()] = value != null ? value.toDouble() : 0.0;
}); });
return jsonEncode({'counts-by-day': map}); return {'counts-by-day': map};
}
String toString() {
return jsonEncode(this.toJson());
} }
} }
...@@ -7,11 +7,11 @@ class CountsByHourData { ...@@ -7,11 +7,11 @@ class CountsByHourData {
required this.data, required this.data,
}); });
factory CountsByHourData.fromJson(Map<String, dynamic> json) { factory CountsByHourData.fromJson(Map<String, dynamic>? json) {
Map<int, double> data = {}; Map<int, double> data = {};
if (json['counts-by-hour'] != null) { if (json?['counts-by-hour'] != null) {
json['counts-by-hour'].keys.forEach((day) { json?['counts-by-hour'].keys.forEach((day) {
if (int.parse(day) != 24) { if (int.parse(day) != 24) {
data[int.parse(day)] = double.parse(json['counts-by-hour'][day].toString()); data[int.parse(day)] = double.parse(json['counts-by-hour'][day].toString());
} }
...@@ -21,11 +21,7 @@ class CountsByHourData { ...@@ -21,11 +21,7 @@ class CountsByHourData {
return CountsByHourData(data: data); return CountsByHourData(data: data);
} }
factory CountsByHourData.createEmpty() { Map<String, Object?>? toJson() {
return CountsByHourData.fromJson({});
}
String toString() {
Map<String, double> map = {}; Map<String, double> map = {};
this.data.keys.forEach((day) { this.data.keys.forEach((day) {
...@@ -33,6 +29,10 @@ class CountsByHourData { ...@@ -33,6 +29,10 @@ class CountsByHourData {
map[day.toString()] = value != null ? value.toDouble() : 0.0; map[day.toString()] = value != null ? value.toDouble() : 0.0;
}); });
return jsonEncode({'counts-by-hour': map}); return {'counts-by-hour': map};
}
String toString() {
return jsonEncode(this.toJson());
} }
} }
...@@ -6,10 +6,10 @@ class DiscoveriesDataValue { ...@@ -6,10 +6,10 @@ class DiscoveriesDataValue {
const DiscoveriesDataValue({required this.newArtistsCount, required this.newTracksCount}); const DiscoveriesDataValue({required this.newArtistsCount, required this.newTracksCount});
factory DiscoveriesDataValue.fromJson(Map<String, dynamic> json) { factory DiscoveriesDataValue.fromJson(Map<String, dynamic>? json) {
return DiscoveriesDataValue( return DiscoveriesDataValue(
newArtistsCount: json['new-artists'] as int, newArtistsCount: json?['new-artists'] as int,
newTracksCount: json['new-tracks'] as int, newTracksCount: json?['new-tracks'] as int,
); );
} }
} }
...@@ -21,10 +21,10 @@ class DiscoveriesData { ...@@ -21,10 +21,10 @@ class DiscoveriesData {
required this.data, required this.data,
}); });
factory DiscoveriesData.fromJson(Map<String, dynamic> json) { factory DiscoveriesData.fromJson(Map<String, dynamic>? json) {
Map<String, DiscoveriesDataValue> data = {}; Map<String, DiscoveriesDataValue> data = {};
json.keys.forEach((date) { json?.keys.forEach((date) {
DiscoveriesDataValue value = DiscoveriesDataValue( DiscoveriesDataValue value = DiscoveriesDataValue(
newArtistsCount: json[date]['new-artists'] as int, newArtistsCount: json[date]['new-artists'] as int,
newTracksCount: json[date]['new-tracks'] as int, newTracksCount: json[date]['new-tracks'] as int,
...@@ -36,11 +36,7 @@ class DiscoveriesData { ...@@ -36,11 +36,7 @@ class DiscoveriesData {
return DiscoveriesData(data: data); return DiscoveriesData(data: data);
} }
factory DiscoveriesData.createEmpty() { Map<String, Object?>? toJson() {
return DiscoveriesData.fromJson({});
}
String toString() {
Map<String, Map<String, int>> map = {}; Map<String, Map<String, int>> map = {};
this.data.keys.forEach((element) { this.data.keys.forEach((element) {
...@@ -51,6 +47,10 @@ class DiscoveriesData { ...@@ -51,6 +47,10 @@ class DiscoveriesData {
}; };
}); });
return jsonEncode(map); return map;
}
String toString() {
return jsonEncode(this.toJson());
} }
} }
import 'dart:convert'; import 'dart:convert';
class StatisticsGlobalData { class StatisticsGlobalData {
final int totalCount; final int? totalCount;
final DateTime lastScrobble; final DateTime? lastScrobble;
const StatisticsGlobalData({ const StatisticsGlobalData({
required this.totalCount, required this.totalCount,
required this.lastScrobble, required this.lastScrobble,
}); });
factory StatisticsGlobalData.fromJson(Map<String, dynamic> json) { factory StatisticsGlobalData.fromJson(Map<String, dynamic>? json) {
return StatisticsGlobalData( return StatisticsGlobalData(
totalCount: json['totalCount'] != null ? json['totalCount'] as int : 0, totalCount: (json?['totalCount'] != null) ? (json?['totalCount'] as int) : null,
lastScrobble: (json['lastScrobble'] != null && json['lastScrobble']['date'] != null) lastScrobble: (json?['lastScrobble'] != null && json?['lastScrobble']['date'] != null)
? DateTime.parse( ? DateTime.parse(
json['lastScrobble']['date'], json?['lastScrobble']['date'],
) )
: DateTime.now(), : null,
); );
} }
factory StatisticsGlobalData.createEmpty() { Map<String, Object?>? toJson() {
return StatisticsGlobalData.fromJson({}); return <String, Object?>{
}
String toString() {
Map<String, dynamic> map = {
'totalCount': this.totalCount, 'totalCount': this.totalCount,
'lastScrobble': { 'lastScrobble': {
'date': this.lastScrobble.toString(), 'date': this.lastScrobble != null ? this.lastScrobble.toString() : null,
}, },
}; };
}
return jsonEncode(map); String toString() {
return jsonEncode(this.toJson());
} }
} }
import 'dart:convert'; import 'dart:convert';
class StatisticsRecentData { class StatisticsRecentData {
final int recentCount; final int? recentCount;
final int firstPlayedArtistsCount; final int? firstPlayedArtistsCount;
final int firstPlayedTracksCount; final int? firstPlayedTracksCount;
final int selectedPeriod; final int? selectedPeriod;
const StatisticsRecentData({ const StatisticsRecentData({
required this.recentCount, required this.recentCount,
...@@ -13,29 +13,30 @@ class StatisticsRecentData { ...@@ -13,29 +13,30 @@ class StatisticsRecentData {
required this.selectedPeriod, required this.selectedPeriod,
}); });
factory StatisticsRecentData.fromJson(Map<String, dynamic> json) { factory StatisticsRecentData.fromJson(Map<String, dynamic>? json) {
return StatisticsRecentData( return StatisticsRecentData(
recentCount: json['recentCount'] != null ? json['recentCount'] as int : 0, recentCount: (json?['recentCount'] != null) ? (json?['recentCount'] as int) : null,
firstPlayedArtistsCount: firstPlayedArtistsCount: (json?['firstPlayedArtistsCount'] != null)
json['firstPlayedArtistsCount'] != null ? json['firstPlayedArtistsCount'] as int : 0, ? (json?['firstPlayedArtistsCount'] as int)
firstPlayedTracksCount: : null,
json['firstPlayedTracksCount'] != null ? json['firstPlayedTracksCount'] as int : 0, firstPlayedTracksCount: (json?['firstPlayedTracksCount'] != null)
selectedPeriod: json['selectedPeriod'] != null ? json['selectedPeriod'] as int : 0, ? (json?['firstPlayedTracksCount'] as int)
: null,
selectedPeriod:
(json?['selectedPeriod'] != null) ? (json?['selectedPeriod'] as int) : null,
); );
} }
factory StatisticsRecentData.createEmpty() { Map<String, Object?>? toJson() {
return StatisticsRecentData.fromJson({}); return <String, Object?>{
}
String toString() {
Map<String, dynamic> map = {
'recentCount': this.recentCount, 'recentCount': this.recentCount,
'firstPlayedArtistsCount': this.firstPlayedArtistsCount, 'firstPlayedArtistsCount': this.firstPlayedArtistsCount,
'firstPlayedTracksCount': this.firstPlayedTracksCount, 'firstPlayedTracksCount': this.firstPlayedTracksCount,
'selectedPeriod': this.selectedPeriod, 'selectedPeriod': this.selectedPeriod,
}; };
}
return jsonEncode(map); String toString() {
return jsonEncode(this.toJson());
} }
} }
...@@ -6,10 +6,10 @@ class TimelineDataValue { ...@@ -6,10 +6,10 @@ class TimelineDataValue {
const TimelineDataValue({required this.counts, required this.eclecticism}); const TimelineDataValue({required this.counts, required this.eclecticism});
factory TimelineDataValue.fromJson(Map<String, dynamic> json) { factory TimelineDataValue.fromJson(Map<String, dynamic>? json) {
return TimelineDataValue( return TimelineDataValue(
counts: json['counts'] as int, counts: json?['counts'] as int,
eclecticism: json['eclecticism'] as int, eclecticism: json?['eclecticism'] as int,
); );
} }
} }
...@@ -21,10 +21,10 @@ class TimelineData { ...@@ -21,10 +21,10 @@ class TimelineData {
required this.data, required this.data,
}); });
factory TimelineData.fromJson(Map<String, dynamic> json) { factory TimelineData.fromJson(Map<String, dynamic>? json) {
Map<String, TimelineDataValue> data = {}; Map<String, TimelineDataValue> data = {};
json.keys.forEach((date) { json?.keys.forEach((date) {
TimelineDataValue value = TimelineDataValue( TimelineDataValue value = TimelineDataValue(
counts: json[date]['counts'] as int, counts: json[date]['counts'] as int,
eclecticism: json[date]['eclecticism'] as int, eclecticism: json[date]['eclecticism'] as int,
...@@ -36,11 +36,7 @@ class TimelineData { ...@@ -36,11 +36,7 @@ class TimelineData {
return TimelineData(data: data); return TimelineData(data: data);
} }
factory TimelineData.createEmpty() { Map<String, Object?>? toJson() {
return TimelineData.fromJson({});
}
String toString() {
Map<String, Map<String, int>> map = {}; Map<String, Map<String, int>> map = {};
this.data.keys.forEach((element) { this.data.keys.forEach((element) {
...@@ -51,6 +47,10 @@ class TimelineData { ...@@ -51,6 +47,10 @@ class TimelineData {
}; };
}); });
return jsonEncode(map); return map;
}
String toString() {
return jsonEncode(this.toJson());
} }
} }
import 'dart:convert';
class TopArtistsDataValue {
final String artistName;
final int count;
const TopArtistsDataValue({required this.artistName, required this.count});
factory TopArtistsDataValue.fromJson(Map<String, dynamic>? json) {
return TopArtistsDataValue(
artistName: json?['artistName'] as String,
count: json?['count'] as int,
);
}
}
class TopArtistsStreamDataValue {
final String artistName;
final double value;
const TopArtistsStreamDataValue({
required this.artistName,
required this.value,
});
factory TopArtistsStreamDataValue.fromJson(Map<String, dynamic>? json) {
return TopArtistsStreamDataValue(
artistName: json?['artistName'] as String,
value: json?['value'] as double,
);
}
Map<String, dynamic>? toJson() {
return {
artistName: value,
};
}
@override
String toString() {
return jsonEncode(this.toJson());
}
}
class TopArtistsData {
final List<TopArtistsDataValue> topArtists;
final Map<String, List<TopArtistsStreamDataValue>> topArtistsStream;
const TopArtistsData({
required this.topArtists,
required this.topArtistsStream,
});
factory TopArtistsData.fromJson(Map<String, dynamic>? json) {
List<TopArtistsDataValue> topArtists = [];
Map<String, List<TopArtistsStreamDataValue>> topArtistsStream = {};
json?['top-artists'].forEach((element) {
TopArtistsDataValue value = TopArtistsDataValue(
artistName: element['artistName'] as String,
count: element['count'] as int,
);
topArtists.add(value);
});
json?['top-artists-stream-by-date'].keys.forEach((date) {
if (json['top-artists-stream-by-date'][date] is Map<String, dynamic>) {
Map<String, dynamic> content = json['top-artists-stream-by-date'][date];
List<TopArtistsStreamDataValue> items = [];
content.forEach((String artistName, dynamic rawValue) {
double value = 0.0;
if (rawValue is double) {
value = rawValue;
} else if (rawValue is int) {
value = rawValue.toDouble();
}
TopArtistsStreamDataValue item = TopArtistsStreamDataValue(
artistName: artistName,
value: value,
);
items.add(item);
});
topArtistsStream[date] = items;
}
});
return TopArtistsData(
topArtists: topArtists,
topArtistsStream: topArtistsStream,
);
}
Map<String, Object?>? toJson() {
List<Map<String, Object>> listArtists = [];
Map<String, List<Map<String, double>>> artistsStreamMap = {};
this.topArtists.forEach((TopArtistsDataValue? item) {
listArtists.add({
'artistName': item != null ? item.artistName : '',
'count': item != null ? item.count : 0,
});
});
this.topArtistsStream.keys.forEach((dateAsString) {
List<TopArtistsStreamDataValue>? items = this.topArtistsStream[dateAsString];
List<Map<String, double>> values = [];
items?.forEach((item) {
values.add({
item.artistName: item.value,
});
});
artistsStreamMap[dateAsString] = values;
});
return {
'top-artists': listArtists,
'top-artists-stream-by-date': artistsStreamMap,
};
}
String toString() {
return jsonEncode(this.toJson());
}
}
import 'dart:convert'; import 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../models/counts_by_day.dart'; import 'package:scrobbles/models/counts_by_day.dart';
import '../models/counts_by_hour.dart'; import 'package:scrobbles/models/counts_by_hour.dart';
import '../models/discoveries.dart'; import 'package:scrobbles/models/discoveries.dart';
import '../models/statistics_global.dart'; import 'package:scrobbles/models/statistics_global.dart';
import '../models/statistics_recent.dart'; import 'package:scrobbles/models/statistics_recent.dart';
import '../models/timeline.dart'; import 'package:scrobbles/models/timeline.dart';
import 'package:scrobbles/models/topartists.dart';
class ScrobblesApi { class ScrobblesApi {
static String baseUrl = 'https://scrobble.harrault.fr'; static String baseUrl = 'https://scrobble.harrault.fr';
static Future<StatisticsGlobalData> fetchGlobalStatistics() async { static Future<StatisticsGlobalData> fetchGlobalStatistics() async {
final String url = baseUrl + '/stats'; final String url = baseUrl + '/stats';
print('fetching ' + url);
final response = await http.get(Uri.parse(url)); final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) { if (response.statusCode == 200) {
...@@ -25,7 +25,6 @@ class ScrobblesApi { ...@@ -25,7 +25,6 @@ class ScrobblesApi {
static Future<StatisticsRecentData> fetchRecentStatistics(int daysCount) async { static Future<StatisticsRecentData> fetchRecentStatistics(int daysCount) async {
final String url = baseUrl + '/' + daysCount.toString() + '/stats'; final String url = baseUrl + '/' + daysCount.toString() + '/stats';
print('fetching ' + url);
final response = await http.get(Uri.parse(url)); final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) { if (response.statusCode == 200) {
...@@ -37,7 +36,6 @@ class ScrobblesApi { ...@@ -37,7 +36,6 @@ class ScrobblesApi {
static Future<TimelineData> fetchTimeline(int daysCount) async { static Future<TimelineData> fetchTimeline(int daysCount) async {
final String url = baseUrl + '/data/' + daysCount.toString() + '/timeline'; final String url = baseUrl + '/data/' + daysCount.toString() + '/timeline';
print('fetching ' + url);
final response = await http.get(Uri.parse(url)); final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) { if (response.statusCode == 200) {
...@@ -49,7 +47,6 @@ class ScrobblesApi { ...@@ -49,7 +47,6 @@ class ScrobblesApi {
static Future<CountsByDayData> fetchCountsByDay(int daysCount) async { static Future<CountsByDayData> fetchCountsByDay(int daysCount) async {
final String url = baseUrl + '/data/' + daysCount.toString() + '/counts-by-day'; final String url = baseUrl + '/data/' + daysCount.toString() + '/counts-by-day';
print('fetching ' + url);
final response = await http.get(Uri.parse(url)); final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) { if (response.statusCode == 200) {
...@@ -61,7 +58,6 @@ class ScrobblesApi { ...@@ -61,7 +58,6 @@ class ScrobblesApi {
static Future<CountsByHourData> fetchCountsByHour(int daysCount) async { static Future<CountsByHourData> fetchCountsByHour(int daysCount) async {
final String url = baseUrl + '/data/' + daysCount.toString() + '/counts-by-hour'; final String url = baseUrl + '/data/' + daysCount.toString() + '/counts-by-hour';
print('fetching ' + url);
final response = await http.get(Uri.parse(url)); final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) { if (response.statusCode == 200) {
...@@ -73,7 +69,6 @@ class ScrobblesApi { ...@@ -73,7 +69,6 @@ class ScrobblesApi {
static Future<DiscoveriesData> fetchDiscoveries(int daysCount) async { static Future<DiscoveriesData> fetchDiscoveries(int daysCount) async {
final String url = baseUrl + '/data/' + daysCount.toString() + '/news'; final String url = baseUrl + '/data/' + daysCount.toString() + '/news';
print('fetching ' + url);
final response = await http.get(Uri.parse(url)); final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) { if (response.statusCode == 200) {
...@@ -82,4 +77,15 @@ class ScrobblesApi { ...@@ -82,4 +77,15 @@ class ScrobblesApi {
throw Exception('Failed to get data from API.'); throw Exception('Failed to get data from API.');
} }
} }
static Future<TopArtistsData> fetchTopArtists(int daysCount) async {
final String url = baseUrl + '/data/' + daysCount.toString() + '/top-artists';
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
return TopArtistsData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
throw Exception('Failed to get data from API.');
}
}
} }
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../widgets/main_screen/counts_by_day_card.dart'; import 'package:scrobbles/ui/widgets/cards/discoveries.dart';
import '../widgets/main_screen/counts_by_hour_card.dart';
class StatisticsScreen extends StatefulWidget { class ScreenDiscoveries extends StatelessWidget {
const StatisticsScreen({super.key}); const ScreenDiscoveries({super.key});
@override
State<StatisticsScreen> createState() => _StatisticsScreenState();
}
class _StatisticsScreenState extends State<StatisticsScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return Material(
...@@ -20,9 +14,7 @@ class _StatisticsScreenState extends State<StatisticsScreen> { ...@@ -20,9 +14,7 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
children: <Widget>[ children: <Widget>[
const SizedBox(height: 8), const SizedBox(height: 8),
const ChartCountsByDayCard(), const CardDiscoveries(),
const SizedBox(height: 6),
const ChartCountsByHourCard(),
const SizedBox(height: 36), const SizedBox(height: 36),
], ],
), ),
......