mirror of
https://github.com/Livinglist/Hacki.git
synced 2025-08-06 18:24:42 +08:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
29e2f4163d | |||
c3de80015d | |||
436cd9ce8b | |||
efb326be68 | |||
047903fe24 |
@ -83,7 +83,9 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
final int pageSize = _getPageSize(isComplexTile: isComplexTile);
|
||||
emit(
|
||||
const StoriesState.init().copyWith(
|
||||
offlineReading: hasCachedStories,
|
||||
offlineReading: hasCachedStories &&
|
||||
// Only go into offline mode in the next session.
|
||||
state.downloadStatus == StoriesDownloadStatus.initial,
|
||||
currentPageSize: pageSize,
|
||||
downloadStatus: state.downloadStatus,
|
||||
storiesDownloaded: state.storiesDownloaded,
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:hacki/extensions/extensions.dart';
|
||||
|
||||
abstract class Constants {
|
||||
static const String endUserAgreementLink =
|
||||
'https://www.termsfeed.com/live/c1417f5c-a48b-4bd7-93b2-9cd4577bfc45';
|
||||
@ -34,16 +36,16 @@ abstract class Constants {
|
||||
static const String featureLogIn = 'log_in';
|
||||
static const String featurePinToTop = 'pin_to_top';
|
||||
|
||||
static const List<String> happyFaces = <String>[
|
||||
static final String happyFace = <String>[
|
||||
'(๑•̀ㅂ•́)و✧',
|
||||
'( ͡• ͜ʖ ͡•)',
|
||||
'( ͡~ ͜ʖ ͡°)',
|
||||
'٩(˘◡˘)۶',
|
||||
'(─‿‿─)',
|
||||
'(¬‿¬)',
|
||||
];
|
||||
].pickRandomly()!;
|
||||
|
||||
static const List<String> sadFaces = <String>[
|
||||
static final String sadFace = <String>[
|
||||
'ಥ_ಥ',
|
||||
'(╯°□°)╯︵ ┻━┻',
|
||||
r'¯\_(ツ)_/¯',
|
||||
@ -53,7 +55,7 @@ abstract class Constants {
|
||||
'(ㆆ_ㆆ)',
|
||||
'ʕ•́ᴥ•̀ʔっ',
|
||||
'(ㆆ_ㆆ)',
|
||||
];
|
||||
].pickRandomly()!;
|
||||
}
|
||||
|
||||
abstract class RegExpConstants {
|
||||
|
@ -56,13 +56,6 @@ class PreferenceCubit extends Cubit<PreferenceState> {
|
||||
}
|
||||
}
|
||||
|
||||
void toggle(BooleanPreference preference) {
|
||||
final BooleanPreference updatedPreference =
|
||||
preference.copyWith(val: !preference.val) as BooleanPreference;
|
||||
emit(state.copyWithPreference(updatedPreference));
|
||||
_preferenceRepository.setBool(preference.key, !preference.val);
|
||||
}
|
||||
|
||||
void update<T>(Preference<T> preference, {required T to}) {
|
||||
final T value = to;
|
||||
final Preference<T> updatedPreference = preference.copyWith(val: value);
|
||||
|
@ -20,23 +20,7 @@ class Comment extends Item {
|
||||
type: '',
|
||||
);
|
||||
|
||||
Comment.fromJson(Map<String, dynamic> json, {this.level = 0})
|
||||
: super(
|
||||
id: json['id'] as int? ?? 0,
|
||||
time: json['time'] as int? ?? 0,
|
||||
by: json['by'] as String? ?? '',
|
||||
text: json['text'] as String? ?? '',
|
||||
kids: (json['kids'] as List<dynamic>?)?.cast<int>() ?? <int>[],
|
||||
parent: json['parent'] as int? ?? 0,
|
||||
deleted: json['deleted'] as bool? ?? false,
|
||||
score: json['score'] as int? ?? 0,
|
||||
descendants: 0,
|
||||
dead: json['dead'] as bool? ?? false,
|
||||
parts: <int>[],
|
||||
title: '',
|
||||
url: '',
|
||||
type: '',
|
||||
);
|
||||
Comment.fromJson(super.json, {this.level = 0}) : super.fromJson();
|
||||
|
||||
final int level;
|
||||
|
||||
|
@ -44,11 +44,11 @@ class Item extends Equatable {
|
||||
title = json['title'] as String? ?? '',
|
||||
text = json['text'] as String? ?? '',
|
||||
url = json['url'] as String? ?? '',
|
||||
kids = <int>[],
|
||||
kids = (json['kids'] as List<dynamic>?)?.cast<int>() ?? <int>[],
|
||||
dead = json['dead'] as bool? ?? false,
|
||||
deleted = json['deleted'] as bool? ?? false,
|
||||
parent = json['parent'] as int? ?? 0,
|
||||
parts = <int>[],
|
||||
parts = (json['parts'] as List<dynamic>?)?.cast<int>() ?? <int>[],
|
||||
type = json['type'] as String? ?? '';
|
||||
|
||||
final int id;
|
||||
|
@ -41,24 +41,9 @@ class PollOption extends Item {
|
||||
type: '',
|
||||
);
|
||||
|
||||
PollOption.fromJson(Map<String, dynamic> json)
|
||||
PollOption.fromJson(super.json)
|
||||
: ratio = 0,
|
||||
super(
|
||||
descendants: 0,
|
||||
id: json['id'] as int? ?? 0,
|
||||
score: json['score'] as int? ?? 0,
|
||||
time: json['time'] as int? ?? 0,
|
||||
by: json['by'] as String? ?? '',
|
||||
title: json['title'] as String? ?? '',
|
||||
url: json['url'] as String? ?? '',
|
||||
kids: <int>[],
|
||||
text: json['text'] as String? ?? '',
|
||||
dead: json['dead'] as bool? ?? false,
|
||||
deleted: json['deleted'] as bool? ?? false,
|
||||
type: json['type'] as String? ?? '',
|
||||
parts: <int>[],
|
||||
parent: 0,
|
||||
);
|
||||
super.fromJson();
|
||||
|
||||
final double ratio;
|
||||
|
||||
|
@ -91,23 +91,7 @@ class Story extends Item {
|
||||
type: '',
|
||||
);
|
||||
|
||||
Story.fromJson(Map<String, dynamic> json)
|
||||
: super(
|
||||
descendants: json['descendants'] as int? ?? 0,
|
||||
id: json['id'] as int? ?? 0,
|
||||
score: json['score'] as int? ?? 0,
|
||||
time: json['time'] as int? ?? 0,
|
||||
by: json['by'] as String? ?? '',
|
||||
title: json['title'] as String? ?? '',
|
||||
url: json['url'] as String? ?? '',
|
||||
kids: (json['kids'] as List<dynamic>?)?.cast<int>() ?? <int>[],
|
||||
text: json['text'] as String? ?? '',
|
||||
dead: json['dead'] as bool? ?? false,
|
||||
deleted: json['deleted'] as bool? ?? false,
|
||||
type: json['type'] as String? ?? '',
|
||||
parts: (json['parts'] as List<dynamic>?)?.cast<int>() ?? <int>[],
|
||||
parent: 0,
|
||||
);
|
||||
Story.fromJson(super.json) : super.fromJson();
|
||||
|
||||
String get metadata =>
|
||||
'''$score point${score > 1 ? 's' : ''} by $by $postedDate | $descendants comment${descendants > 1 ? 's' : ''}''';
|
||||
|
@ -5,11 +5,8 @@ import 'dart:io';
|
||||
import 'package:feature_discovery/feature_discovery.dart';
|
||||
import 'package:flutter/material.dart' hide Badge;
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_fadein/flutter_fadein.dart';
|
||||
import 'package:flutter_siri_suggestions/flutter_siri_suggestions.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:hacki/blocs/blocs.dart';
|
||||
import 'package:hacki/config/constants.dart';
|
||||
import 'package:hacki/config/locator.dart';
|
||||
@ -18,6 +15,7 @@ import 'package:hacki/extensions/extensions.dart';
|
||||
import 'package:hacki/main.dart';
|
||||
import 'package:hacki/models/models.dart';
|
||||
import 'package:hacki/repositories/repositories.dart';
|
||||
import 'package:hacki/screens/home/widgets/widgets.dart';
|
||||
import 'package:hacki/screens/screens.dart';
|
||||
import 'package:hacki/screens/widgets/widgets.dart';
|
||||
import 'package:hacki/services/services.dart';
|
||||
@ -145,57 +143,6 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
previous.metadataEnabled != current.metadataEnabled ||
|
||||
previous.swipeGestureEnabled != current.swipeGestureEnabled,
|
||||
builder: (BuildContext context, PreferenceState preferenceState) {
|
||||
final BlocBuilder<PinCubit, PinState> pinnedStories =
|
||||
BlocBuilder<PinCubit, PinState>(
|
||||
builder: (BuildContext context, PinState state) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
for (final Story story in state.pinnedStories)
|
||||
FadeIn(
|
||||
child: Slidable(
|
||||
startActionPane: ActionPane(
|
||||
motion: const BehindMotion(),
|
||||
children: <Widget>[
|
||||
SlidableAction(
|
||||
onPressed: (_) {
|
||||
HapticFeedback.lightImpact();
|
||||
context.read<PinCubit>().unpinStory(story);
|
||||
},
|
||||
backgroundColor: Palette.red,
|
||||
foregroundColor: Palette.white,
|
||||
icon: preferenceState.complexStoryTileEnabled
|
||||
? Icons.close
|
||||
: null,
|
||||
label: 'Unpin',
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ColoredBox(
|
||||
color: Palette.orangeAccent.withOpacity(0.2),
|
||||
child: StoryTile(
|
||||
key: ValueKey<String>('${story.id}-PinnedStoryTile'),
|
||||
story: story,
|
||||
onTap: () => onStoryTapped(story, isPin: true),
|
||||
showWebPreview:
|
||||
preferenceState.complexStoryTileEnabled,
|
||||
showMetadata: preferenceState.metadataEnabled,
|
||||
showUrl: preferenceState.urlEnabled,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (state.pinnedStories.isNotEmpty)
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: Dimens.pt12),
|
||||
child: Divider(
|
||||
color: Palette.orangeAccent,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return DefaultTabController(
|
||||
length: tabLength,
|
||||
child: Scaffold(
|
||||
@ -235,7 +182,10 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
StoriesListView(
|
||||
key: ValueKey<StoryType>(type),
|
||||
storyType: type,
|
||||
header: pinnedStories,
|
||||
header: PinnedStories(
|
||||
preferenceState: preferenceState,
|
||||
onStoryTapped: onStoryTapped,
|
||||
),
|
||||
onStoryTapped: onStoryTapped,
|
||||
),
|
||||
const ProfileScreen(),
|
||||
@ -251,11 +201,11 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
return ScreenTypeLayout.builder(
|
||||
mobile: (BuildContext context) {
|
||||
context.read<SplitViewCubit>().disableSplitView();
|
||||
return _MobileHomeScreen(
|
||||
return MobileHomeScreen(
|
||||
homeScreen: homeScreen,
|
||||
);
|
||||
},
|
||||
tablet: (BuildContext context) => _TabletHomeScreen(
|
||||
tablet: (BuildContext context) => TabletHomeScreen(
|
||||
homeScreen: homeScreen,
|
||||
),
|
||||
);
|
||||
@ -385,112 +335,3 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _MobileHomeScreen extends StatelessWidget {
|
||||
const _MobileHomeScreen({
|
||||
required this.homeScreen,
|
||||
});
|
||||
|
||||
final Widget homeScreen;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
Positioned.fill(child: homeScreen),
|
||||
if (!context.read<ReminderCubit>().state.hasShown)
|
||||
const Positioned(
|
||||
left: Dimens.pt24,
|
||||
right: Dimens.pt24,
|
||||
bottom: Dimens.pt36,
|
||||
height: Dimens.pt40,
|
||||
child: CountdownReminder(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TabletHomeScreen extends StatelessWidget {
|
||||
const _TabletHomeScreen({
|
||||
required this.homeScreen,
|
||||
});
|
||||
|
||||
final Widget homeScreen;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(
|
||||
builder: (BuildContext context, SizingInformation sizeInfo) {
|
||||
context.read<SplitViewCubit>().enableSplitView();
|
||||
double homeScreenWidth = 428;
|
||||
|
||||
if (sizeInfo.screenSize.width < homeScreenWidth * 2) {
|
||||
homeScreenWidth = 345;
|
||||
}
|
||||
|
||||
return BlocBuilder<SplitViewCubit, SplitViewState>(
|
||||
buildWhen: (SplitViewState previous, SplitViewState current) =>
|
||||
previous.expanded != current.expanded,
|
||||
builder: (BuildContext context, SplitViewState state) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
AnimatedPositioned(
|
||||
left: Dimens.zero,
|
||||
top: Dimens.zero,
|
||||
bottom: Dimens.zero,
|
||||
width: homeScreenWidth,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.elasticOut,
|
||||
child: homeScreen,
|
||||
),
|
||||
Positioned(
|
||||
left: Dimens.pt24,
|
||||
bottom: Dimens.pt36,
|
||||
height: Dimens.pt40,
|
||||
width: homeScreenWidth - Dimens.pt24,
|
||||
child: const CountdownReminder(),
|
||||
),
|
||||
AnimatedPositioned(
|
||||
right: Dimens.zero,
|
||||
top: Dimens.zero,
|
||||
bottom: Dimens.zero,
|
||||
left: state.expanded ? Dimens.zero : homeScreenWidth,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.elasticOut,
|
||||
child: const _TabletStoryView(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TabletStoryView extends StatelessWidget {
|
||||
const _TabletStoryView();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SplitViewCubit, SplitViewState>(
|
||||
buildWhen: (SplitViewState previous, SplitViewState current) =>
|
||||
previous.itemScreenArgs != current.itemScreenArgs,
|
||||
builder: (BuildContext context, SplitViewState state) {
|
||||
if (state.itemScreenArgs != null) {
|
||||
return ItemScreen.build(context, state.itemScreenArgs!);
|
||||
}
|
||||
|
||||
return Material(
|
||||
child: ColoredBox(
|
||||
color: Theme.of(context).canvasColor,
|
||||
child: const Center(
|
||||
child: Text('Tap on story tile to view comments.'),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
31
lib/screens/home/widgets/mobile_home_screen.dart
Normal file
31
lib/screens/home/widgets/mobile_home_screen.dart
Normal file
@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart' hide Badge;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hacki/cubits/cubits.dart';
|
||||
import 'package:hacki/screens/widgets/widgets.dart';
|
||||
import 'package:hacki/styles/styles.dart';
|
||||
|
||||
class MobileHomeScreen extends StatelessWidget {
|
||||
const MobileHomeScreen({
|
||||
super.key,
|
||||
required this.homeScreen,
|
||||
});
|
||||
|
||||
final Widget homeScreen;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
Positioned.fill(child: homeScreen),
|
||||
if (!context.read<ReminderCubit>().state.hasShown)
|
||||
const Positioned(
|
||||
left: Dimens.pt24,
|
||||
right: Dimens.pt24,
|
||||
bottom: Dimens.pt36,
|
||||
height: Dimens.pt40,
|
||||
child: CountdownReminder(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
72
lib/screens/home/widgets/pinned_stories.dart
Normal file
72
lib/screens/home/widgets/pinned_stories.dart
Normal file
@ -0,0 +1,72 @@
|
||||
import 'package:flutter/material.dart' hide Badge;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_fadein/flutter_fadein.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:hacki/cubits/cubits.dart';
|
||||
import 'package:hacki/models/models.dart';
|
||||
import 'package:hacki/screens/widgets/widgets.dart';
|
||||
import 'package:hacki/styles/styles.dart';
|
||||
|
||||
class PinnedStories extends StatelessWidget {
|
||||
const PinnedStories({
|
||||
super.key,
|
||||
required this.preferenceState,
|
||||
required this.onStoryTapped,
|
||||
});
|
||||
|
||||
final PreferenceState preferenceState;
|
||||
final void Function(Story story, {bool isPin}) onStoryTapped;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<PinCubit, PinState>(
|
||||
builder: (BuildContext context, PinState state) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
for (final Story story in state.pinnedStories)
|
||||
FadeIn(
|
||||
child: Slidable(
|
||||
startActionPane: ActionPane(
|
||||
motion: const BehindMotion(),
|
||||
children: <Widget>[
|
||||
SlidableAction(
|
||||
onPressed: (_) {
|
||||
HapticFeedback.lightImpact();
|
||||
context.read<PinCubit>().unpinStory(story);
|
||||
},
|
||||
backgroundColor: Palette.red,
|
||||
foregroundColor: Palette.white,
|
||||
icon: preferenceState.complexStoryTileEnabled
|
||||
? Icons.close
|
||||
: null,
|
||||
label: 'Unpin',
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ColoredBox(
|
||||
color: Palette.orangeAccent.withOpacity(0.2),
|
||||
child: StoryTile(
|
||||
key: ValueKey<String>('${story.id}-PinnedStoryTile'),
|
||||
story: story,
|
||||
onTap: () => onStoryTapped(story, isPin: true),
|
||||
showWebPreview: preferenceState.complexStoryTileEnabled,
|
||||
showMetadata: preferenceState.metadataEnabled,
|
||||
showUrl: preferenceState.urlEnabled,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (state.pinnedStories.isNotEmpty)
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: Dimens.pt12),
|
||||
child: Divider(
|
||||
color: Palette.orangeAccent,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
92
lib/screens/home/widgets/tablet_home_screen.dart
Normal file
92
lib/screens/home/widgets/tablet_home_screen.dart
Normal file
@ -0,0 +1,92 @@
|
||||
import 'package:flutter/material.dart' hide Badge;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hacki/cubits/cubits.dart';
|
||||
import 'package:hacki/screens/screens.dart';
|
||||
import 'package:hacki/screens/widgets/widgets.dart';
|
||||
import 'package:hacki/styles/styles.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
class TabletHomeScreen extends StatelessWidget {
|
||||
const TabletHomeScreen({
|
||||
super.key,
|
||||
required this.homeScreen,
|
||||
});
|
||||
|
||||
final Widget homeScreen;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(
|
||||
builder: (BuildContext context, SizingInformation sizeInfo) {
|
||||
context.read<SplitViewCubit>().enableSplitView();
|
||||
double homeScreenWidth = 428;
|
||||
|
||||
if (sizeInfo.screenSize.width < homeScreenWidth * 2) {
|
||||
homeScreenWidth = 345;
|
||||
}
|
||||
|
||||
return BlocBuilder<SplitViewCubit, SplitViewState>(
|
||||
buildWhen: (SplitViewState previous, SplitViewState current) =>
|
||||
previous.expanded != current.expanded,
|
||||
builder: (BuildContext context, SplitViewState state) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
AnimatedPositioned(
|
||||
left: Dimens.zero,
|
||||
top: Dimens.zero,
|
||||
bottom: Dimens.zero,
|
||||
width: homeScreenWidth,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.elasticOut,
|
||||
child: homeScreen,
|
||||
),
|
||||
Positioned(
|
||||
left: Dimens.pt24,
|
||||
bottom: Dimens.pt36,
|
||||
height: Dimens.pt40,
|
||||
width: homeScreenWidth - Dimens.pt24,
|
||||
child: const CountdownReminder(),
|
||||
),
|
||||
AnimatedPositioned(
|
||||
right: Dimens.zero,
|
||||
top: Dimens.zero,
|
||||
bottom: Dimens.zero,
|
||||
left: state.expanded ? Dimens.zero : homeScreenWidth,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.elasticOut,
|
||||
child: const _TabletStoryView(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TabletStoryView extends StatelessWidget {
|
||||
const _TabletStoryView();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SplitViewCubit, SplitViewState>(
|
||||
buildWhen: (SplitViewState previous, SplitViewState current) =>
|
||||
previous.itemScreenArgs != current.itemScreenArgs,
|
||||
builder: (BuildContext context, SplitViewState state) {
|
||||
if (state.itemScreenArgs != null) {
|
||||
return ItemScreen.build(context, state.itemScreenArgs!);
|
||||
}
|
||||
|
||||
return Material(
|
||||
child: ColoredBox(
|
||||
color: Theme.of(context).canvasColor,
|
||||
child: const Center(
|
||||
child: Text('Tap on story tile to view comments.'),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
3
lib/screens/home/widgets/widgets.dart
Normal file
3
lib/screens/home/widgets/widgets.dart
Normal file
@ -0,0 +1,3 @@
|
||||
export 'mobile_home_screen.dart';
|
||||
export 'pinned_stories.dart';
|
||||
export 'tablet_home_screen.dart';
|
@ -149,7 +149,6 @@ class _ItemScreenState extends State<ItemScreen> with RouteAware {
|
||||
initialRefreshStatus: RefreshStatus.refreshing,
|
||||
);
|
||||
final FocusNode focusNode = FocusNode();
|
||||
final String happyFace = Constants.happyFaces.pickRandomly()!;
|
||||
final Throttle storyLinkTapThrottle = Throttle(
|
||||
delay: _storyLinkTapThrottleDelay,
|
||||
);
|
||||
@ -233,8 +232,7 @@ class _ItemScreenState extends State<ItemScreen> with RouteAware {
|
||||
context.read<EditCubit>().state.replyingTo == null
|
||||
? 'updated'
|
||||
: 'submitted';
|
||||
final String msg =
|
||||
'Comment $verb! ${Constants.happyFaces.pickRandomly()}';
|
||||
final String msg = 'Comment $verb! ${Constants.happyFace}';
|
||||
focusNode.unfocus();
|
||||
HapticFeedback.lightImpact();
|
||||
showSnackBar(content: msg);
|
||||
@ -243,7 +241,7 @@ class _ItemScreenState extends State<ItemScreen> with RouteAware {
|
||||
} else if (postState.status == PostStatus.failure) {
|
||||
showSnackBar(
|
||||
content: 'Something went wrong...'
|
||||
'${Constants.sadFaces.pickRandomly()}',
|
||||
'${Constants.sadFace}',
|
||||
label: 'Okay',
|
||||
action: ScaffoldMessenger.of(context).hideCurrentSnackBar,
|
||||
);
|
||||
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hacki/blocs/blocs.dart';
|
||||
import 'package:hacki/config/constants.dart';
|
||||
import 'package:hacki/extensions/extensions.dart';
|
||||
import 'package:hacki/screens/widgets/widgets.dart';
|
||||
import 'package:hacki/styles/styles.dart';
|
||||
import 'package:hacki/utils/utils.dart';
|
||||
@ -28,9 +27,10 @@ class LoginDialog extends StatelessWidget {
|
||||
return BlocConsumer<AuthBloc, AuthState>(
|
||||
listener: (BuildContext context, AuthState state) {
|
||||
if (state.isLoggedIn) {
|
||||
final String happyFace = Constants.happyFaces.pickRandomly()!;
|
||||
Navigator.pop(context);
|
||||
showSnackBar(content: 'Logged in successfully! $happyFace');
|
||||
showSnackBar(
|
||||
content: 'Logged in successfully! ${Constants.happyFace}',
|
||||
);
|
||||
}
|
||||
},
|
||||
builder: (BuildContext context, AuthState state) {
|
||||
|
@ -135,7 +135,7 @@ class MainView extends StatelessWidget {
|
||||
return SizedBox(
|
||||
height: _trailingBoxHeight,
|
||||
child: Center(
|
||||
child: Text(Constants.happyFaces.pickRandomly()!),
|
||||
child: Text(Constants.happyFace),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -131,6 +131,7 @@ class _SettingsState extends State<Settings> {
|
||||
.toList(),
|
||||
onChanged: (FetchMode? fetchMode) {
|
||||
if (fetchMode != null) {
|
||||
HapticFeedback.selectionClick();
|
||||
context.read<PreferenceCubit>().update(
|
||||
FetchModePreference(),
|
||||
to: fetchMode.index,
|
||||
@ -164,6 +165,7 @@ class _SettingsState extends State<Settings> {
|
||||
.toList(),
|
||||
onChanged: (CommentsOrder? order) {
|
||||
if (order != null) {
|
||||
HapticFeedback.selectionClick();
|
||||
context.read<PreferenceCubit>().update(
|
||||
CommentsOrderPreference(),
|
||||
to: order.index,
|
||||
|
@ -1,4 +1,4 @@
|
||||
export 'home_screen.dart';
|
||||
export 'home/home_screen.dart';
|
||||
export 'item/item_screen.dart';
|
||||
export 'profile/profile_screen.dart';
|
||||
export 'search/search_screen.dart';
|
||||
|
@ -4,7 +4,6 @@ import 'dart:math';
|
||||
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:hacki/config/constants.dart';
|
||||
import 'package:hacki/extensions/extensions.dart';
|
||||
import 'package:hacki/models/models.dart';
|
||||
import 'package:hacki/repositories/repositories.dart';
|
||||
import 'package:hacki/utils/html_util.dart';
|
||||
@ -43,7 +42,6 @@ abstract class Fetcher {
|
||||
final SembastRepository sembastRepository = SembastRepository();
|
||||
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
final String happyFace = Constants.happyFaces.pickRandomly()!;
|
||||
final String? username = await authRepository.username;
|
||||
final List<int> unreadIds = await preferenceRepository.unreadCommentsIds;
|
||||
|
||||
@ -123,7 +121,7 @@ abstract class Fetcher {
|
||||
|
||||
await flutterLocalNotificationsPlugin.show(
|
||||
newReply?.id ?? 0,
|
||||
'You have a new reply! $happyFace',
|
||||
'You have a new reply! ${Constants.happyFace}',
|
||||
'${newReply?.by}: $text',
|
||||
const NotificationDetails(
|
||||
iOS: DarwinNotificationDetails(
|
||||
|
@ -2,14 +2,12 @@ import 'dart:convert';
|
||||
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:hacki/config/constants.dart';
|
||||
import 'package:hacki/extensions/extensions.dart';
|
||||
import 'package:hacki/models/models.dart';
|
||||
|
||||
class LocalNotification {
|
||||
Future<void> pushForNewReply(Comment newReply, int storyId) async {
|
||||
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
final String happyFace = Constants.happyFaces.pickRandomly()!;
|
||||
|
||||
final Map<String, int> payloadJson = <String, int>{
|
||||
'commentId': newReply.id,
|
||||
@ -19,7 +17,7 @@ class LocalNotification {
|
||||
|
||||
return flutterLocalNotificationsPlugin.show(
|
||||
newReply.id,
|
||||
'You have a new reply! $happyFace',
|
||||
'You have a new reply! ${Constants.happyFace}',
|
||||
'${newReply.by}: ${newReply.text}',
|
||||
const NotificationDetails(
|
||||
iOS: DarwinNotificationDetails(
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: hacki
|
||||
description: A Hacker News reader.
|
||||
version: 1.0.4+82
|
||||
version: 1.0.5+83
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
|
Reference in New Issue
Block a user