Compare commits
77 Commits
Author | SHA1 | Date | |
---|---|---|---|
c7824eaef3 | |||
c2b66d29c3 | |||
e0a53e44b2 | |||
4cf8379db0 | |||
c1c26bf0e0 | |||
29e2f4163d | |||
c3de80015d | |||
436cd9ce8b | |||
efb326be68 | |||
047903fe24 | |||
41068ddf89 | |||
196516ce85 | |||
7f647b127d | |||
a50a0874e7 | |||
b176be96fb | |||
1e5af07691 | |||
ecf8c902dc | |||
d3ede8546b | |||
53562ad260 | |||
6c8e7a7cb9 | |||
56c0245335 | |||
0cbd38a530 | |||
7c6da2c36a | |||
185140feb4 | |||
03c01a0b78 | |||
f823fdf241 | |||
fe87ddd8ff | |||
613ba12b05 | |||
8d7f66ecbc | |||
461aae253b | |||
a1b491cf0d | |||
edf0c82040 | |||
946a3c5a9a | |||
d8bc60c071 | |||
48477cd5c8 | |||
38df6293fe | |||
a5fe9e45fc | |||
9de5baa77a | |||
2daccd64e8 | |||
d0c68f9419 | |||
5f1dbfc510 | |||
90eee37c17 | |||
5630e61a74 | |||
eaad4b01dd | |||
3ab172f3d3 | |||
5450eba64b | |||
e2d6bb44d0 | |||
ffbd3a2449 | |||
2405a6d30c | |||
01085e5fd3 | |||
b5e11a72bf | |||
f55bbb6f84 | |||
b3e994269c | |||
a2c66a0075 | |||
5f43fd6968 | |||
d83381a7fd | |||
764ff09345 | |||
ab449adce2 | |||
2ec41b26f2 | |||
19f2107d95 | |||
c9b2d82dfc | |||
56e442b09f | |||
9069efcced | |||
bf6a5667dc | |||
cff73a010b | |||
f0d6cac3fd | |||
a90d52f348 | |||
cff4a3c5c4 | |||
502faaf188 | |||
b952f349fc | |||
9cefffa518 | |||
fe630ea7a9 | |||
459ab961d1 | |||
362d7005df | |||
6b7c1d42de | |||
2a889bca56 | |||
e25026f129 |
13
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: livinglist
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: jfeng_for_open_source
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
25
.github/workflows/commit_check.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: Commit Guard
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
- '!master'
|
||||
|
||||
jobs:
|
||||
releases:
|
||||
name: Check commit
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
FLUTTER_VERSION: "3.7.2"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: '3.7.2'
|
||||
channel: 'stable'
|
||||
- run: flutter pub get
|
||||
- run: flutter format --set-exit-if-changed .
|
||||
- run: flutter analyze
|
||||
- run: flutter test
|
32
.github/workflows/github-actions.yml
vendored
@ -1,32 +0,0 @@
|
||||
name: Releases
|
||||
|
||||
on:
|
||||
push:
|
||||
# tags:
|
||||
# - '*'
|
||||
|
||||
jobs:
|
||||
releases:
|
||||
name: release apk
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
JAVA_VERSION: "11.0"
|
||||
FLUTTER_VERSION: "3.0.0"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '17'
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: '3.0.0'
|
||||
channel: 'stable'
|
||||
- run: flutter pub get
|
||||
- run: flutter analyze
|
||||
# - run: flutter test
|
||||
# - run: flutter build apk --release
|
||||
# - uses: ncipollo/release-action@v1
|
||||
# with:
|
||||
# artifacts: "build/app/outputs/flutter-apk/*.apk"
|
||||
# token: ${{ secrets.GITHUB_TOKEN }}
|
53
.github/workflows/publish_ios.yml
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
name: Publish (iOS)
|
||||
|
||||
on:
|
||||
# Allow manual builds of this workflow
|
||||
workflow_dispatch: {}
|
||||
# Run the workflow whenever a new tag named 'v*' is pushed
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build_and_publish:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
env:
|
||||
# Point the `ruby/setup-ruby` action at this Gemfile, so it
|
||||
# caches dependencies for us.
|
||||
BUNDLE_GEMFILE: ${{ github.workspace }}/ios/Gemfile
|
||||
|
||||
steps:
|
||||
- name: Check out from git
|
||||
uses: actions/checkout@v2
|
||||
# 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.2
|
||||
- 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
|
||||
env:
|
||||
SSH_AUTH_SOCK: /tmp/ssh_agent.sock
|
||||
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 }}
|
||||
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
||||
SSH_AUTH_SOCK: /tmp/ssh_agent.sock
|
||||
run: cd ios && bundle exec fastlane beta
|
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "flutter"]
|
||||
path = submodules/flutter
|
||||
url = https://github.com/flutter/flutter
|
1
.ruby-version
Normal file
@ -0,0 +1 @@
|
||||
2.7.5
|
@ -1,16 +1,13 @@
|
||||
|
||||
# <img width="64" src="https://user-images.githubusercontent.com/7277662/167775086-0b234f28-dee4-44f6-aae4-14a28ed4bbb6.png"> Hacki for Hacker News
|
||||
|
||||
A simple noiseless [Hacker News](https://news.ycombinator.com/) client made with Flutter that is just enough.
|
||||
A [Hacker News](https://news.ycombinator.com/) client made with Flutter that is just enough.
|
||||
|
||||
[](https://apps.apple.com/us/app/hacki/id1602043763?platform=iphone)
|
||||
[](https://f-droid.org/en/packages/com.jiaqifeng.hacki/)
|
||||
[](https://github.com/Livinglist/Hacki/releases/latest)
|
||||
[](https://badges.pufler.dev)
|
||||
[](https://img.shields.io/github/stars/livinglist/Hacki?style=social)
|
||||
[](https://pub.dev/packages/effective_dart)
|
||||
|
||||
<noscript><a href="https://liberapay.com/jfeng_for_open_source/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
||||
[](https://img.shields.io/github/stars/livinglist/Hacki?style=social)
|
||||
|
||||
[<img src="assets/images/app_store_badge.png" height="50">](https://apps.apple.com/us/app/hacki/id1602043763?platform=iphone) [<img src="assets/images/google_play_badge.png" height="50">](https://play.google.com/store/apps/details?id=com.jiaqifeng.hacki&hl=en_US&gl=US) [<img src="assets/images/f_droid_badge.png" height="50">](https://f-droid.org/en/packages/com.jiaqifeng.hacki/)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
include: package:very_good_analysis/analysis_options.2.4.0.yaml
|
||||
include: package:very_good_analysis/analysis_options.3.1.0.yaml
|
||||
linter:
|
||||
rules:
|
||||
parameter_assignments: false
|
||||
@ -6,4 +6,8 @@ linter:
|
||||
library_private_types_in_public_api: false
|
||||
omit_local_variable_types: false
|
||||
one_member_abstracts: false
|
||||
always_specify_types: true
|
||||
always_specify_types: true
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
- "submodules/**"
|
||||
|
@ -33,7 +33,7 @@ if (keystorePropertiesFile.exists()) {
|
||||
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
compileSdkVersion 33
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
@ -49,10 +49,9 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.jiaqifeng.hacki"
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 30
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
@ -78,5 +77,16 @@ flutter {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
}
|
||||
|
||||
ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
|
||||
import com.android.build.OutputFile
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
|
||||
if (abiVersionCode != null) {
|
||||
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,10 @@
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:label="hacki"
|
||||
android:label="Hacki"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:usesCleartextTraffic="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
@ -25,6 +27,7 @@
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:exported="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
|
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 539 B After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 5.8 KiB |
4
android/app/src/main/res/xml/backup_rules.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<full-backup-content>
|
||||
<exclude domain="sharedpref" path="FlutterSecureStorage"/>
|
||||
</full-backup-content>
|
@ -1,12 +1,12 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.10'
|
||||
ext.kotlin_version = '1.7.0'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.1'
|
||||
classpath 'com.android.tools.build:gradle:7.1.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 698 KiB After Width: | Height: | Size: 820 KiB |
Before Width: | Height: | Size: 313 KiB After Width: | Height: | Size: 406 KiB |
@ -1,4 +1,4 @@
|
||||
include: package:very_good_analysis/analysis_options.2.4.0.yaml
|
||||
include: package:very_good_analysis/analysis_options.3.1.0.yaml
|
||||
linter:
|
||||
rules:
|
||||
parameter_assignments: false
|
||||
|
@ -1,5 +1,82 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
import Foundation
|
||||
|
||||
typealias APNSHandler = ()->Void
|
||||
|
||||
let keyKey = "key"
|
||||
let valKey = "val"
|
||||
|
||||
final class SharedPrefsCore {
|
||||
fileprivate static let shared: SharedPrefsCore = SharedPrefsCore()
|
||||
|
||||
fileprivate func setBool(key: String?, val: Bool?) -> Bool {
|
||||
guard let key = key,
|
||||
let val = val else {
|
||||
return false
|
||||
}
|
||||
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
let allVals = keyStore.dictionaryRepresentation;
|
||||
let allKeys = allVals.keys
|
||||
|
||||
// Limit is 1024, reserve rest slots for fav and pins.
|
||||
if allKeys.count >= 1000 {
|
||||
for key in allKeys.filter({ $0.contains("hasRead") }) {
|
||||
keyStore.removeObject(forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
keyStore.set(val, forKey: key)
|
||||
return true
|
||||
}
|
||||
|
||||
fileprivate func getBool(key: String?) -> Bool {
|
||||
guard let key = key else {
|
||||
return false
|
||||
}
|
||||
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
let val = keyStore.bool(forKey: key)
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
fileprivate func setStringList(key: String?, val: [String]?) -> Bool {
|
||||
guard let key = key,
|
||||
let val = val else {
|
||||
return false
|
||||
}
|
||||
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
keyStore.set(val, forKey: key)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fileprivate func getStringList(key: String?) -> [Any] {
|
||||
guard let key = key else {
|
||||
return [Any]()
|
||||
}
|
||||
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
let list = keyStore.array(forKey: key) as [Any]? ?? [Any]()
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
fileprivate func clearAll() -> Bool{
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
let allVals = keyStore.dictionaryRepresentation;
|
||||
let allKeys = allVals.keys
|
||||
|
||||
for key in allKeys.filter({ $0.contains("hasRead") }) {
|
||||
keyStore.removeObject(forKey: key)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public class SwiftSyncedSharedPreferencesPlugin: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
@ -7,46 +84,49 @@ public class SwiftSyncedSharedPreferencesPlugin: NSObject, FlutterPlugin {
|
||||
let instance = SwiftSyncedSharedPreferencesPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "setBool":
|
||||
if let params = call.arguments as? [String: Any] {
|
||||
let info: [String: Any] = ["result": result,
|
||||
"params": params]
|
||||
NotificationCenter.default.post(name: Notification.Name("setBool"), object: nil, userInfo: info)
|
||||
let val = params[valKey] as? Bool
|
||||
let key = params[keyKey] as? String
|
||||
|
||||
let res = SharedPrefsCore.shared.setBool(key: key, val: val)
|
||||
result(res)
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
case "getBool":
|
||||
if let params = call.arguments as? [String: Any] {
|
||||
let info: [String: Any] = ["result": result,
|
||||
"params": params]
|
||||
NotificationCenter.default.post(name: Notification.Name("getBool"), object: nil, userInfo: info)
|
||||
let key = params[keyKey] as? String
|
||||
let res = SharedPrefsCore.shared.getBool(key: key)
|
||||
result(res)
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
case "setStringList":
|
||||
if let params = call.arguments as? [String: Any] {
|
||||
let info: [String: Any] = ["result": result,
|
||||
"params": params]
|
||||
NotificationCenter.default.post(name: Notification.Name("setStringList"), object: nil, userInfo: info)
|
||||
let val = params[valKey] as? [String]
|
||||
let key = params[keyKey] as? String
|
||||
|
||||
let res = SharedPrefsCore.shared.setStringList(key: key, val: val)
|
||||
result(res)
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
case "getStringList":
|
||||
if let params = call.arguments as? [String: Any] {
|
||||
let info: [String: Any] = ["result": result,
|
||||
"params": params]
|
||||
NotificationCenter.default.post(name: Notification.Name("getStringList"), object: nil, userInfo: info)
|
||||
let key = params[keyKey] as? String
|
||||
let res = SharedPrefsCore.shared.getStringList(key: key)
|
||||
result(res)
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
case "clearAll":
|
||||
if let params = call.arguments as? [String: Any] {
|
||||
let info: [String: Any] = ["result": result,
|
||||
"params": params]
|
||||
NotificationCenter.default.post(name: Notification.Name("clearAll"), object: nil, userInfo: info)
|
||||
let res = SharedPrefsCore.shared.clearAll()
|
||||
result(res)
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -1 +0,0 @@
|
||||
- Bugfixes.
|
@ -1 +0,0 @@
|
||||
- Updates to UI.
|
@ -1 +0,0 @@
|
||||
- Updates to UI.
|
@ -1 +0,0 @@
|
||||
- Updates to UI.
|
@ -1 +0,0 @@
|
||||
- Tapping on comments in notification and history screen will lead you directly to the comment.
|
@ -1,3 +0,0 @@
|
||||
- Tapping on comment in notification or history screen will now lead you directly to the comment.
|
||||
- Fixed the bug where reply box cannot be expanded in editing mode.
|
||||
- Fixed inconsistent font size in history screen.
|
@ -1 +0,0 @@
|
||||
- Added offline mode.
|
@ -1,2 +0,0 @@
|
||||
- Added offline mode.
|
||||
- Bugfixes.
|
@ -1,2 +0,0 @@
|
||||
- Added offline mode.
|
||||
- Bugfixes.
|
@ -1,2 +0,0 @@
|
||||
- Added offline mode.
|
||||
- Bugfixes.
|
@ -1,3 +0,0 @@
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,3 +0,0 @@
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,3 +0,0 @@
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,4 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
@ -1,5 +0,0 @@
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
||||
- Bugfixes.
|
@ -1,6 +0,0 @@
|
||||
- You can now add filters for searching.
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
||||
- Bugfixes.
|
@ -1,6 +0,0 @@
|
||||
- You can now add filters for searching.
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
||||
- Bugfixes.
|
@ -1,6 +0,0 @@
|
||||
- You can now add filters for searching.
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
||||
- Bugfixes.
|
@ -1,6 +0,0 @@
|
||||
- You can now add filters for searching.
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
||||
- Bugfixes.
|
@ -1,7 +0,0 @@
|
||||
- You can share links.
|
||||
- You can now add filters for searching.
|
||||
- You can now participate in polls.
|
||||
- Pick up where you left off.
|
||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
||||
- Huge performance boost.
|
||||
- Bugfixes.
|
1
fastlane/metadata/android/en-US/changelogs/58.txt
Normal file
@ -0,0 +1 @@
|
||||
- Offline mode now includes web pages.
|
1
fastlane/metadata/android/en-US/changelogs/59.txt
Normal file
@ -0,0 +1 @@
|
||||
- Offline mode now includes web pages.
|
1
fastlane/metadata/android/en-US/changelogs/60.txt
Normal file
@ -0,0 +1 @@
|
||||
- Offline mode now includes web pages.
|
1
fastlane/metadata/android/en-US/changelogs/61.txt
Normal file
@ -0,0 +1 @@
|
||||
- Offline mode now includes web pages.
|
2
fastlane/metadata/android/en-US/changelogs/62.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
2
fastlane/metadata/android/en-US/changelogs/63.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
2
fastlane/metadata/android/en-US/changelogs/64.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
2
fastlane/metadata/android/en-US/changelogs/65.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
2
fastlane/metadata/android/en-US/changelogs/66.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
2
fastlane/metadata/android/en-US/changelogs/67.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
2
fastlane/metadata/android/en-US/changelogs/68.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
3
fastlane/metadata/android/en-US/changelogs/69.txt
Normal file
@ -0,0 +1,3 @@
|
||||
- Lazy loading.
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
3
fastlane/metadata/android/en-US/changelogs/70.txt
Normal file
@ -0,0 +1,3 @@
|
||||
- Lazy loading.
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
3
fastlane/metadata/android/en-US/changelogs/71.txt
Normal file
@ -0,0 +1,3 @@
|
||||
- Lazy loading.
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
3
fastlane/metadata/android/en-US/changelogs/72.txt
Normal file
@ -0,0 +1,3 @@
|
||||
- Lazy loading.
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
3
fastlane/metadata/android/en-US/changelogs/73.txt
Normal file
@ -0,0 +1,3 @@
|
||||
- Lazy loading.
|
||||
- Offline mode now includes web pages.
|
||||
- You can now sort comments in story screen.
|
2
fastlane/metadata/android/en-US/changelogs/74.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Bumped Flutter version.
|
||||
- Updated navigation bar background color.
|
2
fastlane/metadata/android/en-US/changelogs/75.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Bumped Flutter version.
|
||||
- Updated navigation bar background color.
|
2
fastlane/metadata/android/en-US/changelogs/76.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Fixed app icon.
|
||||
- Added font size setting to comments screen.
|
2
fastlane/metadata/android/en-US/changelogs/77.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Fixed app icon.
|
||||
- Added font size setting to comments screen.
|
1
fastlane/metadata/android/en-US/changelogs/78.txt
Normal file
@ -0,0 +1 @@
|
||||
- Fixed time machine.
|
1
fastlane/metadata/android/en-US/changelogs/79.txt
Normal file
@ -0,0 +1 @@
|
||||
- Fixed time machine.
|
0
fastlane/metadata/android/en-US/changelogs/81.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/84.txt
Normal file
@ -0,0 +1,3 @@
|
||||
- Customization of tab bar.
|
||||
- Option to enable swipe gesture for switching between tabs.
|
||||
- Access to action menu from home screen.
|
Before Width: | Height: | Size: 698 KiB After Width: | Height: | Size: 820 KiB |
Before Width: | Height: | Size: 313 KiB After Width: | Height: | Size: 406 KiB |
68
integration_test/scrolling_test.dart
Normal file
@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hacki/main.dart' as app;
|
||||
import 'package:hacki/screens/widgets/widgets.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
void main() {
|
||||
final IntegrationTestWidgetsFlutterBinding binding =
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
testWidgets('Scrolling test', (WidgetTester tester) async {
|
||||
await app.main(testing: true);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final Finder bestTabFinder = find.widgetWithText(Tab, 'BEST');
|
||||
|
||||
expect(bestTabFinder, findsOneWidget);
|
||||
|
||||
Future<void> scrollDown(WidgetTester tester) async {
|
||||
await tester.timedDragFrom(
|
||||
const Offset(100, 200),
|
||||
const Offset(100, -700),
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
await tester.pump();
|
||||
}
|
||||
|
||||
Future<void> scrollUp(WidgetTester tester) async {
|
||||
await tester.timedDragFrom(
|
||||
const Offset(100, 200),
|
||||
const Offset(100, 700),
|
||||
const Duration(seconds: 1),
|
||||
);
|
||||
await tester.pump();
|
||||
}
|
||||
|
||||
await binding.traceAction(
|
||||
() async {
|
||||
await tester.tap(bestTabFinder);
|
||||
await tester.pump();
|
||||
|
||||
const int count = 10;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
await scrollDown(tester);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count - 3; i++) {
|
||||
await scrollUp(tester);
|
||||
}
|
||||
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
|
||||
final Finder storyFinder = find.byType(StoryTile);
|
||||
|
||||
expect(storyFinder, findsWidgets);
|
||||
|
||||
final Finder firstStoryFinder = storyFinder.first;
|
||||
|
||||
expect(firstStoryFinder, findsOneWidget);
|
||||
|
||||
await tester.tap(firstStoryFinder);
|
||||
await tester.pump(const Duration(seconds: 4));
|
||||
},
|
||||
reportKey: 'scrolling_timeline',
|
||||
);
|
||||
});
|
||||
}
|
@ -21,6 +21,6 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>9.0</string>
|
||||
<string>11.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
4
ios/Gemfile
Normal file
@ -0,0 +1,4 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
||||
gem "cocoapods"
|
285
ios/Gemfile.lock
Normal file
@ -0,0 +1,285 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.5)
|
||||
rexml
|
||||
activesupport (6.1.7)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
addressable (2.8.1)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
algoliasearch (1.27.5)
|
||||
httpclient (~> 2.8, >= 2.8.3)
|
||||
json (>= 1.5.1)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.680.0)
|
||||
aws-sdk-core (3.168.4)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.61.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.117.2)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.5.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.1.0)
|
||||
cocoapods (1.11.3)
|
||||
addressable (~> 2.8)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.11.3)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 1.4.0, < 2.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.4.0, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
escape (~> 0.0.4)
|
||||
fourflusher (>= 2.3.0, < 3.0)
|
||||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.8.0)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (>= 1.0, < 3.0)
|
||||
xcodeproj (>= 1.21.0, < 2.0)
|
||||
cocoapods-core (1.11.3)
|
||||
activesupport (>= 5.0, < 7)
|
||||
addressable (~> 2.8)
|
||||
algoliasearch (~> 1.0)
|
||||
concurrent-ruby (~> 1.1)
|
||||
fuzzy_match (~> 2.0.4)
|
||||
nap (~> 1.0)
|
||||
netrc (~> 0.11)
|
||||
public_suffix (~> 4.0)
|
||||
typhoeus (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.5)
|
||||
cocoapods-downloader (1.6.3)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.1)
|
||||
cocoapods-trunk (1.6.0)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.2.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
concurrent-ruby (1.1.10)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.6.4)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.8.1)
|
||||
emoji_regex (3.2.3)
|
||||
escape (0.0.4)
|
||||
ethon (0.15.0)
|
||||
ffi (>= 1.15.0)
|
||||
excon (0.95.0)
|
||||
faraday (1.10.2)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.4)
|
||||
multipart-post (~> 2)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.6)
|
||||
fastlane (2.211.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander (~> 4.6)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 1.0)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
google-cloud-storage (~> 1.31)
|
||||
highline (~> 2.0)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (~> 2.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (~> 0.1.1)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
ffi (1.15.5)
|
||||
fourflusher (2.3.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.32.0)
|
||||
google-apis-core (>= 0.9.1, < 2.a)
|
||||
google-apis-core (0.9.2)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.16.0)
|
||||
google-apis-core (>= 0.9.1, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.12.0)
|
||||
google-apis-core (>= 0.9.1, < 2.a)
|
||||
google-apis-storage_v1 (0.19.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.3.0)
|
||||
google-cloud-storage (1.44.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.19.0)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.3.0)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.5)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.12.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jmespath (1.6.2)
|
||||
json (2.6.3)
|
||||
jwt (2.5.0)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.12.0)
|
||||
mini_mime (1.1.2)
|
||||
minitest (5.16.3)
|
||||
molinillo (0.8.0)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.3.0)
|
||||
nap (1.1.0)
|
||||
naturally (2.2.1)
|
||||
netrc (0.11.0)
|
||||
optparse (0.1.1)
|
||||
os (1.1.4)
|
||||
plist (3.6.0)
|
||||
public_suffix (4.0.7)
|
||||
rake (13.0.6)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.5)
|
||||
rouge (2.0.7)
|
||||
ruby-macho (2.5.1)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
security (0.1.3)
|
||||
signet (0.17.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.8)
|
||||
CFPropertyList
|
||||
naturally
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
trailblazer-option (0.1.2)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
typhoeus (1.4.0)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (2.0.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (1.8.0)
|
||||
webrick (1.7.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.22.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
zeitwerk (2.6.0)
|
||||
|
||||
PLATFORMS
|
||||
universal-darwin-21
|
||||
x86_64-darwin-19
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.22
|
@ -1,6 +1,3 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
platform :ios, '13.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
@ -37,8 +34,5 @@ end
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -3,6 +3,8 @@ PODS:
|
||||
- Flutter
|
||||
- ReachabilitySwift
|
||||
- Flutter (1.0.0)
|
||||
- flutter_email_sender (0.0.1):
|
||||
- Flutter
|
||||
- flutter_inappwebview (0.0.1):
|
||||
- Flutter
|
||||
- flutter_inappwebview/Core (= 0.0.1)
|
||||
@ -12,23 +14,29 @@ PODS:
|
||||
- OrderedSet (~> 5.0)
|
||||
- flutter_local_notifications (0.0.1):
|
||||
- Flutter
|
||||
- flutter_secure_storage (3.3.1):
|
||||
- flutter_secure_storage (6.0.0):
|
||||
- Flutter
|
||||
- flutter_siri_suggestions (0.0.1):
|
||||
- Flutter
|
||||
- FMDB (2.7.5):
|
||||
- FMDB/standard (= 2.7.5)
|
||||
- FMDB/standard (2.7.5)
|
||||
- OrderedSet (5.0.0)
|
||||
- path_provider_ios (0.0.1):
|
||||
- integration_test (0.0.1):
|
||||
- Flutter
|
||||
- OrderedSet (5.0.0)
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- ReachabilitySwift (5.0.0)
|
||||
- receive_sharing_intent (0.0.1):
|
||||
- Flutter
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_ios (0.0.1):
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- sqflite (0.0.2):
|
||||
- Flutter
|
||||
- FMDB (>= 2.7.5)
|
||||
@ -36,8 +44,6 @@ PODS:
|
||||
- Flutter
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- video_player_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
- wakelock (0.0.1):
|
||||
- Flutter
|
||||
- webview_flutter_wkwebview (0.0.1):
|
||||
@ -48,18 +54,20 @@ PODS:
|
||||
DEPENDENCIES:
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_email_sender (from `.symlinks/plugins/flutter_email_sender/ios`)
|
||||
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- flutter_siri_suggestions (from `.symlinks/plugins/flutter_siri_suggestions/ios`)
|
||||
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
||||
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
|
||||
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
- synced_shared_preferences (from `.symlinks/plugins/synced_shared_preferences/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
|
||||
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
|
||||
- workmanager (from `.symlinks/plugins/workmanager/ios`)
|
||||
@ -75,6 +83,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_email_sender:
|
||||
:path: ".symlinks/plugins/flutter_email_sender/ios"
|
||||
flutter_inappwebview:
|
||||
:path: ".symlinks/plugins/flutter_inappwebview/ios"
|
||||
flutter_local_notifications:
|
||||
@ -83,22 +93,24 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
flutter_siri_suggestions:
|
||||
:path: ".symlinks/plugins/flutter_siri_suggestions/ios"
|
||||
path_provider_ios:
|
||||
:path: ".symlinks/plugins/path_provider_ios/ios"
|
||||
integration_test:
|
||||
:path: ".symlinks/plugins/integration_test/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/ios"
|
||||
receive_sharing_intent:
|
||||
:path: ".symlinks/plugins/receive_sharing_intent/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_ios:
|
||||
:path: ".symlinks/plugins/shared_preferences_ios/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/ios"
|
||||
sqflite:
|
||||
:path: ".symlinks/plugins/sqflite/ios"
|
||||
synced_shared_preferences:
|
||||
:path: ".symlinks/plugins/synced_shared_preferences/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
video_player_avfoundation:
|
||||
:path: ".symlinks/plugins/video_player_avfoundation/ios"
|
||||
wakelock:
|
||||
:path: ".symlinks/plugins/wakelock/ios"
|
||||
webview_flutter_wkwebview:
|
||||
@ -108,26 +120,28 @@ EXTERNAL SOURCES:
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
|
||||
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
|
||||
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
|
||||
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
|
||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
||||
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||
flutter_siri_suggestions: 226fb7ef33d25d3fe0d4aa2a8bcf4b72730c466f
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5
|
||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
|
||||
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
||||
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1
|
||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
|
||||
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
|
||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
synced_shared_preferences: f722742b06d65c7315b8e9f56b794c9fbd5597f7
|
||||
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
|
||||
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
|
||||
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
|
||||
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||
webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f
|
||||
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
|
||||
|
||||
PODFILE CHECKSUM: e4c97c7a9aacaeda4b952f7ef9ea29e47660f622
|
||||
PODFILE CHECKSUM: d28e9a1c7bee335d05ddd795703aad5bf05bb937
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
COCOAPODS: 1.11.3
|
||||
|
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 51;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@ -21,7 +21,6 @@
|
||||
E530B1AD283B54DA004E8EB6 /* ActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E530B1AC283B54DA004E8EB6 /* ActionViewController.swift */; };
|
||||
E530B1B0283B54DA004E8EB6 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E530B1AE283B54DA004E8EB6 /* MainInterface.storyboard */; };
|
||||
E530B1B4283B54DA004E8EB6 /* Action Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E530B1A6283B54DA004E8EB6 /* Action Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
E54B4753282B3B8900579261 /* HackiCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54B4752282B3B8900579261 /* HackiCore.swift */; };
|
||||
E575B6F127EBC6DB002B1508 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E575B6F027EBC6DA002B1508 /* CloudKit.framework */; };
|
||||
FC507E94AA7767C155787DB3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFB5AA41D6C22D228077D166 /* Pods_Runner.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
@ -56,7 +55,7 @@
|
||||
};
|
||||
E51D52B8283B464E00FC8DD8 /* Embed App Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
buildActionMask = 8;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
@ -64,7 +63,7 @@
|
||||
E530B1B4283B54DA004E8EB6 /* Action Extension.appex in Embed App Extensions */,
|
||||
);
|
||||
name = "Embed App Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
@ -97,7 +96,6 @@
|
||||
E530B1AF283B54DA004E8EB6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||
E530B1B1283B54DA004E8EB6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
E530B1B9283B54E4004E8EB6 /* Action Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Action Extension.entitlements"; sourceTree = "<group>"; };
|
||||
E54B4752282B3B8900579261 /* HackiCore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HackiCore.swift; sourceTree = "<group>"; };
|
||||
E575B6EF27EBC6C6002B1508 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||
E575B6F027EBC6DA002B1508 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
|
||||
E59F28EE283B477D00512089 /* Share Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Share Extension.entitlements"; sourceTree = "<group>"; };
|
||||
@ -177,7 +175,6 @@
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
E54B4752282B3B8900579261 /* HackiCore.swift */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
@ -363,6 +360,7 @@
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
@ -416,6 +414,7 @@
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
@ -437,7 +436,6 @@
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
E54B4753282B3B8900579261 /* HackiCore.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -549,7 +547,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
@ -567,20 +565,25 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = QMWX3X2NF7;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Hacki;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.news";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.2.18;
|
||||
MARKETING_VERSION = 0.3.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.jiaqi.hacki";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -635,7 +638,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@ -684,7 +687,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
@ -704,20 +707,25 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = QMWX3X2NF7;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Hacki;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.news";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.2.18;
|
||||
MARKETING_VERSION = 0.3.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.jiaqi.hacki";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -735,20 +743,25 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = QMWX3X2NF7;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Hacki;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.news";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.2.18;
|
||||
MARKETING_VERSION = 0.3.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.jiaqi.hacki";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -767,9 +780,11 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = "Share Extension/Share Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = QMWX3X2NF7;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Share Extension/Info.plist";
|
||||
@ -786,6 +801,8 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jiaqi.hacki.Share-Extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "hacki share extension profile";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@ -806,9 +823,11 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = "Share Extension/Share Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = QMWX3X2NF7;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Share Extension/Info.plist";
|
||||
@ -824,6 +843,8 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jiaqi.hacki.Share-Extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.jiaqi.hacki.Share-Extension";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -842,9 +863,11 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = "Share Extension/Share Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = QMWX3X2NF7;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Share Extension/Info.plist";
|
||||
@ -860,6 +883,8 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jiaqi.hacki.Share-Extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "hacki share extension profile";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -880,9 +905,11 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = "Action Extension/Action Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = QMWX3X2NF7;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Action Extension/Info.plist";
|
||||
@ -899,6 +926,8 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jiaqi.hacki.Action-Extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "hacki action extension profile";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@ -921,9 +950,11 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = "Action Extension/Action Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = QMWX3X2NF7;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Action Extension/Info.plist";
|
||||
@ -939,6 +970,8 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jiaqi.hacki.Action-Extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.jiaqi.hacki.Action-Extension";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -959,9 +992,11 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = "Action Extension/Action Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = QMWX3X2NF7;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Action Extension/Info.plist";
|
||||
@ -977,6 +1012,8 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jiaqi.hacki.Action-Extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "hacki action extension profile";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
@ -1,9 +1,9 @@
|
||||
import UIKit
|
||||
import Flutter
|
||||
import workmanager
|
||||
import shared_preferences_ios
|
||||
import shared_preferences_foundation
|
||||
import flutter_secure_storage
|
||||
import path_provider_ios
|
||||
import path_provider_foundation
|
||||
import flutter_local_notifications
|
||||
|
||||
@UIApplicationMain
|
||||
@ -16,8 +16,6 @@ import flutter_local_notifications
|
||||
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.delegate = self
|
||||
|
||||
HackiCore.start()
|
||||
|
||||
WorkmanagerPlugin.register(with: self.registrar(forPlugin: "be.tramckrijte.workmanager.WorkmanagerPlugin")!)
|
||||
|
||||
@ -28,8 +26,8 @@ import flutter_local_notifications
|
||||
|
||||
WorkmanagerPlugin.setPluginRegistrantCallback { registry in
|
||||
GeneratedPluginRegistrant.register(with: registry)
|
||||
FLTSharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin")!)
|
||||
FLTPathProviderPlugin.register(with: registry.registrar(forPlugin: "io.flutter.plugins.pathprovider.PathProviderPlugin")!)
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin")!)
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "io.flutter.plugins.pathprovider.PathProviderPlugin")!)
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin")!)
|
||||
}
|
||||
|
||||
|
@ -1,134 +0,0 @@
|
||||
//
|
||||
// HackiCore.swift
|
||||
// Runner
|
||||
//
|
||||
// Created by Jiaqi Feng on 5/10/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Flutter
|
||||
|
||||
extension Notification.Name {
|
||||
static let setBool = Notification.Name("setBool")
|
||||
static let getBool = Notification.Name("getBool")
|
||||
static let setStringList = Notification.Name("setStringList")
|
||||
static let getStringList = Notification.Name("getStringList")
|
||||
static let clearAll = Notification.Name("clearAll")
|
||||
}
|
||||
|
||||
typealias APNSHandler = ()->Void
|
||||
|
||||
final class HackiCore: NSObject {
|
||||
private static let keyKey = "key"
|
||||
private static let valKey = "val"
|
||||
|
||||
private static let shared: HackiCore = HackiCore()
|
||||
private let notificationCenter = NotificationCenter.default
|
||||
|
||||
// Called at app launch
|
||||
class func start() {
|
||||
shared.registerNotifications()
|
||||
}
|
||||
|
||||
private class func setupFlutterEvent(channelName: String, handler: NSObjectProtocol & FlutterStreamHandler) {
|
||||
guard let rootVC = UIApplication.shared.delegate?.window.unsafelyUnwrapped?.rootViewController as? FlutterViewController else { return }
|
||||
let eventChannel = FlutterEventChannel(name: channelName, binaryMessenger: rootVC.binaryMessenger)
|
||||
eventChannel.setStreamHandler(handler)
|
||||
}
|
||||
|
||||
private func registerNotifications() {
|
||||
// SyncedSharedPreferences
|
||||
notificationCenter.addObserver(self, selector: #selector(setBool(_:)), name: .setBool, object: nil)
|
||||
notificationCenter.addObserver(self, selector: #selector(getBool(_:)), name: .getBool, object: nil)
|
||||
notificationCenter.addObserver(self, selector: #selector(setStringList(_:)), name: .setStringList, object: nil)
|
||||
notificationCenter.addObserver(self, selector: #selector(getStringList(_:)), name: .getStringList, object: nil)
|
||||
notificationCenter.addObserver(self, selector: #selector(clearAll(_:)), name: .clearAll, object: nil)
|
||||
}
|
||||
|
||||
@objc private func setBool(_ notification: Notification) {
|
||||
guard let resultCompletionBlock: FlutterResult = notification.userInfo?["result"] as? FlutterResult else { fatalError(" failed to obtain result block") }
|
||||
guard
|
||||
let params = notification.userInfo?["params"] as? [String: Any],
|
||||
let key = params[HackiCore.keyKey] as? String,
|
||||
let val = params[HackiCore.valKey] as? Bool else {
|
||||
resultCompletionBlock(false)
|
||||
return
|
||||
}
|
||||
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
let allVals = keyStore.dictionaryRepresentation;
|
||||
let allKeys = allVals.keys
|
||||
|
||||
// Limit is 1024, reserve rest slots for fav and pins.
|
||||
if allKeys.count >= 1000 {
|
||||
for key in allKeys.filter({ $0.contains("hasRead") }) {
|
||||
keyStore.removeObject(forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
keyStore.set(val, forKey: key)
|
||||
|
||||
resultCompletionBlock(true)
|
||||
}
|
||||
|
||||
@objc private func getBool(_ notification: Notification) {
|
||||
guard let resultCompletionBlock: FlutterResult = notification.userInfo?["result"] as? FlutterResult else { fatalError(" failed to obtain result block") }
|
||||
guard
|
||||
let params = notification.userInfo?["params"] as? [String: Any],
|
||||
let key = params[HackiCore.keyKey] as? String else {
|
||||
resultCompletionBlock(false)
|
||||
return
|
||||
}
|
||||
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
let val = keyStore.bool(forKey: key)
|
||||
|
||||
resultCompletionBlock(val)
|
||||
}
|
||||
|
||||
@objc private func setStringList(_ notification: Notification) {
|
||||
guard let resultCompletionBlock: FlutterResult = notification.userInfo?["result"] as? FlutterResult else { fatalError(" failed to obtain result block") }
|
||||
guard
|
||||
let params = notification.userInfo?["params"] as? [String: Any],
|
||||
let key = params[HackiCore.keyKey] as? String,
|
||||
let val = params[HackiCore.valKey] as? [String] else {
|
||||
resultCompletionBlock(false)
|
||||
return
|
||||
}
|
||||
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
keyStore.set(val, forKey: key)
|
||||
|
||||
resultCompletionBlock(true)
|
||||
}
|
||||
|
||||
@objc private func getStringList(_ notification: Notification) {
|
||||
guard let resultCompletionBlock: FlutterResult = notification.userInfo?["result"] as? FlutterResult else { fatalError(" failed to obtain result block") }
|
||||
guard
|
||||
let params = notification.userInfo?["params"] as? [String: Any],
|
||||
let key = params[HackiCore.keyKey] as? String else {
|
||||
resultCompletionBlock(false)
|
||||
return
|
||||
}
|
||||
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
let list = keyStore.array(forKey: key) as [Any]? ?? [Any]()
|
||||
|
||||
resultCompletionBlock(list)
|
||||
}
|
||||
|
||||
@objc private func clearAll(_ notification: Notification) {
|
||||
guard let resultCompletionBlock: FlutterResult = notification.userInfo?["result"] as? FlutterResult else { fatalError(" failed to obtain result block") }
|
||||
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
let allVals = keyStore.dictionaryRepresentation;
|
||||
let allKeys = allVals.keys
|
||||
|
||||
for key in allKeys.filter({ $0.contains("hasRead") }) {
|
||||
keyStore.removeObject(forKey: key)
|
||||
}
|
||||
|
||||
resultCompletionBlock(true)
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@ -38,7 +38,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>https</string>
|
||||
@ -72,5 +72,9 @@
|
||||
<array>
|
||||
<string>applinks:example.com</string>
|
||||
</array>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -7,10 +7,10 @@
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
<string>MainInterface</string>
|
||||
|
8
ios/fastlane/Appfile
Normal file
@ -0,0 +1,8 @@
|
||||
app_identifier("com.jiaqi.hacki") # The bundle identifier of your app
|
||||
apple_id("georgefung78@Live.com") # Your Apple Developer Portal username
|
||||
|
||||
itc_team_id("120097292") # App Store Connect Team ID
|
||||
team_id("QMWX3X2NF7") # Developer Portal Team ID
|
||||
|
||||
# For more information about the Appfile, see:
|
||||
# https://docs.fastlane.tools/advanced/#appfile
|
80
ios/fastlane/Fastfile
Normal file
@ -0,0 +1,80 @@
|
||||
# This file contains the fastlane.tools configuration
|
||||
# You can find the documentation at https://docs.fastlane.tools
|
||||
#
|
||||
# For a list of all available actions, check out
|
||||
#
|
||||
# https://docs.fastlane.tools/actions
|
||||
#
|
||||
# For a list of all available plugins, check out
|
||||
#
|
||||
# https://docs.fastlane.tools/plugins/available-plugins
|
||||
#
|
||||
|
||||
## Update these lines to match your project!
|
||||
# Bundle Identifier used for the iOS App on the App Store Connect portal
|
||||
APP_IDENTIFIER = 'com.jiaqi.hacki'
|
||||
# Issuer ID from the Keys section of https://appstoreconnect.apple.com/access/users
|
||||
APPSTORECONNECT_ISSUER_ID = '0b588ac9-5b3e-4420-867a-a33decce7b91'
|
||||
# Key ID from the key matching the `APP_STORE_CONNECT_API_KEY_KEY` secret, found under the Keys section of https://appstoreconnect.apple.com/access/users
|
||||
APPSTORECONNECT_KEY_ID = 'DPNP8R66QS'
|
||||
|
||||
# Uncomment the line if you want fastlane to automatically update itself
|
||||
# update_fastlane
|
||||
|
||||
default_platform(:ios)
|
||||
|
||||
platform :ios do
|
||||
desc "Push a new beta build to TestFlight"
|
||||
|
||||
lane :beta do |options|
|
||||
setup_ci if ENV['CI']
|
||||
|
||||
is_example_repo = ENV['CI'] && ENV['GITHUB_REPOSITORY'] == 'jorgenpt/flutter_github_example'
|
||||
|
||||
# Download code signing certificates using `match` (and the `MATCH_PASSWORD` secret)
|
||||
sync_code_signing(
|
||||
type: "appstore",
|
||||
app_identifier: [APP_IDENTIFIER, "#{APP_IDENTIFIER}.Share-Extension", "#{APP_IDENTIFIER}.Action-Extension"],
|
||||
readonly: true
|
||||
)
|
||||
|
||||
# We expose the key data using `APP_STORE_CONNECT_API_KEY_KEY` secret on GH
|
||||
app_store_connect_api_key(
|
||||
key_id: APPSTORECONNECT_KEY_ID,
|
||||
issuer_id: APPSTORECONNECT_ISSUER_ID
|
||||
)
|
||||
latest_testflight_build_number
|
||||
# Figure out the build number (and optionally build name)
|
||||
new_build_number = ( + 1)
|
||||
|
||||
# Prep the xcodeproject from Flutter without building (`--config-only`)
|
||||
sh(
|
||||
"flutter", "build", "ios", "--config-only",
|
||||
"--release", "--no-pub", "--no-codesign",
|
||||
"--build-number", new_build_number.to_s
|
||||
)
|
||||
|
||||
version = get_version_number(xcodeproj: "Runner.xcodeproj", target: 'Runner')
|
||||
|
||||
increment_version_number(
|
||||
version_number: version
|
||||
)
|
||||
|
||||
increment_build_number({
|
||||
build_number: latest_testflight_build_number + 1
|
||||
})
|
||||
|
||||
# Build & sign using Runner.xcworkspace
|
||||
build_app(
|
||||
workspace: "Runner.xcworkspace",
|
||||
scheme: "Runner",
|
||||
output_directory: "../build/ios/archive"
|
||||
)
|
||||
|
||||
upload_to_testflight(
|
||||
# This takes a long time, so don't waste GH runner minutes (but it means manually needing to
|
||||
# set the build live for external testers).
|
||||
skip_waiting_for_build_processing: true,
|
||||
)
|
||||
end
|
||||
end
|
13
ios/fastlane/Matchfile
Normal file
@ -0,0 +1,13 @@
|
||||
git_url("git@github.com:Livinglist/certificates.git")
|
||||
|
||||
storage_mode("git")
|
||||
|
||||
type("development") # The default type, can be: appstore, adhoc, enterprise or development
|
||||
|
||||
# app_identifier(["tools.fastlane.app", "tools.fastlane.app2"])
|
||||
# username("user@fastlane.tools") # Your Apple Developer Portal username
|
||||
|
||||
# For all available options run `fastlane match --help`
|
||||
# Remove the # in the beginning of the line to enable the other options
|
||||
|
||||
# The docs are available on https://docs.fastlane.tools/actions/match
|
@ -20,7 +20,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
_sembastRepository =
|
||||
sembastRepository ?? locator.get<SembastRepository>(),
|
||||
super(AuthState.init()) {
|
||||
super(const AuthState.init()) {
|
||||
on<AuthInitialize>(onInitialize);
|
||||
on<AuthLogin>(onLogin);
|
||||
on<AuthLogout>(onLogout);
|
||||
@ -101,7 +101,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
Future<void> onLogout(AuthLogout event, Emitter<AuthState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
user: User.empty(),
|
||||
user: const User.empty(),
|
||||
isLoggedIn: false,
|
||||
agreedToEULA: false,
|
||||
),
|
||||
|
@ -14,8 +14,8 @@ class AuthState extends Equatable {
|
||||
required this.agreedToEULA,
|
||||
});
|
||||
|
||||
AuthState.init()
|
||||
: user = User.empty(),
|
||||
const AuthState.init()
|
||||
: user = const User.empty(),
|
||||
isLoggedIn = false,
|
||||
status = AuthStatus.loaded,
|
||||
agreedToEULA = false;
|
||||
|
@ -7,7 +7,9 @@ import 'package:hacki/config/locator.dart';
|
||||
import 'package:hacki/cubits/cubits.dart';
|
||||
import 'package:hacki/models/models.dart';
|
||||
import 'package:hacki/repositories/repositories.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
part 'stories_event.dart';
|
||||
part 'stories_state.dart';
|
||||
@ -15,15 +17,18 @@ part 'stories_state.dart';
|
||||
class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
StoriesBloc({
|
||||
required PreferenceCubit preferenceCubit,
|
||||
CacheRepository? cacheRepository,
|
||||
OfflineRepository? offlineRepository,
|
||||
StoriesRepository? storiesRepository,
|
||||
PreferenceRepository? preferenceRepository,
|
||||
Logger? logger,
|
||||
}) : _preferenceCubit = preferenceCubit,
|
||||
_cacheRepository = cacheRepository ?? locator.get<CacheRepository>(),
|
||||
_offlineRepository =
|
||||
offlineRepository ?? locator.get<OfflineRepository>(),
|
||||
_storiesRepository =
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
_preferenceRepository =
|
||||
preferenceRepository ?? locator.get<PreferenceRepository>(),
|
||||
_logger = logger ?? locator.get<Logger>(),
|
||||
super(const StoriesState.init()) {
|
||||
on<StoriesInitialize>(onInitialize);
|
||||
on<StoriesRefresh>(onRefresh);
|
||||
@ -32,15 +37,18 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
on<StoryRead>(onStoryRead);
|
||||
on<StoriesLoaded>(onStoriesLoaded);
|
||||
on<StoriesDownload>(onDownload);
|
||||
on<StoriesCancelDownload>(onCancelDownload);
|
||||
on<StoryDownloaded>(onStoryDownloaded);
|
||||
on<StoriesExitOffline>(onExitOffline);
|
||||
on<StoriesPageSizeChanged>(onPageSizeChanged);
|
||||
on<ClearAllReadStories>(onClearAllReadStories);
|
||||
}
|
||||
|
||||
final PreferenceCubit _preferenceCubit;
|
||||
final CacheRepository _cacheRepository;
|
||||
final OfflineRepository _offlineRepository;
|
||||
final StoriesRepository _storiesRepository;
|
||||
final PreferenceRepository _preferenceRepository;
|
||||
final Logger _logger;
|
||||
DeviceScreenType? deviceScreenType;
|
||||
StreamSubscription<PreferenceState>? _streamSubscription;
|
||||
static const int _smallPageSize = 10;
|
||||
@ -63,20 +71,25 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
) async {
|
||||
_streamSubscription ??=
|
||||
_preferenceCubit.stream.listen((PreferenceState event) {
|
||||
final bool isComplexTile = event.showComplexStoryTile;
|
||||
final bool isComplexTile = event.complexStoryTileEnabled;
|
||||
final int pageSize = _getPageSize(isComplexTile: isComplexTile);
|
||||
|
||||
if (pageSize != state.currentPageSize) {
|
||||
add(StoriesPageSizeChanged(pageSize: pageSize));
|
||||
}
|
||||
});
|
||||
final bool hasCachedStories = await _cacheRepository.hasCachedStories;
|
||||
final bool isComplexTile = _preferenceCubit.state.showComplexStoryTile;
|
||||
final bool hasCachedStories = await _offlineRepository.hasCachedStories;
|
||||
final bool isComplexTile = _preferenceCubit.state.complexStoryTileEnabled;
|
||||
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,
|
||||
storiesToBeDownloaded: state.storiesToBeDownloaded,
|
||||
),
|
||||
);
|
||||
for (final StoryType type in types) {
|
||||
@ -89,13 +102,13 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
required Emitter<StoriesState> emit,
|
||||
}) async {
|
||||
if (state.offlineReading) {
|
||||
final List<int> ids = await _cacheRepository.getCachedStoryIds(of: of);
|
||||
final List<int> ids = await _offlineRepository.getCachedStoryIds(of: of);
|
||||
emit(
|
||||
state
|
||||
.copyWithStoryIdsUpdated(of: of, to: ids)
|
||||
.copyWithCurrentPageUpdated(of: of, to: 0),
|
||||
);
|
||||
_cacheRepository
|
||||
_offlineRepository
|
||||
.getCachedStoriesStream(
|
||||
ids: ids.sublist(0, min(ids.length, state.currentPageSize)),
|
||||
)
|
||||
@ -166,7 +179,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
}
|
||||
|
||||
if (state.offlineReading) {
|
||||
_cacheRepository
|
||||
_offlineRepository
|
||||
.getCachedStoriesStream(
|
||||
ids: state.storyIdsByType[event.type]!.sublist(
|
||||
lower,
|
||||
@ -240,39 +253,46 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
),
|
||||
);
|
||||
|
||||
await _cacheRepository.deleteAllStoryIds();
|
||||
await _cacheRepository.deleteAllStories();
|
||||
await _cacheRepository.deleteAllComments();
|
||||
await _offlineRepository.deleteAllStoryIds();
|
||||
await _offlineRepository.deleteAllStories();
|
||||
await _offlineRepository.deleteAllComments();
|
||||
|
||||
final Set<int> allIds = <int>{};
|
||||
final Set<int> prioritizedIds = <int>{};
|
||||
final List<StoryType> prioritizedTypes = <StoryType>[...types]
|
||||
..remove(StoryType.latest);
|
||||
|
||||
for (final StoryType type in types) {
|
||||
for (final StoryType type in prioritizedTypes) {
|
||||
final List<int> ids = await _storiesRepository.fetchStoryIds(of: type);
|
||||
await _cacheRepository.cacheStoryIds(of: type, ids: ids);
|
||||
allIds.addAll(ids);
|
||||
await _offlineRepository.cacheStoryIds(of: type, ids: ids);
|
||||
prioritizedIds.addAll(ids);
|
||||
}
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
storiesDownloaded: 0,
|
||||
storiesToBeDownloaded: prioritizedIds.length,
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
_storiesRepository
|
||||
.fetchStoriesStream(ids: allIds.toList())
|
||||
.listen((Story story) async {
|
||||
if (story.kids.isNotEmpty) {
|
||||
await _cacheRepository.cacheStory(story: story);
|
||||
_storiesRepository
|
||||
.fetchAllChildrenComments(ids: story.kids)
|
||||
.listen((Comment? comment) async {
|
||||
if (comment != null) {
|
||||
await _cacheRepository.cacheComment(comment: comment);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).onDone(() {
|
||||
emit(
|
||||
state.copyWith(
|
||||
downloadStatus: StoriesDownloadStatus.finished,
|
||||
),
|
||||
);
|
||||
});
|
||||
await fetchAndCacheStories(
|
||||
prioritizedIds,
|
||||
includingWebPage: event.includingWebPage,
|
||||
isPrioritized: true,
|
||||
);
|
||||
|
||||
final Set<int> latestIds = <int>{};
|
||||
final List<int> ids = await _storiesRepository.fetchStoryIds(
|
||||
of: StoryType.latest,
|
||||
);
|
||||
await _offlineRepository.cacheStoryIds(of: StoryType.latest, ids: ids);
|
||||
latestIds.addAll(ids);
|
||||
|
||||
await fetchAndCacheStories(
|
||||
latestIds,
|
||||
includingWebPage: event.includingWebPage,
|
||||
isPrioritized: false,
|
||||
);
|
||||
} catch (_) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
@ -282,11 +302,104 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onCancelDownload(
|
||||
StoriesCancelDownload event,
|
||||
Emitter<StoriesState> emit,
|
||||
) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
downloadStatus: StoriesDownloadStatus.canceled,
|
||||
),
|
||||
);
|
||||
|
||||
await _offlineRepository.deleteAllStoryIds();
|
||||
await _offlineRepository.deleteAllStories();
|
||||
await _offlineRepository.deleteAllComments();
|
||||
}
|
||||
|
||||
Future<void> fetchAndCacheStories(
|
||||
Iterable<int> ids, {
|
||||
required bool includingWebPage,
|
||||
required bool isPrioritized,
|
||||
}) async {
|
||||
for (final int id in ids) {
|
||||
if (state.downloadStatus == StoriesDownloadStatus.canceled) break;
|
||||
|
||||
_logger.d('fetching story $id');
|
||||
final Story? story = await _storiesRepository.fetchStoryBy(id);
|
||||
|
||||
if (story == null) {
|
||||
if (isPrioritized) {
|
||||
add(StoryDownloaded(skipped: true));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (story.kids.isEmpty) {
|
||||
if (isPrioritized) {
|
||||
add(StoryDownloaded(skipped: true));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
await _offlineRepository.cacheStory(story: story);
|
||||
|
||||
if (story.url.isNotEmpty && includingWebPage) {
|
||||
_logger.i('downloading ${story.url}');
|
||||
await _offlineRepository.cacheUrl(url: story.url);
|
||||
}
|
||||
|
||||
_storiesRepository
|
||||
.fetchAllChildrenComments(ids: story.kids)
|
||||
.whereType<Comment>()
|
||||
.listen(
|
||||
(Comment comment) {
|
||||
_logger.d('fetched comment ${comment.id}');
|
||||
unawaited(
|
||||
_offlineRepository.cacheComment(comment: comment),
|
||||
);
|
||||
},
|
||||
).onDone(() => add(StoryDownloaded(skipped: false)));
|
||||
}
|
||||
}
|
||||
|
||||
void onStoryDownloaded(StoryDownloaded event, Emitter<StoriesState> emit) {
|
||||
if (event.skipped) {
|
||||
final int updatedStoriesToBeDownloaded = state.storiesToBeDownloaded - 1;
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
storiesToBeDownloaded: updatedStoriesToBeDownloaded,
|
||||
downloadStatus:
|
||||
state.storiesDownloaded == updatedStoriesToBeDownloaded
|
||||
? StoriesDownloadStatus.finished
|
||||
: null,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
final int updatedStoriesDownloaded = state.storiesDownloaded + 1;
|
||||
final int updatedStoriesToBeDownloaded =
|
||||
updatedStoriesDownloaded > state.storiesToBeDownloaded
|
||||
? state.storiesToBeDownloaded + 1
|
||||
: state.storiesToBeDownloaded;
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
storiesDownloaded: updatedStoriesDownloaded,
|
||||
storiesToBeDownloaded: updatedStoriesToBeDownloaded,
|
||||
downloadStatus:
|
||||
updatedStoriesDownloaded == updatedStoriesToBeDownloaded
|
||||
? StoriesDownloadStatus.finished
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onPageSizeChanged(
|
||||
StoriesPageSizeChanged event,
|
||||
Emitter<StoriesState> emit,
|
||||
) async {
|
||||
emit(const StoriesState.init());
|
||||
add(StoriesInitialize());
|
||||
}
|
||||
|
||||
@ -294,9 +407,10 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
StoriesExitOffline event,
|
||||
Emitter<StoriesState> emit,
|
||||
) async {
|
||||
await _cacheRepository.deleteAllStoryIds();
|
||||
await _cacheRepository.deleteAllStories();
|
||||
await _cacheRepository.deleteAllComments();
|
||||
await _offlineRepository.deleteAllStoryIds();
|
||||
await _offlineRepository.deleteAllStories();
|
||||
await _offlineRepository.deleteAllComments();
|
||||
await _offlineRepository.deleteAllWebPages();
|
||||
emit(state.copyWith(offlineReading: false));
|
||||
add(StoriesInitialize());
|
||||
}
|
||||
|
@ -38,10 +38,30 @@ class StoriesLoadMore extends StoriesEvent {
|
||||
}
|
||||
|
||||
class StoriesDownload extends StoriesEvent {
|
||||
StoriesDownload({required this.includingWebPage});
|
||||
|
||||
final bool includingWebPage;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[includingWebPage];
|
||||
}
|
||||
|
||||
class StoriesCancelDownload extends StoriesEvent {
|
||||
StoriesCancelDownload();
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[];
|
||||
}
|
||||
|
||||
class StoryDownloaded extends StoriesEvent {
|
||||
StoryDownloaded({required this.skipped});
|
||||
|
||||
final bool skipped;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[skipped];
|
||||
}
|
||||
|
||||
class StoriesExitOffline extends StoriesEvent {
|
||||
@override
|
||||
List<Object?> get props => <Object?>[];
|
||||
|
@ -11,6 +11,7 @@ enum StoriesDownloadStatus {
|
||||
downloading,
|
||||
finished,
|
||||
failure,
|
||||
canceled,
|
||||
}
|
||||
|
||||
class StoriesState extends Equatable {
|
||||
@ -23,6 +24,8 @@ class StoriesState extends Equatable {
|
||||
required this.offlineReading,
|
||||
required this.downloadStatus,
|
||||
required this.currentPageSize,
|
||||
required this.storiesDownloaded,
|
||||
required this.storiesToBeDownloaded,
|
||||
});
|
||||
|
||||
const StoriesState.init({
|
||||
@ -32,7 +35,6 @@ class StoriesState extends Equatable {
|
||||
StoryType.latest: <Story>[],
|
||||
StoryType.ask: <Story>[],
|
||||
StoryType.show: <Story>[],
|
||||
StoryType.jobs: <Story>[],
|
||||
},
|
||||
this.storyIdsByType = const <StoryType, List<int>>{
|
||||
StoryType.top: <int>[],
|
||||
@ -40,7 +42,6 @@ class StoriesState extends Equatable {
|
||||
StoryType.latest: <int>[],
|
||||
StoryType.ask: <int>[],
|
||||
StoryType.show: <int>[],
|
||||
StoryType.jobs: <int>[],
|
||||
},
|
||||
this.statusByType = const <StoryType, StoriesStatus>{
|
||||
StoryType.top: StoriesStatus.initial,
|
||||
@ -48,7 +49,6 @@ class StoriesState extends Equatable {
|
||||
StoryType.latest: StoriesStatus.initial,
|
||||
StoryType.ask: StoriesStatus.initial,
|
||||
StoryType.show: StoriesStatus.initial,
|
||||
StoryType.jobs: StoriesStatus.initial,
|
||||
},
|
||||
this.currentPageByType = const <StoryType, int>{
|
||||
StoryType.top: 0,
|
||||
@ -56,12 +56,13 @@ class StoriesState extends Equatable {
|
||||
StoryType.latest: 0,
|
||||
StoryType.ask: 0,
|
||||
StoryType.show: 0,
|
||||
StoryType.jobs: 0,
|
||||
},
|
||||
}) : offlineReading = false,
|
||||
downloadStatus = StoriesDownloadStatus.initial,
|
||||
currentPageSize = 0,
|
||||
readStoriesIds = const <int>{};
|
||||
readStoriesIds = const <int>{},
|
||||
storiesDownloaded = 0,
|
||||
storiesToBeDownloaded = 0;
|
||||
|
||||
final Map<StoryType, List<Story>> storiesByType;
|
||||
final Map<StoryType, List<int>> storyIdsByType;
|
||||
@ -71,6 +72,8 @@ class StoriesState extends Equatable {
|
||||
final StoriesDownloadStatus downloadStatus;
|
||||
final bool offlineReading;
|
||||
final int currentPageSize;
|
||||
final int storiesDownloaded;
|
||||
final int storiesToBeDownloaded;
|
||||
|
||||
StoriesState copyWith({
|
||||
Map<StoryType, List<Story>>? storiesByType,
|
||||
@ -81,6 +84,8 @@ class StoriesState extends Equatable {
|
||||
StoriesDownloadStatus? downloadStatus,
|
||||
bool? offlineReading,
|
||||
int? currentPageSize,
|
||||
int? storiesDownloaded,
|
||||
int? storiesToBeDownloaded,
|
||||
}) {
|
||||
return StoriesState(
|
||||
storiesByType: storiesByType ?? this.storiesByType,
|
||||
@ -91,6 +96,9 @@ class StoriesState extends Equatable {
|
||||
offlineReading: offlineReading ?? this.offlineReading,
|
||||
downloadStatus: downloadStatus ?? this.downloadStatus,
|
||||
currentPageSize: currentPageSize ?? this.currentPageSize,
|
||||
storiesDownloaded: storiesDownloaded ?? this.storiesDownloaded,
|
||||
storiesToBeDownloaded:
|
||||
storiesToBeDownloaded ?? this.storiesToBeDownloaded,
|
||||
);
|
||||
}
|
||||
|
||||
@ -102,18 +110,12 @@ class StoriesState extends Equatable {
|
||||
final Map<StoryType, List<Story>> newMap =
|
||||
Map<StoryType, List<Story>>.from(storiesByType);
|
||||
newMap[of] = List<Story>.from(newMap[of]!)..add(story);
|
||||
return StoriesState(
|
||||
return copyWith(
|
||||
storiesByType: newMap,
|
||||
storyIdsByType: storyIdsByType,
|
||||
statusByType: statusByType,
|
||||
currentPageByType: currentPageByType,
|
||||
readStoriesIds: <int>{
|
||||
...readStoriesIds,
|
||||
if (hasRead) story.id,
|
||||
},
|
||||
offlineReading: offlineReading,
|
||||
downloadStatus: downloadStatus,
|
||||
currentPageSize: currentPageSize,
|
||||
);
|
||||
}
|
||||
|
||||
@ -124,15 +126,8 @@ class StoriesState extends Equatable {
|
||||
final Map<StoryType, List<int>> newMap =
|
||||
Map<StoryType, List<int>>.from(storyIdsByType);
|
||||
newMap[of] = to;
|
||||
return StoriesState(
|
||||
storiesByType: storiesByType,
|
||||
return copyWith(
|
||||
storyIdsByType: newMap,
|
||||
statusByType: statusByType,
|
||||
currentPageByType: currentPageByType,
|
||||
readStoriesIds: readStoriesIds,
|
||||
offlineReading: offlineReading,
|
||||
downloadStatus: downloadStatus,
|
||||
currentPageSize: currentPageSize,
|
||||
);
|
||||
}
|
||||
|
||||
@ -143,15 +138,8 @@ class StoriesState extends Equatable {
|
||||
final Map<StoryType, StoriesStatus> newMap =
|
||||
Map<StoryType, StoriesStatus>.from(statusByType);
|
||||
newMap[of] = to;
|
||||
return StoriesState(
|
||||
storiesByType: storiesByType,
|
||||
storyIdsByType: storyIdsByType,
|
||||
return copyWith(
|
||||
statusByType: newMap,
|
||||
currentPageByType: currentPageByType,
|
||||
readStoriesIds: readStoriesIds,
|
||||
offlineReading: offlineReading,
|
||||
downloadStatus: downloadStatus,
|
||||
currentPageSize: currentPageSize,
|
||||
);
|
||||
}
|
||||
|
||||
@ -162,15 +150,8 @@ class StoriesState extends Equatable {
|
||||
final Map<StoryType, int> newMap =
|
||||
Map<StoryType, int>.from(currentPageByType);
|
||||
newMap[of] = to;
|
||||
return StoriesState(
|
||||
storiesByType: storiesByType,
|
||||
storyIdsByType: storyIdsByType,
|
||||
statusByType: statusByType,
|
||||
return copyWith(
|
||||
currentPageByType: newMap,
|
||||
readStoriesIds: readStoriesIds,
|
||||
offlineReading: offlineReading,
|
||||
downloadStatus: downloadStatus,
|
||||
currentPageSize: currentPageSize,
|
||||
);
|
||||
}
|
||||
|
||||
@ -187,15 +168,11 @@ class StoriesState extends Equatable {
|
||||
final Map<StoryType, int> newCurrentPageMap =
|
||||
Map<StoryType, int>.from(currentPageByType);
|
||||
newCurrentPageMap[of] = 0;
|
||||
return StoriesState(
|
||||
return copyWith(
|
||||
storiesByType: newStoriesMap,
|
||||
storyIdsByType: newStoryIdsMap,
|
||||
statusByType: newStatusMap,
|
||||
currentPageByType: newCurrentPageMap,
|
||||
readStoriesIds: readStoriesIds,
|
||||
offlineReading: offlineReading,
|
||||
downloadStatus: downloadStatus,
|
||||
currentPageSize: currentPageSize,
|
||||
);
|
||||
}
|
||||
|
||||
@ -209,5 +186,7 @@ class StoriesState extends Equatable {
|
||||
offlineReading,
|
||||
downloadStatus,
|
||||
currentPageSize,
|
||||
storiesDownloaded,
|
||||
storiesToBeDownloaded,
|
||||
];
|
||||
}
|
||||
|