diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index 111bacf..e75317f 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -24,6 +24,14 @@ A clear and concise description of what the bug is. 3. Scroll down to '....' 4. See error +### Relevant logs + +
+ Logs + + Paste your logs here. Logs can be found in lemmur: settings > about lemmur > logs. +
+ ### Expected behavior A clear and concise description of what you expected to happen. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 119f47b..1943a7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,16 +7,12 @@ on: branches: [master] jobs: - android: - name: Android + lint: + name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 - with: - java-version: "12.x" - - uses: subosito/flutter-action@v1 with: channel: "stable" @@ -33,12 +29,51 @@ jobs: - name: Run tests run: flutter test + android: + name: Android + runs-on: ubuntu-latest + needs: lint + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-java@v1 + with: + java-version: "12.x" + + - uses: subosito/flutter-action@v1 + with: + channel: "stable" + + - name: Inject keystore + working-directory: android/app + run: | + echo "${{ secrets.SIGNING_KEY }}" | base64 -d | tee key.jks >/dev/null + - name: Android build - run: flutter build apk --split-per-abi + env: + ANDROID_KEY_ALIAS: ${{ secrets.ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + ANDROID_STORE_PATH: key.jks + ANDROID_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} + run: flutter build apk --split-per-abi --release --target lib/main_prod.dart --flavor prod + + ios: + name: iOS + runs-on: macos-latest + needs: lint + steps: + - uses: actions/checkout@v2 + + - uses: subosito/flutter-action@v1 + with: + channel: "stable" + + - run: flutter build ios --no-codesign --release --target lib/main_prod.dart --flavor prod linux: name: Linux runs-on: ubuntu-latest + needs: lint steps: - uses: actions/checkout@v2 @@ -57,11 +92,12 @@ jobs: - name: Build run: | - flutter build linux + flutter build linux --release --target lib/main_prod.dart windows: name: Windows runs-on: windows-latest + needs: lint steps: - uses: actions/checkout@v2 @@ -75,4 +111,4 @@ jobs: - name: Build run: | - flutter build windows + flutter build windows --release --target lib/main_prod.dart diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dcaedc4..ee31602 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,36 +37,36 @@ jobs: run: flutter pub get - name: Inject keystore - working-directory: android - env: - KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} - KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} - ALIAS: ${{ secrets.ALIAS }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + working-directory: android/app run: | - echo storePassword=$KEY_STORE_PASSWORD > key.properties - echo keyPassword=$KEY_PASSWORD >> key.properties - echo keyAlias=$ALIAS >> key.properties - echo storeFile=$HOME/key.jks >> key.properties - - echo $SIGNING_KEY | base64 -d | tee ~/key.jks >/dev/null + echo "${{ secrets.SIGNING_KEY }}" | base64 -d | tee key.jks >/dev/null - name: Generate appbundle - run: flutter build appbundle + env: + ANDROID_KEY_ALIAS: ${{ secrets.ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + ANDROID_STORE_PATH: key.jks + ANDROID_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} + run: flutter build appbundle --release --target lib/main_prod.dart --flavor prod - uses: actions/upload-artifact@v2 with: name: android-appbundle path: | - build/app/outputs/bundle/release/app-release.aab + build/app/outputs/bundle/prodRelease/app-prod-release.aab - name: Android build - run: | - flutter build apk --split-per-abi + env: + ANDROID_KEY_ALIAS: ${{ secrets.ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + ANDROID_STORE_PATH: key.jks + ANDROID_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} + run: + flutter build apk --split-per-abi --release --target lib/main_prod.dart --flavor prod - mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk lemmur-${{ needs.get-vars.outputs.tag }}-arm64-v8a-android.apk - mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk lemmur-${{ needs.get-vars.outputs.tag }}-armeabi-v7a-android.apk - mv build/app/outputs/flutter-apk/app-x86_64-release.apk lemmur-${{ needs.get-vars.outputs.tag }}-x86_64-android.apk + mv build/app/outputs/flutter-apk/app-arm64-v8a-prod-release.apk lemmur-${{ needs.get-vars.outputs.tag }}-arm64-v8a-android.apk + mv build/app/outputs/flutter-apk/app-armeabi-v7a-prod-release.apk lemmur-${{ needs.get-vars.outputs.tag }}-armeabi-v7a-android.apk + mv build/app/outputs/flutter-apk/app-x86_64-prod-release.apk lemmur-${{ needs.get-vars.outputs.tag }}-x86_64-android.apk - uses: actions/upload-artifact@v2 with: @@ -96,17 +96,17 @@ jobs: - name: Build run: | - flutter build linux + flutter build linux --release --target lib/main_prod.dart - name: Archive - working-directory: build/linux/release/bundle + working-directory: build/linux/x64/release/bundle run: | tar -czf lemmur-${{ needs.get-vars.outputs.tag }}-x86_64-linux.tar.gz * - uses: actions/upload-artifact@v2 with: name: linux-build - path: build/linux/release/bundle/lemmur-*.tar.gz + path: build/linux/x64/release/bundle/lemmur-*.tar.gz windows-build: name: Windows build @@ -125,7 +125,7 @@ jobs: - name: Build run: | - flutter build windows + flutter build windows --release --target lib/main_prod.dart - name: Archive working-directory: build/windows/runner/Release diff --git a/.gitignore b/.gitignore index edbaef4..ea78c22 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ app.*.map.json # Exceptions to above rules. !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages + +# Xcode build files +ios/build diff --git a/.vscode/launch.json b/.vscode/launch.json index 0e466ae..f9174c8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,19 +4,25 @@ { "name": "Debug", "request": "launch", - "type": "dart" + "type": "dart", + "program": "lib/main_dev.dart", + "args": ["--flavor", "dev"] }, { "name": "Profile", "request": "launch", "type": "dart", - "flutterMode": "profile" + "flutterMode": "profile", + "program": "lib/main_dev.dart", + "args": ["--flavor", "dev"] }, { "name": "Release", "request": "launch", "type": "dart", - "flutterMode": "release" + "flutterMode": "release", + "program": "lib/main_dev.dart", + "args": ["--flavor", "dev"] } ] } diff --git a/.vscode/lemmur.code-snippets b/.vscode/lemmur.code-snippets index 8f926b5..6f05ad0 100644 --- a/.vscode/lemmur.code-snippets +++ b/.vscode/lemmur.code-snippets @@ -29,5 +29,20 @@ "L10n string": { "prefix": "l10n", "body": ["L10n.of(context)!.$0"] + }, + "Mobx store": { + "prefix": "mobxstore", + "body": [ + "import 'package:mobx/mobx.dart';", + "", + "part '$TM_FILENAME_BASE.g.dart';", + "", + "class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g} = _${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g} with _$${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g};", + "", + "abstract class _${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g} with Store {", + "\t@observable", + "\t$0", + "}" + ] } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 8976339..6b34b9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +### Added + +- Logging: local logs about some actions/errors. Can be accessed from **settings > about lemmur > logs** + ## v0.6.0 - 2021-09-06 ### Added diff --git a/analysis_options.yaml b/analysis_options.yaml index 3355edd..3bb119f 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -8,6 +8,7 @@ linter: - avoid_init_to_null - avoid_null_checks_in_equality_operators - avoid_positional_boolean_parameters + - avoid_print - avoid_private_typedef_functions - avoid_redundant_argument_values - avoid_relative_lib_imports diff --git a/android/app/build.gradle b/android/app/build.gradle index eef128a..081e300 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -25,12 +25,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" -def keystoreProperties = new Properties() -def keystorePropertiesFile = rootProject.file('key.properties') -if (keystorePropertiesFile.exists()) { - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) -} - android { compileSdkVersion 30 @@ -51,21 +45,55 @@ android { } signingConfigs { - release { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null - storePassword keystoreProperties['storePassword'] + dev { } + + if (System.getenv("ANDROID_STORE_PATH")) { + prod { + keyAlias System.getenv("ANDROID_KEY_ALIAS") + keyPassword System.getenv("ANDROID_KEY_PASSWORD") + storeFile file(System.getenv("ANDROID_STORE_PATH")) + storePassword System.getenv("ANDROID_STORE_PASSWORD") + } + } else { + prod { } } - } + } + + flavorDimensions "app" + + productFlavors { + dev { + dimension "app" + applicationIdSuffix ".dev" + versionNameSuffix "-dev" + manifestPlaceholders = [ + appName: "lemmur DEV" + ] + signingConfig signingConfigs.debug + } + + prod { + dimension "app" + manifestPlaceholders = [ + appName: "lemmur" + ] + signingConfig signingConfigs.prod + } + } buildTypes { + debug { + testCoverageEnabled true + debuggable true + minifyEnabled false + signingConfig null + } + release { - if (keystorePropertiesFile.exists()) { - signingConfig signingConfigs.release - } else { - signingConfig signingConfigs.debug - } + debuggable false + minifyEnabled true + shrinkResources false + zipAlignEnabled true } } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 76dfcf3..c5516c9 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ + location = "self:"> diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme new file mode 100644 index 0000000..b547afd --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/prod.xcscheme similarity index 92% rename from ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to ios/Runner.xcodeproj/xcshareddata/xcschemes/prod.xcscheme index fb2dffc..c0b004d 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/prod.xcscheme @@ -1,6 +1,6 @@ @@ -40,7 +40,7 @@ + buildConfiguration = "Debug-prod"> diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 18954dc..2b09730 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,53 +1,55 @@ - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - lemmur - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(BUNDLE_NAME) + CFBundleDisplayName + $(BUNDLE_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + - - NSPhotoLibraryUsageDescription - For uploading images for posts/avatars - NSCameraUsageDescription - For uploading images for posts/avatars - NSMicrophoneUsageDescription - For recording videos for posts - + + NSPhotoLibraryUsageDescription + For uploading images for posts/avatars + NSCameraUsageDescription + For uploading images for posts/avatars + NSMicrophoneUsageDescription + For recording videos for posts + diff --git a/lib/app.dart b/lib/app.dart new file mode 100644 index 0000000..5944e4d --- /dev/null +++ b/lib/app.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:keyboard_dismisser/keyboard_dismisser.dart'; + +import 'hooks/stores.dart'; +import 'l10n/l10n.dart'; +import 'pages/home_page.dart'; +import 'theme.dart'; + +class MyApp extends HookWidget { + const MyApp(); + + @override + Widget build(BuildContext context) { + final configStore = useConfigStore(); + + return KeyboardDismisser( + child: MaterialApp( + title: 'lemmur', + supportedLocales: L10n.supportedLocales, + localizationsDelegates: L10n.localizationsDelegates, + themeMode: configStore.theme, + darkTheme: configStore.amoledDarkMode ? amoledTheme : darkTheme, + locale: configStore.locale, + theme: lightTheme, + home: const HomePage(), + ), + ); + } +} diff --git a/lib/app_config.dart b/lib/app_config.dart new file mode 100644 index 0000000..2a86cd5 --- /dev/null +++ b/lib/app_config.dart @@ -0,0 +1,7 @@ +class AppConfig { + final bool debugMode; + + const AppConfig({ + required this.debugMode, + }); +} diff --git a/lib/main_common.dart b/lib/main_common.dart new file mode 100644 index 0000000..3eec4c4 --- /dev/null +++ b/lib/main_common.dart @@ -0,0 +1,57 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import 'package:provider/provider.dart'; + +import 'app.dart'; +import 'app_config.dart'; +import 'pages/log_console_page/log_console_page_store.dart'; +import 'stores/accounts_store.dart'; +import 'stores/config_store.dart'; + +Future mainCommon(AppConfig appConfig) async { + WidgetsFlutterBinding.ensureInitialized(); + + final logConsoleStore = LogConsolePageStore(); + + _setupLogger(appConfig, logConsoleStore); + + final configStore = await ConfigStore.load(); + final accountsStore = await AccountsStore.load(); + + runApp( + MultiProvider( + providers: [ + ChangeNotifierProvider.value(value: configStore), + ChangeNotifierProvider.value(value: accountsStore), + Provider.value(value: logConsoleStore), + ], + child: const MyApp(), + ), + ); +} + +void _setupLogger(AppConfig appConfig, LogConsolePageStore logConsoleStore) { + Logger.root.level = Level.ALL; + + Logger.root.onRecord.listen((logRecord) { + // ignore: avoid_print + print(logRecord); + logConsoleStore.addLog(logRecord); + }); + + final flutterErrorLogger = Logger('FlutterError'); + + FlutterError.onError = (details) { + if (appConfig.debugMode) { + FlutterError.dumpErrorToConsole(details); + } else { + flutterErrorLogger.warning( + details.summary.name, + details.exception, + details.stack, + ); + } + }; +} diff --git a/lib/main_dev.dart b/lib/main_dev.dart new file mode 100644 index 0000000..0276d2c --- /dev/null +++ b/lib/main_dev.dart @@ -0,0 +1,6 @@ +import 'app_config.dart'; +import 'main_common.dart'; + +void main() { + mainCommon(const AppConfig(debugMode: true)); +} diff --git a/lib/main_prod.dart b/lib/main_prod.dart new file mode 100644 index 0000000..b4a7b6e --- /dev/null +++ b/lib/main_prod.dart @@ -0,0 +1,6 @@ +import 'app_config.dart'; +import 'main_common.dart'; + +void main() { + mainCommon(const AppConfig(debugMode: false)); +} diff --git a/lib/main.dart b/lib/pages/home_page.dart similarity index 57% rename from lib/main.dart rename to lib/pages/home_page.dart index 71275cb..79794dd 100644 --- a/lib/main.dart +++ b/lib/pages/home_page.dart @@ -1,64 +1,16 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:keyboard_dismisser/keyboard_dismisser.dart'; -import 'package:provider/provider.dart'; -import 'hooks/stores.dart'; -import 'l10n/l10n.dart'; -import 'pages/communities_tab.dart'; -import 'pages/create_post.dart'; -import 'pages/home_tab.dart'; -import 'pages/profile_tab.dart'; -import 'pages/search_tab.dart'; -import 'stores/accounts_store.dart'; -import 'stores/config_store.dart'; -import 'theme.dart'; -import 'util/extensions/brightness.dart'; +import '../util/extensions/brightness.dart'; +import 'communities_tab.dart'; +import 'create_post.dart'; +import 'home_tab.dart'; +import 'profile_tab.dart'; +import 'search_tab.dart'; -Future main() async { - WidgetsFlutterBinding.ensureInitialized(); - - final configStore = await ConfigStore.load(); - final accountsStore = await AccountsStore.load(); - - runApp( - MultiProvider( - providers: [ - ChangeNotifierProvider.value(value: configStore), - ChangeNotifierProvider.value(value: accountsStore), - ], - child: const MyApp(), - ), - ); -} - -class MyApp extends HookWidget { - const MyApp(); - - @override - Widget build(BuildContext context) { - final configStore = useConfigStore(); - - return KeyboardDismisser( - child: MaterialApp( - title: 'lemmur', - supportedLocales: L10n.supportedLocales, - localizationsDelegates: L10n.localizationsDelegates, - themeMode: configStore.theme, - darkTheme: configStore.amoledDarkMode ? amoledTheme : darkTheme, - locale: configStore.locale, - theme: lightTheme, - home: const MyHomePage(), - ), - ); - } -} - -class MyHomePage extends HookWidget { - const MyHomePage(); +class HomePage extends HookWidget { + const HomePage(); static const List pages = [ HomeTab(), diff --git a/lib/pages/log_console_page/log_console_page.dart b/lib/pages/log_console_page/log_console_page.dart new file mode 100644 index 0000000..20e0243 --- /dev/null +++ b/lib/pages/log_console_page/log_console_page.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:logging/logging.dart'; +import 'package:provider/provider.dart'; + +import '../../util/observer_consumers.dart'; +import '../../widgets/bottom_safe.dart'; +import 'log_console_page_store.dart'; + +class LogConsolePage extends StatelessWidget { + const LogConsolePage(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Logs')), + body: SafeArea( + child: ObserverBuilder( + builder: (context, store) { + final logStrings = store.stringified(); + + if (store.logs.isEmpty) { + return const Center( + child: Text( + 'no logs', + style: TextStyle(fontStyle: FontStyle.italic), + ), + ); + } + + return ListView.separated( + padding: const EdgeInsets.all(8) + .copyWith(bottom: BottomSafe.fabPadding + 8), + itemCount: store.logs.length, + itemBuilder: (context, i) => SelectableText( + logStrings[i], + style: TextStyle(color: store.logs[i].level.color), + ), + separatorBuilder: (context, i) => const SizedBox(height: 6), + ); + }, + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () async { + final data = context.read().stringified(); + + await Clipboard.setData(ClipboardData(text: data.join('\n'))); + + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + const SnackBar(content: Text('all logs copied to the clipboard')), + ); + }, + tooltip: 'Copy to clipboard', + child: const Icon(Icons.copy), + ), + ); + } + + static Route route() => MaterialPageRoute( + builder: (context) => const LogConsolePage(), + fullscreenDialog: true, + ); +} + +extension on Level { + Color get color { + if (this == Level.FINEST) return Colors.lime[100]!; + if (this == Level.FINER) return Colors.lime[300]!; + if (this == Level.FINE) return Colors.lime; + if (this == Level.CONFIG) return Colors.green; + if (this == Level.INFO) return Colors.blue; + if (this == Level.WARNING) return Colors.amber; + if (this == Level.SEVERE) return Colors.orange; + if (this == Level.SHOUT) return Colors.red; + + throw StateError('unreachable'); + } +} diff --git a/lib/pages/log_console_page/log_console_page_store.dart b/lib/pages/log_console_page/log_console_page_store.dart new file mode 100644 index 0000000..b39cf27 --- /dev/null +++ b/lib/pages/log_console_page/log_console_page_store.dart @@ -0,0 +1,33 @@ +import 'package:logging/logging.dart'; +import 'package:mobx/mobx.dart'; + +part 'log_console_page_store.g.dart'; + +class LogConsolePageStore = _LogConsolePageStore with _$LogConsolePageStore; + +abstract class _LogConsolePageStore with Store { + // TODO: implement as an ObservableDeque + final logs = ObservableList(); + static const _bufferSize = 200; + + @action + void addLog(LogRecord logRecord) { + if (logs.length == _bufferSize) { + logs.removeAt(0); + } + + logs.add(logRecord); + } + + List stringified() { + return logs.map( + (log) { + var str = '${log.time} $log'; + + if (log.stackTrace != null) str += '\n${log.stackTrace}'; + + return str; + }, + ).toList(); + } +} diff --git a/lib/pages/log_console_page/log_console_page_store.g.dart b/lib/pages/log_console_page/log_console_page_store.g.dart new file mode 100644 index 0000000..c7fc5e7 --- /dev/null +++ b/lib/pages/log_console_page/log_console_page_store.g.dart @@ -0,0 +1,32 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'log_console_page_store.dart'; + +// ************************************************************************** +// StoreGenerator +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic + +mixin _$LogConsolePageStore on _LogConsolePageStore, Store { + final _$_LogConsolePageStoreActionController = + ActionController(name: '_LogConsolePageStore'); + + @override + void addLog(LogRecord logRecord) { + final _$actionInfo = _$_LogConsolePageStoreActionController.startAction( + name: '_LogConsolePageStore.addLog'); + try { + return super.addLog(logRecord); + } finally { + _$_LogConsolePageStoreActionController.endAction(_$actionInfo); + } + } + + @override + String toString() { + return ''' + + '''; + } +} diff --git a/lib/pages/log_console_page/log_console_store.g.dart b/lib/pages/log_console_page/log_console_store.g.dart new file mode 100644 index 0000000..621b8c0 --- /dev/null +++ b/lib/pages/log_console_page/log_console_store.g.dart @@ -0,0 +1,32 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'log_console_store.dart'; + +// ************************************************************************** +// StoreGenerator +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic + +mixin _$LogConsoleStore on _LogConsoleStore, Store { + final _$_LogConsoleStoreActionController = + ActionController(name: '_LogConsoleStore'); + + @override + void addLog(LogRecord logRecord) { + final _$actionInfo = _$_LogConsoleStoreActionController.startAction( + name: '_LogConsoleStore.addLog'); + try { + return super.addLog(logRecord); + } finally { + _$_LogConsoleStoreActionController.endAction(_$actionInfo); + } + } + + @override + String toString() { + return ''' + + '''; + } +} diff --git a/lib/url_launcher.dart b/lib/url_launcher.dart index 75bda92..dc14fe9 100644 --- a/lib/url_launcher.dart +++ b/lib/url_launcher.dart @@ -63,7 +63,6 @@ Future linkLauncher({ return goToPost(context, matchedInstance, int.parse(split[2])); } else if (split.length == 5) { // TODO: post with focus on comment thread - print('comment in post'); return goToPost(context, matchedInstance, int.parse(split[2])); } break; @@ -73,21 +72,17 @@ Future linkLauncher({ case 'communities': // TODO: put here push to communities page - print('communities'); return; case 'modlog': // TODO: put here push to modlog - print('modlog'); return; case 'inbox': // TODO: put here push to inbox - print('inbox'); return; case 'search': // TODO: *maybe* put here push to search. we'll see // how much web version differs form the app - print('search'); return; case 'create_post': case 'create_community': diff --git a/lib/widgets/about_tile.dart b/lib/widgets/about_tile.dart index 7b817ca..bfa0964 100644 --- a/lib/widgets/about_tile.dart +++ b/lib/widgets/about_tile.dart @@ -6,6 +6,7 @@ import 'package:package_info_plus/package_info_plus.dart'; import '../gen/assets.gen.dart'; import '../hooks/memo_future.dart'; +import '../pages/log_console_page/log_console_page.dart'; import '../url_launcher.dart'; import 'bottom_modal.dart'; @@ -72,7 +73,14 @@ class AboutTile extends HookWidget { ), ), ); - }, // TODO: link to some donation site + }, + ), + TextButton.icon( + icon: const Icon(Icons.list_alt), + label: const Text('logs'), + onPressed: () { + Navigator.of(context).push(LogConsolePage.route()); + }, ), ], applicationIcon: ClipRRect( diff --git a/lib/widgets/bottom_safe.dart b/lib/widgets/bottom_safe.dart index f4eead8..451f8fe 100644 --- a/lib/widgets/bottom_safe.dart +++ b/lib/widgets/bottom_safe.dart @@ -2,9 +2,18 @@ import 'package:flutter/material.dart'; class BottomSafe extends StatelessWidget { final double additionalPadding; + + static const fabPadding = + // FAB size + FAB margin, 56 is as per https://material.io/components/buttons-floating-action-button#anatomy + 56 + kFloatingActionButtonMargin; + const BottomSafe([this.additionalPadding = 0]); + const BottomSafe.fab() : this(fabPadding); @override - Widget build(BuildContext context) => SizedBox( - height: MediaQuery.of(context).padding.bottom + additionalPadding); + Widget build(BuildContext context) { + return SizedBox( + height: MediaQuery.of(context).padding.bottom + additionalPadding, + ); + } } diff --git a/lib/widgets/comment_section.dart b/lib/widgets/comment_section.dart index 1e87ff3..b505c9c 100644 --- a/lib/widgets/comment_section.dart +++ b/lib/widgets/comment_section.dart @@ -105,7 +105,7 @@ class CommentSection extends HookWidget { com, key: ValueKey(com), ), - const BottomSafe(kMinInteractiveDimension + kFloatingActionButtonMargin), + const BottomSafe.fab(), ]); } } diff --git a/lib/widgets/post_list_options.dart b/lib/widgets/post_list_options.dart index a2bc022..cd9dd8e 100644 --- a/lib/widgets/post_list_options.dart +++ b/lib/widgets/post_list_options.dart @@ -31,10 +31,10 @@ class PostListOptions extends StatelessWidget { ), const Spacer(), if (styleButton) - IconButton( - icon: const Icon(Icons.view_stream), + const IconButton( + icon: Icon(Icons.view_stream), // TODO: create compact post and dropdown for selecting - onPressed: () => print('TBD'), + onPressed: null, ), ], ), diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt index a1da1b9..6dc9705 100644 --- a/linux/flutter/CMakeLists.txt +++ b/linux/flutter/CMakeLists.txt @@ -82,7 +82,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - linux-x64 ${CMAKE_BUILD_TYPE} + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/pubspec.lock b/pubspec.lock index 87d82b6..604d757 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -456,7 +456,7 @@ packages: source: hosted version: "0.16.0" logging: - dependency: transitive + dependency: "direct main" description: name: logging url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index ba60ea2..35357a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,6 +55,7 @@ dependencies: json_annotation: ^4.1.0 keyboard_dismisser: ^2.0.0 freezed_annotation: ^0.14.3 + logging: ^1.0.1 flutter: sdk: flutter diff --git a/scripts/common.dart b/scripts/common.dart index c718091..a9e820d 100644 --- a/scripts/common.dart +++ b/scripts/common.dart @@ -1,3 +1,4 @@ +// ignore_for_file: avoid_print import 'dart:io'; void confirm(String message) { diff --git a/scripts/release.dart b/scripts/release.dart index aa5d393..5771c9e 100755 --- a/scripts/release.dart +++ b/scripts/release.dart @@ -1,3 +1,4 @@ +// ignore_for_file: avoid_print /// Used to create a new lemmur release. Bumps semver, build number, updates changelog, fastlane, pubspec, and finishes by adding a git tag import 'dart:io';