Skip to content

Commit

Permalink
Merge pull request #13 from NiklasLehnfeld/feature/redesign
Browse files Browse the repository at this point in the history
Redesign of Article list entry
  • Loading branch information
NiklasLehnfeld authored Oct 13, 2021
2 parents 9fb3739 + b510bd8 commit ad7d39b
Show file tree
Hide file tree
Showing 25 changed files with 565 additions and 236 deletions.
10 changes: 10 additions & 0 deletions lib/extensions/article_ext.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import 'package:netzpolitik_mobile/models/article.dart';
import 'package:netzpolitik_mobile/extensions/string_ext.dart';

extension ArticleExteinsions on Article {

bool get hasMp3 => content?.containsMP3 ?? false;

String? get mp3Url => content?.mp3Url;

}
5 changes: 4 additions & 1 deletion lib/extensions/context_ext.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,17 @@ extension ContextExteinsions on BuildContext {

TextStyle get body1 => Theme.of(this).textTheme.bodyText1!;
TextStyle get body2 => Theme.of(this).textTheme.bodyText2!;
TextStyle get quote => Theme.of(this).textTheme.bodyText2!.copyWith(
fontStyle: FontStyle.italic
);

TextStyle get caption => Theme.of(this).textTheme.caption!;

Color get primaryColor => Theme.of(this).primaryColor;
Color get scaffoldColor => Theme.of(this).scaffoldBackgroundColor;
Color get iconButtonColor => Theme.of(this).primaryColor;
Color get dialogBackground => Theme.of(this).dialogBackgroundColor;
Color get audioPlayerBackground => Theme.of(this).bottomNavigationBarTheme.backgroundColor!;
Color get audioPlayerBackground => Colors.white70;

double get width => MediaQuery.of(this).size.width;
double get height => MediaQuery.of(this).size.height;
Expand Down
2 changes: 2 additions & 0 deletions lib/extensions/string_ext.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ extension MP3Checker on String {

bool get containsMP3 => RegExp(MP3_REGEX).hasMatch(this);

String get withoutMp3 => replaceAll(RegExp('<p>(.|\n)*<\/audio><\/p>\n'), '');

String? get mp3Url => RegExp(MP3_REGEX).firstMatch(this)?.group(1);

}
27 changes: 15 additions & 12 deletions lib/logic/audio_player.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import 'package:assets_audio_player/assets_audio_player.dart';
import 'package:flutter/cupertino.dart';
import 'package:netzpolitik_mobile/models/audio_model.dart';
import 'package:netzpolitik_mobile/models/article.dart';
import 'package:netzpolitik_mobile/extensions/article_ext.dart';

class AudioPlayer extends ChangeNotifier {

late AssetsAudioPlayer _assetsAudioPlayer;
AudioModel? _audio;
Article? currentPlayingArticle;

AudioPlayer() {
_assetsAudioPlayer = AssetsAudioPlayer.newPlayer();
Expand All @@ -14,7 +15,7 @@ class AudioPlayer extends ChangeNotifier {

bool get isPlaying => _assetsAudioPlayer.isPlaying.value;
bool get isBuffering => _assetsAudioPlayer.isBuffering.value;
bool get isOpened => _audio != null;
bool get isOpened => currentPlayingArticle != null;

Duration? get totalDuration {
if (_assetsAudioPlayer.realtimePlayingInfos.hasValue) {
Expand All @@ -32,28 +33,30 @@ class AudioPlayer extends ChangeNotifier {

set currentDuration(Duration? value) => _assetsAudioPlayer.seek(value ?? Duration());

String? get title => _audio?.title;
String? get title => currentPlayingArticle?.title;

bool isPlayingAudio(AudioModel audio) => isPlaying && _audio?.url == audio.url;
bool isBufferingAudio(AudioModel audio) => isBuffering && _audio?.url == audio.url;
bool isPlayingArticle(Article article) => isPlaying && currentPlayingArticle?.mp3Url == article.mp3Url;
bool isBufferingArticle(Article article) => isBuffering && currentPlayingArticle?.mp3Url == article.mp3Url;

void close() {
_audio = null;
currentPlayingArticle = null;
_assetsAudioPlayer.stop();
notifyListeners();
}

void openOrToggle(AudioModel audio) {
if (_audio != null && _audio?.url == audio.url) {
void openOrToggle(Article article) {
if (currentPlayingArticle != null && currentPlayingArticle?.mp3Url == article.mp3Url) {
toggle();
} else if (audio.url != null){
open(audio.url!);
_audio = audio;
} else if (article.mp3Url != null){
open(article.mp3Url!);
currentPlayingArticle = article;
}
}

void open(String url) => _assetsAudioPlayer.open(Audio.network(url));

void toggle() => _assetsAudioPlayer.playOrPause();



}
4 changes: 4 additions & 0 deletions lib/logic/rating_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,8 @@ class RatingManager {
}
}

void openStore() async {
await _inAppReview.openStoreListing();
}

}
1 change: 1 addition & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class WPApp extends StatelessWidget {
theme: ThemeData(
primarySwatch: Colors.blue,
hintColor: Colors.grey,
scaffoldBackgroundColor: Colors.grey[100],
textSelectionTheme: TextSelectionThemeData(
cursorColor: Colors.blue
),
Expand Down
8 changes: 0 additions & 8 deletions lib/models/audio_model.dart

This file was deleted.

2 changes: 2 additions & 0 deletions lib/models/category.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class Category {

Category();

Category.withId(this.id);

factory Category.fromJson(Map<String, dynamic> json) {
if (json['description'] != null) {
json['description'] = HtmlUnescape().convert(json['description']);
Expand Down
46 changes: 25 additions & 21 deletions lib/routes/article_detail_route.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import 'package:flutter/material.dart';
import 'package:netzpolitik_mobile/extensions/context_ext.dart';
import 'package:netzpolitik_mobile/extensions/int_ext.dart';
import 'package:netzpolitik_mobile/extensions/article_ext.dart';
import 'package:netzpolitik_mobile/extensions/string_ext.dart';
import 'package:netzpolitik_mobile/logic/rating_manager.dart';
import 'package:netzpolitik_mobile/models/article.dart';
import 'package:netzpolitik_mobile/models/audio_model.dart';
import 'package:netzpolitik_mobile/models/author.dart';
import 'package:netzpolitik_mobile/models/category.dart';
import 'package:netzpolitik_mobile/models/reply.dart';
import 'package:netzpolitik_mobile/widgets/custom_views/wp_article_appbar.dart';
import 'package:netzpolitik_mobile/widgets/custom_views/wp_audio_player.dart';
import 'package:netzpolitik_mobile/widgets/custom_views/wp_html.dart';
import 'package:netzpolitik_mobile/widgets/custom_views/wp_play_button.dart';
import 'package:netzpolitik_mobile/widgets/dashboard/articles/article_image.dart';
import 'package:provider/provider.dart';

Expand All @@ -19,16 +19,14 @@ class ArticleDetailRoute extends StatefulWidget {
final bool isBig;
final String identifier;

ArticleDetailRoute(this.article, {required this.isBig, required this.identifier});
ArticleDetailRoute(this.article,
{required this.isBig, required this.identifier});

@override
_ArticleDetailRouteState createState() => _ArticleDetailRouteState();
}

class _ArticleDetailRouteState extends State<ArticleDetailRoute> {

bool hasMp3 = false;

@override
void initState() {
var ratingManager = context.read<RatingManager>();
Expand All @@ -53,7 +51,6 @@ class _ArticleDetailRouteState extends State<ArticleDetailRoute> {
topArea = Column(
children: [_buildSummaryArea(context), _buildImage(context)]);
}

return Scaffold(
body: SingleChildScrollView(
child: Column(
Expand All @@ -77,7 +74,7 @@ class _ArticleDetailRouteState extends State<ArticleDetailRoute> {

Widget _buildContentArea(BuildContext context) => Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: WPHtml(widget.article.content ?? ''),
child: WPHtml((widget.article.content ?? '').withoutMp3),
);

Widget _buildSummaryArea(BuildContext context) => Padding(
Expand All @@ -97,11 +94,15 @@ class _ArticleDetailRouteState extends State<ArticleDetailRoute> {
);

List<Author> get authors => widget.article.authors ?? [];

List<Reply?> get replies => widget.article.replies ?? [];

List<Category> get categories => widget.article.categories ?? [];

String get authorNames => authors.map((a) => a.name).join(', ');

String get categoryName => categories.first.name ?? '';

int get numberOfReplies => replies.length;

Widget _buildSummary(BuildContext context) => Hero(
Expand All @@ -127,25 +128,28 @@ class _ArticleDetailRouteState extends State<ArticleDetailRoute> {
);

Widget _buildImage(BuildContext context) {
return Stack(
alignment: Alignment.bottomRight,
return Column(
children: [
Visibility(
visible: widget.article.hasMp3,
child: Column(
children: [
Divider(
height: 1,
),
WPAudioPlayer(
article: widget.article,
alwaysVisible: true,
showCross: false,
),
],
)),
ArticleImage(
widget.article,
identifier: widget.identifier,
captionVisible: true,
showAudioPlayer: false,
),
Visibility(
visible: widget.article.content?.containsMP3 ?? false,
child: Container(
margin: EdgeInsets.only(right: 10, bottom: 50),
child: WPPlayButton(
audio: AudioModel(
title: widget.article.title ?? '',
url: widget.article.content?.mp3Url ?? ''),
),
),
)
],
);
}
Expand Down
57 changes: 57 additions & 0 deletions lib/widgets/custom_views/bookmark_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:netzpolitik_mobile/extensions/context_ext.dart';
import 'package:netzpolitik_mobile/models/article.dart';
import 'package:netzpolitik_mobile/persistence/article_dao.dart';
import 'package:provider/provider.dart';

class BookmarkButtonWidget extends StatefulWidget {

final Article article;
final bool hasPadding;
final Color? color;

const BookmarkButtonWidget(this.article, {this.hasPadding = true, this.color});

@override
State<BookmarkButtonWidget> createState() => _BookmarkButtonWidgetState();
}

class _BookmarkButtonWidgetState extends State<BookmarkButtonWidget> {

bool _bookmarked = false;

@override
Widget build(BuildContext context) {
final dao = context.watch<ArticleDAO>();

return FutureBuilder<bool>(
future: dao.isStored(widget.article),
builder: (context, snapshot) {

if (snapshot.hasData && snapshot.data != null) {
_bookmarked = snapshot.data!;
}

return IconButton(
padding: widget.hasPadding ? EdgeInsets.all(8.0) : EdgeInsets.zero,
constraints: widget.hasPadding ? null : BoxConstraints(),
icon: FaIcon(
_bookmarked ? FontAwesomeIcons.solidBookmark : FontAwesomeIcons.bookmark,
color: widget.color ?? context.iconButtonColor,
),
onPressed: () async {
if (_bookmarked) {
await dao.delete(widget.article.id!);
} else {
await dao.insert(widget.article);
}
setState(() {
_bookmarked = !_bookmarked;
});
},
);
}
);
}
}
38 changes: 38 additions & 0 deletions lib/widgets/custom_views/share_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:netzpolitik_mobile/extensions/context_ext.dart';
import 'package:netzpolitik_mobile/models/article.dart';
import 'package:share/share.dart';

class ShareButtonWidget extends StatelessWidget {

final Article article;
final bool hasPadding;
final Color? color;

const ShareButtonWidget(this.article, {this.hasPadding = true, this.color});

@override
Widget build(BuildContext context) {
var url = article.link;

if (url == null) {
return Container();
}

return IconButton(
padding: hasPadding ? EdgeInsets.all(8.0) : EdgeInsets.zero,
constraints: hasPadding ? null : BoxConstraints(),
icon: FaIcon(
FontAwesomeIcons.shareAlt,
color: color ?? context.iconButtonColor,
),
onPressed: () async {
await Share.share(url);
},
);
}



}
Loading

0 comments on commit ad7d39b

Please sign in to comment.