mirror of
https://github.com/Livinglist/Hacki.git
synced 2025-08-06 18:24:42 +08:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
58139ba7a3 | |||
33a31acbe2 | |||
0fcfcbb7e3 | |||
a98f52c90b | |||
8e8e48c44a | |||
603b7cc939 | |||
649fa33df3 | |||
81d4a0f2df | |||
24112a471e | |||
c7824eaef3 |
18
.github/workflows/commit_check.yml
vendored
18
.github/workflows/commit_check.yml
vendored
@ -11,15 +11,13 @@ jobs:
|
||||
name: Check commit
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
FLUTTER_VERSION: "3.7.1"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: subosito/flutter-action@v2
|
||||
- name: checkout all the submodules
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
flutter-version: '3.7.1'
|
||||
channel: 'stable'
|
||||
- run: flutter pub get
|
||||
- run: flutter format --set-exit-if-changed .
|
||||
- run: flutter analyze
|
||||
- run: flutter test
|
||||
submodules: recursive
|
||||
- run: submodules/flutter/bin/flutter doctor
|
||||
- run: submodules/flutter/bin/flutter pub get
|
||||
- run: submodules/flutter/bin/dart format --set-exit-if-changed lib test integration_test
|
||||
- run: submodules/flutter/bin/flutter analyze lib test integration_test
|
||||
- run: submodules/flutter/bin/flutter test
|
23
.github/workflows/publish_ios.yml
vendored
23
.github/workflows/publish_ios.yml
vendored
@ -20,21 +20,21 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out from git
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- run: submodules/flutter/bin/flutter doctor
|
||||
- run: submodules/flutter/bin/flutter pub get
|
||||
- run: submodules/flutter/bin/dart format --set-exit-if-changed lib test integration_test
|
||||
- run: submodules/flutter/bin/flutter analyze lib test integration_test
|
||||
- run: submodules/flutter/bin/flutter test
|
||||
|
||||
# Configure ruby according to our .ruby-version
|
||||
- name: Setup ruby & Bundler
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
# Set up flutter (feel free to adjust the version below)
|
||||
- name: Setup flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
cache: true
|
||||
flutter-version: 3.7.1
|
||||
- run: flutter pub get
|
||||
- run: flutter format --set-exit-if-changed .
|
||||
- run: flutter analyze
|
||||
|
||||
# Start an ssh-agent that will provide the SSH key from the
|
||||
# SSH_PRIVATE_KEY secret to `fastlane match`
|
||||
- name: Setup SSH key
|
||||
@ -43,8 +43,7 @@ jobs:
|
||||
run: |
|
||||
ssh-agent -a $SSH_AUTH_SOCK > /dev/null
|
||||
ssh-add - <<< "${{ secrets.SSH_PRIVATE_KEY }}"
|
||||
- name: Download dependencies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Build & Publish to TestFlight with Fastlane
|
||||
env:
|
||||
APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }}
|
||||
|
@ -49,7 +49,7 @@ latest_testflight_build_number
|
||||
|
||||
# Prep the xcodeproject from Flutter without building (`--config-only`)
|
||||
sh(
|
||||
"flutter", "build", "ios", "--config-only",
|
||||
"/Users/runner/work/Hacki/Hacki/submodules/flutter/bin/flutter", "build", "ios", "--config-only",
|
||||
"--release", "--no-pub", "--no-codesign",
|
||||
"--build-number", new_build_number.to_s
|
||||
)
|
||||
|
@ -56,6 +56,8 @@ abstract class Constants {
|
||||
'ʕ•́ᴥ•̀ʔっ',
|
||||
'(ㆆ_ㆆ)',
|
||||
].pickRandomly()!;
|
||||
|
||||
static final String errorMessage = 'Something went wrong...$sadFace';
|
||||
}
|
||||
|
||||
abstract class RegExpConstants {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hacki/config/constants.dart';
|
||||
import 'package:hacki/screens/screens.dart';
|
||||
|
||||
/// Custom router.
|
||||
@ -39,8 +40,8 @@ class CustomRouter {
|
||||
appBar: AppBar(
|
||||
title: const Text('Error'),
|
||||
),
|
||||
body: const Center(
|
||||
child: Text('Something went wrong!'),
|
||||
body: Center(
|
||||
child: Text(Constants.errorMessage),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -20,7 +20,7 @@ Future<void> setUpLocator() async {
|
||||
Logger(
|
||||
filter: CustomLogFilter(),
|
||||
printer: LogUtil.logPrinter,
|
||||
output: LogUtil.getLogOutput(logOutputFile),
|
||||
output: LogUtil.logOutput(logOutputFile),
|
||||
),
|
||||
)
|
||||
..registerSingleton<StoriesRepository>(StoriesRepository())
|
||||
|
@ -2,6 +2,8 @@ import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hacki/config/constants.dart';
|
||||
import 'package:hacki/styles/styles.dart';
|
||||
|
||||
extension ContextExtension on BuildContext {
|
||||
T? tryRead<T>() {
|
||||
@ -12,6 +14,31 @@ extension ContextExtension on BuildContext {
|
||||
}
|
||||
}
|
||||
|
||||
void showSnackBar({
|
||||
required String content,
|
||||
VoidCallback? action,
|
||||
String? label,
|
||||
}) {
|
||||
ScaffoldMessenger.of(this).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Palette.deepOrange,
|
||||
content: Text(content),
|
||||
action: action != null && label != null
|
||||
? SnackBarAction(
|
||||
label: label,
|
||||
onPressed: action,
|
||||
textColor: Theme.of(this).textTheme.bodyLarge?.color,
|
||||
)
|
||||
: null,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void showErrorSnackBar() => showSnackBar(
|
||||
content: Constants.errorMessage,
|
||||
);
|
||||
|
||||
Rect? get rect {
|
||||
final RenderBox? box = findRenderObject() as RenderBox?;
|
||||
final Rect? rect =
|
||||
|
@ -21,22 +21,15 @@ extension StateExtension on State {
|
||||
VoidCallback? action,
|
||||
String? label,
|
||||
}) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Palette.deepOrange,
|
||||
content: Text(content),
|
||||
action: action != null && label != null
|
||||
? SnackBarAction(
|
||||
context.showSnackBar(
|
||||
content: content,
|
||||
action: action,
|
||||
label: label,
|
||||
onPressed: action,
|
||||
textColor: Theme.of(context).textTheme.bodyLarge?.color,
|
||||
)
|
||||
: null,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void showErrorSnackBar() => context.showErrorSnackBar();
|
||||
|
||||
Future<void>? goToItemScreen({
|
||||
required ItemScreenArgs args,
|
||||
bool forceNewScreen = false,
|
||||
@ -70,7 +63,6 @@ extension StateExtension on State {
|
||||
return MorePopupMenu(
|
||||
item: item,
|
||||
isBlocked: isBlocked,
|
||||
showSnackBar: showSnackBar,
|
||||
onStoryLinkTapped: onStoryLinkTapped,
|
||||
onLoginTapped: onLoginTapped,
|
||||
);
|
||||
|
@ -301,7 +301,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
.fetchStoryBy(storyId)
|
||||
.then((Story? story) {
|
||||
if (story == null) {
|
||||
showSnackBar(content: 'Something went wrong...');
|
||||
showErrorSnackBar();
|
||||
return;
|
||||
}
|
||||
final ItemScreenArgs args = ItemScreenArgs(item: story);
|
||||
@ -326,7 +326,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
.fetchStoryBy(storyId)
|
||||
.then((Story? story) {
|
||||
if (story == null) {
|
||||
showSnackBar(content: 'Something went wrong...');
|
||||
showErrorSnackBar();
|
||||
return;
|
||||
}
|
||||
final ItemScreenArgs args = ItemScreenArgs(item: story);
|
||||
|
@ -239,12 +239,7 @@ class _ItemScreenState extends State<ItemScreen> with RouteAware {
|
||||
context.read<EditCubit>().onReplySubmittedSuccessfully();
|
||||
context.read<PostCubit>().reset();
|
||||
} else if (postState.status == PostStatus.failure) {
|
||||
showSnackBar(
|
||||
content: 'Something went wrong...'
|
||||
'${Constants.sadFace}',
|
||||
label: 'Okay',
|
||||
action: ScaffoldMessenger.of(context).hideCurrentSnackBar,
|
||||
);
|
||||
showErrorSnackBar();
|
||||
context.read<PostCubit>().reset();
|
||||
}
|
||||
},
|
||||
|
@ -87,13 +87,13 @@ class LoginDialog extends StatelessWidget {
|
||||
height: Dimens.pt16,
|
||||
),
|
||||
if (state.status == AuthStatus.failure)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: Dimens.pt18,
|
||||
),
|
||||
child: Text(
|
||||
'Something went wrong...',
|
||||
style: TextStyle(
|
||||
Constants.errorMessage,
|
||||
style: const TextStyle(
|
||||
color: Palette.grey,
|
||||
fontSize: TextDimens.pt12,
|
||||
),
|
||||
|
@ -15,18 +15,12 @@ class MorePopupMenu extends StatelessWidget {
|
||||
super.key,
|
||||
required this.item,
|
||||
required this.isBlocked,
|
||||
required this.showSnackBar,
|
||||
required this.onStoryLinkTapped,
|
||||
required this.onLoginTapped,
|
||||
});
|
||||
|
||||
final Item item;
|
||||
final bool isBlocked;
|
||||
final void Function({
|
||||
required String content,
|
||||
VoidCallback? action,
|
||||
String? label,
|
||||
}) showSnackBar;
|
||||
final ValueChanged<String> onStoryLinkTapped;
|
||||
final VoidCallback onLoginTapped;
|
||||
|
||||
@ -43,24 +37,26 @@ class MorePopupMenu extends StatelessWidget {
|
||||
},
|
||||
listener: (BuildContext context, VoteState voteState) {
|
||||
if (voteState.status == VoteStatus.submitted) {
|
||||
showSnackBar(content: 'Vote submitted successfully.');
|
||||
context.showSnackBar(content: 'Vote submitted successfully.');
|
||||
} else if (voteState.status == VoteStatus.canceled) {
|
||||
showSnackBar(content: 'Vote canceled.');
|
||||
context.showSnackBar(content: 'Vote canceled.');
|
||||
} else if (voteState.status == VoteStatus.failure) {
|
||||
showSnackBar(content: 'Something went wrong...');
|
||||
context.showErrorSnackBar();
|
||||
} else if (voteState.status ==
|
||||
VoteStatus.failureKarmaBelowThreshold) {
|
||||
showSnackBar(
|
||||
context.showSnackBar(
|
||||
content: "You can't downvote because you are karmaly broke.",
|
||||
);
|
||||
} else if (voteState.status == VoteStatus.failureNotLoggedIn) {
|
||||
showSnackBar(
|
||||
context.showSnackBar(
|
||||
content: 'Not logged in, no voting! (;`O´)o',
|
||||
action: onLoginTapped,
|
||||
label: 'Log in',
|
||||
);
|
||||
} else if (voteState.status == VoteStatus.failureBeHumble) {
|
||||
showSnackBar(content: 'No voting on your own post! (;`O´)o');
|
||||
context.showSnackBar(
|
||||
content: 'No voting on your own post! (;`O´)o',
|
||||
);
|
||||
}
|
||||
|
||||
Navigator.pop(
|
||||
|
@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_fadein/flutter_fadein.dart';
|
||||
import 'package:hacki/blocs/auth/auth_bloc.dart';
|
||||
import 'package:hacki/cubits/cubits.dart';
|
||||
import 'package:hacki/extensions/context_extension.dart';
|
||||
import 'package:hacki/models/models.dart';
|
||||
import 'package:hacki/styles/styles.dart';
|
||||
|
||||
@ -66,23 +67,18 @@ class PollView extends StatelessWidget {
|
||||
content: 'Vote submitted successfully.',
|
||||
);
|
||||
} else if (voteState.status == VoteStatus.canceled) {
|
||||
showSnackBar(context, content: 'Vote canceled.');
|
||||
context.showSnackBar(content: 'Vote canceled.');
|
||||
} else if (voteState.status == VoteStatus.failure) {
|
||||
showSnackBar(
|
||||
context,
|
||||
content: 'Something went wrong...',
|
||||
);
|
||||
context.showErrorSnackBar();
|
||||
} else if (voteState.status ==
|
||||
VoteStatus.failureKarmaBelowThreshold) {
|
||||
showSnackBar(
|
||||
context,
|
||||
context.showSnackBar(
|
||||
content: "You can't downvote because"
|
||||
' you are karmaly broke.',
|
||||
);
|
||||
} else if (voteState.status ==
|
||||
VoteStatus.failureNotLoggedIn) {
|
||||
showSnackBar(
|
||||
context,
|
||||
context.showSnackBar(
|
||||
content: 'Not logged in, no voting! (;`O´)o',
|
||||
action: onLoginTapped,
|
||||
label: 'Log in',
|
||||
|
@ -453,13 +453,13 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
height: Dimens.pt16,
|
||||
),
|
||||
if (state.status == AuthStatus.failure)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: Dimens.pt18,
|
||||
),
|
||||
child: Text(
|
||||
'Something went wrong...',
|
||||
style: TextStyle(
|
||||
Constants.errorMessage,
|
||||
style: const TextStyle(
|
||||
color: Palette.grey,
|
||||
fontSize: TextDimens.pt12,
|
||||
),
|
||||
|
@ -50,9 +50,7 @@ class _SubmitScreenState extends State<SubmitScreen> {
|
||||
content: 'Post submitted successfully.',
|
||||
);
|
||||
} else if (state.status == SubmitStatus.failure) {
|
||||
showSnackBar(
|
||||
content: 'Something went wrong...',
|
||||
);
|
||||
showErrorSnackBar();
|
||||
}
|
||||
},
|
||||
builder: (BuildContext context, SubmitState state) {
|
||||
|
@ -157,6 +157,11 @@ class CommentTile extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (actionable && state.collapsed)
|
||||
Center(
|
||||
child: Padding(
|
||||
@ -200,7 +205,8 @@ class CommentTile extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
)
|
||||
else if (blocklistState.blocklist.contains(comment.by))
|
||||
else if (blocklistState.blocklist
|
||||
.contains(comment.by))
|
||||
const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
@ -226,7 +232,8 @@ class CommentTile extends StatelessWidget {
|
||||
? SelectableText.rich(
|
||||
key: ValueKey<int>(comment.id),
|
||||
buildTextSpan(
|
||||
(comment as BuildableComment).elements,
|
||||
(comment as BuildableComment)
|
||||
.elements,
|
||||
style: TextStyle(
|
||||
fontSize: MediaQuery.of(
|
||||
context,
|
||||
@ -238,12 +245,14 @@ class CommentTile extends StatelessWidget {
|
||||
context,
|
||||
).textScaleFactor *
|
||||
prefState.fontSize.fontSize,
|
||||
decoration: TextDecoration.underline,
|
||||
decoration:
|
||||
TextDecoration.underline,
|
||||
color: Palette.orange,
|
||||
),
|
||||
onOpen: (LinkableElement link) {
|
||||
if (link.url.isStoryLink) {
|
||||
onStoryLinkTapped.call(link.url);
|
||||
onStoryLinkTapped
|
||||
.call(link.url);
|
||||
} else {
|
||||
LinkUtil.launch(link.url);
|
||||
}
|
||||
@ -293,13 +302,16 @@ class CommentTile extends StatelessWidget {
|
||||
horizontal: Dimens.pt12,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
HapticFeedback.selectionClick();
|
||||
context.read<CommentsCubit>().loadMore(
|
||||
context
|
||||
.read<CommentsCubit>()
|
||||
.loadMore(
|
||||
comment: comment,
|
||||
);
|
||||
},
|
||||
@ -320,6 +332,9 @@ class CommentTile extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -111,7 +111,7 @@ class _CountDownReminderState extends State<CountdownReminder>
|
||||
.fetchStoryBy(state.storyId!)
|
||||
.then((Story? story) {
|
||||
if (story == null) {
|
||||
showSnackBar(content: 'Something went wrong...');
|
||||
showErrorSnackBar();
|
||||
return;
|
||||
}
|
||||
final ItemScreenArgs args = ItemScreenArgs(item: story);
|
||||
|
@ -5,7 +5,7 @@ import 'package:hacki/config/constants.dart';
|
||||
import 'package:hacki/extensions/extensions.dart';
|
||||
import 'package:hacki/models/models.dart';
|
||||
import 'package:hacki/screens/widgets/link_preview/link_view.dart';
|
||||
import 'package:hacki/screens/widgets/link_preview/web_analyzer.dart';
|
||||
import 'package:hacki/services/services.dart';
|
||||
import 'package:hacki/styles/styles.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
@ -119,7 +119,7 @@ class _LinkPreviewState extends State<LinkPreview> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_errorTitle = widget.errorTitle ?? 'Something went wrong!';
|
||||
_errorTitle = widget.errorTitle ?? Constants.errorMessage;
|
||||
_errorBody = widget.errorBody ??
|
||||
'Oops! Unable to parse the url. We have '
|
||||
'sent feedback to our developers & '
|
||||
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hacki/blocs/blocs.dart';
|
||||
import 'package:hacki/cubits/cubits.dart';
|
||||
import 'package:hacki/screens/widgets/link_preview/web_analyzer.dart';
|
||||
import 'package:hacki/services/services.dart';
|
||||
import 'package:hacki/styles/styles.dart';
|
||||
|
||||
class OfflineBanner extends StatelessWidget {
|
||||
|
@ -3,3 +3,4 @@ export 'custom_bloc_observer.dart';
|
||||
export 'fetcher.dart';
|
||||
export 'firebase_client.dart';
|
||||
export 'local_notification.dart';
|
||||
export 'web_analyzer.dart';
|
||||
|
@ -14,7 +14,7 @@ abstract class LogUtil {
|
||||
colors: false,
|
||||
);
|
||||
|
||||
static LogOutput getLogOutput(File outputFile) => MultiOutput(
|
||||
static LogOutput logOutput(File outputFile) => MultiOutput(
|
||||
<LogOutput>[
|
||||
ConsoleOutput(),
|
||||
CustomFileOutput(
|
||||
@ -43,7 +43,7 @@ abstract class LogUtil {
|
||||
|
||||
final Uint8List fileContent = await currentSessionLog.readAsBytes();
|
||||
await previousSessionLog.writeAsString(
|
||||
'Current session logs:',
|
||||
'Current session logs:\n',
|
||||
mode: FileMode.append,
|
||||
);
|
||||
return previousSessionLog.writeAsBytes(
|
||||
|
@ -1359,4 +1359,4 @@ packages:
|
||||
version: "3.1.1"
|
||||
sdks:
|
||||
dart: ">=2.18.0 <3.0.0"
|
||||
flutter: ">=3.7.1"
|
||||
flutter: ">=3.7.3"
|
||||
|
@ -1,11 +1,11 @@
|
||||
name: hacki
|
||||
description: A Hacker News reader.
|
||||
version: 1.0.7+85
|
||||
version: 1.0.9+87
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
flutter: "3.7.1"
|
||||
flutter: "3.7.3"
|
||||
|
||||
dependencies:
|
||||
adaptive_theme: ^3.0.0
|
||||
|
Submodule submodules/flutter updated: 7048ed95a5...9944297138
Reference in New Issue
Block a user