Compare commits
162 Commits
Author | SHA1 | Date | |
---|---|---|---|
d4fe042245 | |||
b82c4a1777 | |||
7e0d1f0f1d | |||
f405a10c2e | |||
edbad79cd3 | |||
c9d8b2950a | |||
f2bc48f980 | |||
d56697c57c | |||
320ec41aae | |||
d85b3535d5 | |||
f8cd1cbba0 | |||
817ec208d6 | |||
554a165789 | |||
0c680370ef | |||
59541d2fcc | |||
32083c3564 | |||
258dbc4b8b | |||
6c8047ebac | |||
00a0135867 | |||
1db7be7a2c | |||
ff400f9c40 | |||
f03b45a98a | |||
cbe5bba986 | |||
268f4054a3 | |||
988c5d9881 | |||
e748e2f818 | |||
1b0a0dbda9 | |||
64d68389ba | |||
381c99b353 | |||
39ee3137f8 | |||
0d76be8634 | |||
9986f72e11 | |||
ef557e7b84 | |||
ec065c0122 | |||
2960c6e59e | |||
92dac6b932 | |||
20365393a3 | |||
8d238744c7 | |||
e33ff417fb | |||
d8922c2641 | |||
c6e0461857 | |||
30ca356dc8 | |||
7d11398e6d | |||
a4f52284ef | |||
c7d1a42d5a | |||
f83fd66bcc | |||
c2ec3647e2 | |||
ba63852b7d | |||
438041183c | |||
114540edd7 | |||
588b3e9508 | |||
2f0376f8f8 | |||
ab4051c018 | |||
c230c21218 | |||
c24e12237e | |||
e15dcba93b | |||
1362b93a74 | |||
ac18793f98 | |||
e52f65c773 | |||
06212a0d72 | |||
e77c0e3e73 | |||
cb6f41ec49 | |||
ab1e90ccad | |||
0ca3e96d91 | |||
d1c8eed3de | |||
aa6a2c684c | |||
d4778d9530 | |||
c702e08481 | |||
2af10391bc | |||
c420dd3ca4 | |||
da7d0757cd | |||
32ae2087bc | |||
0b5329d050 | |||
c375def289 | |||
3469543c7b | |||
ab755581fd | |||
6b75eb8549 | |||
36ded8a8e3 | |||
582ac7b0be | |||
e5e3391785 | |||
9159fe0fe1 | |||
7c51bad35e | |||
6836138d11 | |||
2f71964277 | |||
c24c5c1b7a | |||
755b112382 | |||
d44b64d249 | |||
35ed917e66 | |||
15b75ef37c | |||
f39408fbcc | |||
ca2f063297 | |||
1ad231adbb | |||
60b09fd81e | |||
fe162208ca | |||
58139ba7a3 | |||
33a31acbe2 | |||
0fcfcbb7e3 | |||
a98f52c90b | |||
8e8e48c44a | |||
603b7cc939 | |||
649fa33df3 | |||
81d4a0f2df | |||
24112a471e | |||
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 |
24
.github/workflows/commit_check.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
name: Commit Guard
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
- '!master'
|
||||
|
||||
jobs:
|
||||
releases:
|
||||
name: Check commit
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: checkout all the submodules
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- run: submodules/flutter/bin/flutter doctor
|
||||
- run: submodules/flutter/bin/flutter pub get
|
||||
- run: submodules/flutter/bin/dart format --set-exit-if-changed lib test integration_test
|
||||
- run: submodules/flutter/bin/flutter analyze lib test integration_test
|
||||
- run: submodules/flutter/bin/flutter test
|
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.3"
|
||||
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.3'
|
||||
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@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- run: submodules/flutter/bin/flutter doctor
|
||||
- run: submodules/flutter/bin/flutter pub get
|
||||
- run: submodules/flutter/bin/dart format --set-exit-if-changed lib test integration_test
|
||||
- run: submodules/flutter/bin/flutter analyze lib test integration_test
|
||||
- run: submodules/flutter/bin/flutter test
|
||||
|
||||
# Configure ruby according to our .ruby-version
|
||||
- name: Setup ruby & Bundler
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
|
||||
# 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: 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,14 +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)
|
||||
[](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/)
|
||||
|
||||
@ -30,6 +29,7 @@ Features:
|
||||
- Download stories and comments for offline reading.
|
||||
- Pick up where you left off.
|
||||
- Synced favorites and pins across devices. (iOS only)
|
||||
- Export or import your favorites.
|
||||
- Launch from system share sheet.
|
||||
- And more...
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
include: package:very_good_analysis/analysis_options.2.4.0.yaml
|
||||
include: package:very_good_analysis/analysis_options.5.0.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 32
|
||||
compileSdkVersion 33
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
@ -50,8 +50,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.jiaqifeng.hacki"
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 32
|
||||
minSdkVersion 25
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
@ -64,12 +64,15 @@ android {
|
||||
storePassword keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
flutter {
|
||||
@ -79,3 +82,14 @@ flutter {
|
||||
dependencies {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,9 @@
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.support.customtabs.action.CustomTabsService" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
@ -37,15 +40,6 @@
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<!-- Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_adaptive_back"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_adaptive_fore"/>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 4.9 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 539 B After Width: | Height: | Size: 4.7 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png
Normal file
After Width: | Height: | Size: 940 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 5.0 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 5.4 KiB |
@ -24,6 +24,6 @@ subprojects {
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
18
assets/eula.md
Normal file
@ -0,0 +1,18 @@
|
||||
## End-user License Agreement
|
||||
This policy applies to the usage of the Hacki app.
|
||||
|
||||
Please read this Mobile Application End User License Agreement (“EULA”) carefully before using the Hacki mobile application ("Mobile App"), which allows You to read and contribute to Hacker News from Your mobile device. This EULA forms a binding legal agreement between you (and any other entity on whose behalf you accept these terms) (collectively “You” or “Your”) and Hacki (each separately a “Party” and collectively the “Parties”) as of the date you download the Mobile App. Your use of the Mobile App is subject to this EULA.
|
||||
|
||||
### Changes to this EULA
|
||||
Hacki reserves the right to modify this EULA at any time and for any reason. You are responsible for complying with the updated EULA. Your continued use of the Mobile App indicates Your consent to the updated terms.
|
||||
|
||||
### No Included Maintenance and Support
|
||||
Hacki may deploy changes, updates, or enhancements to the Mobile App at any time. Hacki may provide maintenance and support for the Mobile App, but has no obligation whatsoever to furnish such services to You and may terminate such services at any time without notice.
|
||||
|
||||
### No Warranty
|
||||
Hacki expressly disclaims all warranties of any kind, whether express or implied.
|
||||
|
||||
The Mobile App is only available for supported devices and might not work on every device. Determining whether Your device is a supported or compatible device for use of the Mobile App is solely Your responsibility, and downloading the Mobile App is done at Your own risk. Smartsheet does not represent or warrant that the Mobile App and Your device are compatible or that the Mobile App will work on Your device.
|
||||
|
||||
### Your Consent
|
||||
By using the app, you consent to the end-user license agreement.
|
BIN
assets/fonts/noto_serif/NotoSerif-Bold.ttf
Normal file
BIN
assets/fonts/noto_serif/NotoSerif-Regular.ttf
Normal file
BIN
assets/fonts/roboto_slab/RobotoSlab-Bold.ttf
Normal file
BIN
assets/fonts/roboto_slab/RobotoSlab-Regular.ttf
Normal file
BIN
assets/fonts/ubuntu/Ubuntu-Bold.ttf
Normal file
BIN
assets/fonts/ubuntu/Ubuntu-Regular.ttf
Normal file
BIN
assets/fonts/ubuntu_mono/UbuntuMono-Bold.ttf
Normal file
BIN
assets/fonts/ubuntu_mono/UbuntuMono-Regular.ttf
Normal file
BIN
assets/hacki-github.xcf
Normal file
BIN
assets/hacki.xcf
Normal file
48
assets/privacy_policy.md
Normal file
@ -0,0 +1,48 @@
|
||||
## Privacy Policy
|
||||
This policy applies to all information collected or submitted on Hacki.
|
||||
|
||||
### Information we collect
|
||||
Hacki collects anonymous statistics such as crash reports and feature usage. These data are solely used to track app's health and are only stored locally on your device and only got sent to us when you choose to do so.
|
||||
|
||||
### Ads and analytics
|
||||
Hacki does not serve ads.
|
||||
|
||||
Hacki collects aggregate, anonymous statistics to improve the app but these data are only stored locally on your device and only got sent to us when you choose to do so.
|
||||
|
||||
### Information usage
|
||||
We use the information we collect to operate and improve our website, apps, and customer support.
|
||||
|
||||
We do not share personal information with outside parties except to the extent necessary to accomplish Hacki’s functionality.
|
||||
|
||||
We may disclose your information in response to subpoenas, court orders, or other legal requirements; to exercise our legal rights or defend against legal claims; to investigate, prevent, or take action regarding illegal activities, suspected fraud or abuse, violations of our policies; or to protect our rights and property.
|
||||
|
||||
### Security
|
||||
Hacki uses the official Hacker News API for fetching data from Hacker News.
|
||||
|
||||
When logging in, usernames and passwords are securely sent to Hacker News' servers for authentication.
|
||||
|
||||
### Third-party links and content
|
||||
Hacki displays links and content from third-party websites. These websites have their own independent privacy policies, and we have no responsibility or liability for their content or activities.
|
||||
|
||||
#### California Online Privacy Protection Act Compliance
|
||||
Hacki complies with the California Online Privacy Protection Act. We therefore will not distribute your personal information to outside parties without your consent.
|
||||
|
||||
#### Children’s Online Privacy Protection Act Compliance
|
||||
Hacki never collects or maintain information at our website from those we actually know are under 13, and no part of our website is structured to attract anyone under 13.
|
||||
|
||||
#### Information for European Union Customers
|
||||
By using Hacki and providing your information, you authorize us to collect, use, and store your information outside of the European Union.
|
||||
|
||||
#### International Transfers of Information
|
||||
Information may be processed, stored, and used outside of the country in which you are located. Data privacy laws vary across jurisdictions, and different laws may be applicable to your data depending on where it is processed, stored, or used.
|
||||
|
||||
### Your Consent
|
||||
By using the app, you consent to the privacy policy.
|
||||
|
||||
### Contacting Us
|
||||
If you have questions regarding this privacy policy, you may e-mail me us at jfeng@fastmail.com.
|
||||
|
||||
### Changes to this policy
|
||||
If we decide to change this privacy policy, we will post those changes on this page.
|
||||
|
||||
February 27, 2023: First published.
|
BIN
assets/screenshots/hacki-1.png
Normal file
After Width: | Height: | Size: 890 KiB |
BIN
assets/screenshots/hacki-2.png
Normal file
After Width: | Height: | Size: 873 KiB |
BIN
assets/screenshots/hacki-3.png
Normal file
After Width: | Height: | Size: 770 KiB |
BIN
assets/screenshots/hacki-4.png
Normal file
After Width: | Height: | Size: 517 KiB |
30
components/in_app_review/.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
39
components/in_app_review/.metadata
Normal file
@ -0,0 +1,39 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
channel: stable
|
||||
|
||||
project_type: plugin
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
- platform: android
|
||||
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
- platform: ios
|
||||
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
- platform: macos
|
||||
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
- platform: windows
|
||||
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
102
components/in_app_review/CHANGELOG.md
Normal file
@ -0,0 +1,102 @@
|
||||
# [2.0.6]
|
||||
- Update Android Play Core dependency to Play Review 2.0.1.
|
||||
|
||||
# [2.0.5]
|
||||
|
||||
- Migrate Android Play Core dependency to Play Review 2.0.0.
|
||||
- Recreate the example app.
|
||||
- Update in_app_review_platform_interface to 2.0.4
|
||||
|
||||
# [2.0.4]
|
||||
|
||||
- Migrate maven repository from jcenter to mavenCentral
|
||||
- `isAvailable()` now returns `false` on web.
|
||||
|
||||
# [2.0.3]
|
||||
|
||||
- Fix iOS no-scene exception. ([#41](https://github.com/britannio/in_app_review/issues/41))
|
||||
# [2.0.2]
|
||||
|
||||
- Replace iOS Swift code with Objective-C to add compatibility with Objective-C Flutter apps.
|
||||
|
||||
# [2.0.1]
|
||||
|
||||
- Fix rare null pointer exception on Android
|
||||
- Fix MissingPluginException on MacOS
|
||||
- Bump the minimum Dart SDK version from `2.12.0-0` to `2.12.0`.
|
||||
- Bump the minimum Flutter version to `2.0.0`.
|
||||
- Update in_app_review_platform_interface to 2.0.2
|
||||
|
||||
# [2.0.0]
|
||||
|
||||
- Migrate to null safety.
|
||||
|
||||
# [1.0.4]
|
||||
|
||||
- Update in_app_review_platform_interface to 1.0.5
|
||||
- Remove dependency on `package_info`.
|
||||
- Handle `openStoreListing()` with native code for Android, iOS and MacOS.
|
||||
|
||||
# [1.0.3]
|
||||
|
||||
- Update in_app_review_platform_interface to 1.0.4
|
||||
- Update android compileSdkVersion to 29.
|
||||
- Lower dependency version constraints.
|
||||
|
||||
# [1.0.2]
|
||||
|
||||
- Update in_app_review_platform_interface to 1.0.3
|
||||
- Open the App Store directly instead of via the Safari View Controller.
|
||||
- Add automated tests.
|
||||
- Improve docs.
|
||||
|
||||
# [1.0.1+1]
|
||||
|
||||
- Update in_app_review_platform_interface to 1.0.2
|
||||
|
||||
# [1.0.0]
|
||||
|
||||
- Migrate to use `in_app_review_platform_interface`.
|
||||
- Add Windows support for `openStoreListing`.
|
||||
|
||||
# [0.2.1+1]
|
||||
|
||||
- Improve iOS testing docs.
|
||||
|
||||
# [0.2.1]
|
||||
|
||||
- Update dependencies.
|
||||
- Android Play Core Library V1.8.2 release notes:
|
||||
- Fixed UI flickering in the In-App Review API
|
||||
|
||||
# [0.2.0+4]
|
||||
|
||||
- Remove deprecated API warning.
|
||||
- Update dependencies.
|
||||
|
||||
# [0.2.0+3]
|
||||
|
||||
- Instructions in the README have been improved along with the example.
|
||||
|
||||
# [0.2.0+2]
|
||||
|
||||
- Update changelog format
|
||||
|
||||
# [0.2.0+1]
|
||||
|
||||
- Update MacOS testing instructions
|
||||
|
||||
# [0.2.0] Breaking Change
|
||||
|
||||
- Add MacOS support
|
||||
- Rename `openStoreListing(iOSAppStoreId: '')` to `openStoreListing(appStoreId: '')`
|
||||
|
||||
# [0.1.0]
|
||||
|
||||
- Improve docs
|
||||
- Set Android minSdkVersion to 16
|
||||
- Refactor Android Plugin
|
||||
|
||||
# [0.0.1]
|
||||
|
||||
Initial release
|
21
components/in_app_review/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Britannio Jarrett
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
0
components/in_app_review/analysis_options.yaml
Normal file
9
components/in_app_review/android/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.cxx
|
49
components/in_app_review/android/build.gradle
Normal file
@ -0,0 +1,49 @@
|
||||
group 'dev.britannio.in_app_review'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.10'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
}
|
||||
dependencies {
|
||||
|
||||
}
|
||||
}
|
1
components/in_app_review/android/settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
rootProject.name = 'in_app_review'
|
@ -0,0 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="dev.britannio.in_app_review">
|
||||
</manifest>
|
@ -0,0 +1,59 @@
|
||||
package dev.britannio.in_app_review;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugin.common.MethodChannel.Result;
|
||||
|
||||
/**
|
||||
* InAppReviewPlugin
|
||||
*/
|
||||
public class InAppReviewPlugin implements FlutterPlugin, MethodCallHandler {
|
||||
/// The MethodChannel that will the communication between Flutter and native Android
|
||||
///
|
||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
/// when the Flutter Engine is detached from the Activity
|
||||
private MethodChannel channel;
|
||||
|
||||
|
||||
private final String TAG = "InAppReviewPlugin";
|
||||
|
||||
@Override
|
||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
||||
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "dev.britannio.in_app_review");
|
||||
channel.setMethodCallHandler(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
||||
Log.i(TAG, "onMethodCall: " + call.method);
|
||||
switch (call.method) {
|
||||
case "isAvailable":
|
||||
case "requestReview":
|
||||
case "openStoreListing":
|
||||
default:
|
||||
result.notImplemented();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
||||
channel.setMethodCallHandler(null);
|
||||
}
|
||||
}
|
38
components/in_app_review/ios/.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
.idea/
|
||||
.vagrant/
|
||||
.sconsign.dblite
|
||||
.svn/
|
||||
|
||||
.DS_Store
|
||||
*.swp
|
||||
profile
|
||||
|
||||
DerivedData/
|
||||
build/
|
||||
GeneratedPluginRegistrant.h
|
||||
GeneratedPluginRegistrant.m
|
||||
|
||||
.generated/
|
||||
|
||||
*.pbxuser
|
||||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.perspectivev3
|
||||
|
||||
!default.pbxuser
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
!default.perspectivev3
|
||||
|
||||
xcuserdata
|
||||
|
||||
*.moved-aside
|
||||
|
||||
*.pyc
|
||||
*sync/
|
||||
Icon?
|
||||
.tags*
|
||||
|
||||
/Flutter/Generated.xcconfig
|
||||
/Flutter/ephemeral/
|
||||
/Flutter/flutter_export_environment.sh
|
0
components/in_app_review/ios/Assets/.gitkeep
Normal file
4
components/in_app_review/ios/Classes/InAppReviewPlugin.h
Normal file
@ -0,0 +1,4 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface InAppReviewPlugin : NSObject<FlutterPlugin>
|
||||
@end
|
107
components/in_app_review/ios/Classes/InAppReviewPlugin.m
Normal file
@ -0,0 +1,107 @@
|
||||
#import "InAppReviewPlugin.h"
|
||||
|
||||
@import StoreKit;
|
||||
@implementation InAppReviewPlugin
|
||||
|
||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
||||
|
||||
FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"dev.britannio.in_app_review" binaryMessenger:[registrar messenger]];
|
||||
|
||||
InAppReviewPlugin* instance = [[InAppReviewPlugin alloc] init];
|
||||
[registrar addMethodCallDelegate:instance channel:channel];
|
||||
}
|
||||
|
||||
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
|
||||
|
||||
[self logMessage:@"handle" details:call.method];
|
||||
|
||||
if ([call.method isEqual:@"requestReview"]) {
|
||||
[self requestReview:result];
|
||||
} else if ([call.method isEqual:@"isAvailable"]) {
|
||||
[self isAvailable:result];
|
||||
} else if ([call.method isEqual:@"openStoreListing"]) {
|
||||
[self openStoreListingWithStoreId:call.arguments result:result];
|
||||
} else {
|
||||
[self logMessage:@"method not implemented"];
|
||||
result(FlutterMethodNotImplemented);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) requestReview:(FlutterResult)result {
|
||||
if (@available(iOS 14, *)) {
|
||||
[self logMessage:@"iOS 14+"];
|
||||
UIWindowScene *scene = [self findActiveScene];
|
||||
[SKStoreReviewController requestReviewInScene:scene];
|
||||
result(nil);
|
||||
} else if (@available(iOS 10.3, *)) {
|
||||
[self logMessage:@"iOS 10.3+"];
|
||||
[SKStoreReviewController requestReview];
|
||||
result(nil);
|
||||
} else {
|
||||
result([FlutterError errorWithCode:@"unavailable"
|
||||
message:@"In-App Review unavailable"
|
||||
details:nil]);
|
||||
}
|
||||
}
|
||||
|
||||
- (UIWindowScene *) findActiveScene API_AVAILABLE(ios(13.0)){
|
||||
for (UIWindowScene *scene in UIApplication.sharedApplication.connectedScenes) {
|
||||
|
||||
if (scene.activationState == UISceneActivationStateForegroundActive) {
|
||||
return scene;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void) isAvailable:(FlutterResult)result {
|
||||
if (@available(iOS 10.3, *)) {
|
||||
[self logMessage:@"available"];
|
||||
result(@YES);
|
||||
} else {
|
||||
[self logMessage:@"unavailable"];
|
||||
result(@NO);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) openStoreListingWithStoreId:(NSString *)storeId result:(FlutterResult)result {
|
||||
|
||||
if (!storeId) {
|
||||
result([FlutterError errorWithCode:@"no-store-id"
|
||||
message:@"Your store id must be passed as the method channel's argument"
|
||||
details:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://apps.apple.com/app/id%@?action=write-review", storeId]];
|
||||
|
||||
if (!url) {
|
||||
result([FlutterError errorWithCode:@"url-construct-fail"
|
||||
message:@"Failed to construct url"
|
||||
details:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
UIApplication *app = [UIApplication sharedApplication];
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[app openURL:url options:@{} completionHandler:nil];
|
||||
} else {
|
||||
[app openURL:url];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Logging Helpers
|
||||
|
||||
- (void) logMessage:(NSString *) message {
|
||||
NSLog(@"InAppReviewPlugin: %@", message);
|
||||
}
|
||||
|
||||
- (void) logMessage:(NSString *) message
|
||||
details:(NSString *) details {
|
||||
NSLog(@"InAppReviewPlugin: %@ %@", message, details);
|
||||
}
|
||||
|
||||
@end
|
23
components/in_app_review/ios/in_app_review.podspec
Normal file
@ -0,0 +1,23 @@
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint in_app_review.podspec` to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'in_app_review'
|
||||
s.version = '0.2.0'
|
||||
s.summary = 'Flutter plugin for showing the In-App Review/System Rating pop up.'
|
||||
s.description = <<-DESC
|
||||
Flutter plugin for showing the In-App Review/System Rating pop up..
|
||||
DESC
|
||||
s.homepage = 'http://example.com'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Britannio Jarrett' => 'britanniojarrett@gmail.com' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'Flutter'
|
||||
s.platform = :ios, '9.0'
|
||||
|
||||
# Flutter.framework does not contain a i386 slice.
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
||||
s.swift_version = '5.0'
|
||||
end
|
50
components/in_app_review/lib/in_app_review.dart
Normal file
@ -0,0 +1,50 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:in_app_review_platform_interface/in_app_review_platform_interface.dart';
|
||||
|
||||
class InAppReview {
|
||||
InAppReview._();
|
||||
|
||||
static final InAppReview instance = InAppReview._();
|
||||
|
||||
/// Checks if the device is able to show a review dialog.
|
||||
///
|
||||
/// On Android the Google Play Store must be installed and the device must be
|
||||
/// running **Android 5 Lollipop(API 21)** or higher.
|
||||
///
|
||||
/// iOS devices must be running **iOS version 10.3** or higher.
|
||||
///
|
||||
/// MacOS devices must be running **MacOS version 10.14** or higher
|
||||
Future<bool> isAvailable() => InAppReviewPlatform.instance.isAvailable();
|
||||
|
||||
/// Attempts to show the review dialog. It's recommended to first check if
|
||||
/// the device supports this feature via [isAvailable].
|
||||
///
|
||||
/// To improve the users experience, iOS and Android enforce limitations
|
||||
/// that might prevent this from working after a few tries. iOS & MacOS users
|
||||
/// can also disable this feature entirely in the App Store settings.
|
||||
///
|
||||
/// More info and guidance:
|
||||
/// https://developer.android.com/guide/playcore/in-app-review#when-to-request
|
||||
/// https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/ratings-and-reviews/
|
||||
/// https://developer.apple.com/design/human-interface-guidelines/macos/system-capabilities/ratings-and-reviews/
|
||||
Future<void> requestReview() => InAppReviewPlatform.instance.requestReview();
|
||||
|
||||
/// Opens the Play Store on Android, the App Store with a review
|
||||
/// screen on iOS & MacOS and the Microsoft Store on Windows.
|
||||
///
|
||||
/// [appStoreId] is required for iOS & MacOS.
|
||||
///
|
||||
/// [microsoftStoreId] is required for Windows.
|
||||
Future<void> openStoreListing({
|
||||
/// Required for iOS & MacOS.
|
||||
String? appStoreId,
|
||||
|
||||
/// Required for Windows.
|
||||
String? microsoftStoreId,
|
||||
}) =>
|
||||
InAppReviewPlatform.instance.openStoreListing(
|
||||
appStoreId: appStoreId,
|
||||
microsoftStoreId: microsoftStoreId,
|
||||
);
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import StoreKit
|
||||
|
||||
public class InAppReviewPlugin: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "dev.britannio.in_app_review", binaryMessenger: registrar.messenger)
|
||||
let instance = InAppReviewPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "requestReview":
|
||||
//App Store Review
|
||||
if #available(OSX 10.14, *) {
|
||||
SKStoreReviewController.requestReview()
|
||||
result(nil)
|
||||
} else {
|
||||
result(FlutterError(code: "unavailable", message: "In-App Review unavailable", details: nil))
|
||||
}
|
||||
case "isAvailable":
|
||||
if #available(OSX 10.14, *) {
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
case "openStoreListing":
|
||||
let storeId : String = call.arguments as! String;
|
||||
|
||||
guard let writeReviewURL = URL(string: "macappstore://apps.apple.com/app/id" + storeId + "?action=write-review")
|
||||
else {
|
||||
result(FlutterError(code: "url_construct_fail", message: "Failed to construct url", details: nil))
|
||||
return
|
||||
}
|
||||
NSWorkspace.shared.open(writeReviewURL)
|
||||
result(nil);
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
22
components/in_app_review/macos/in_app_review.podspec
Normal file
@ -0,0 +1,22 @@
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint in_app_review.podspec' to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'in_app_review'
|
||||
s.version = '0.2.0'
|
||||
s.summary = 'Flutter plugin for showing the In-App Review/System Rating pop up.'
|
||||
s.description = <<-DESC
|
||||
Flutter plugin for showing the In-App Review/System Rating pop up.
|
||||
DESC
|
||||
s.homepage = 'https://github.com/britannio/in_app_review'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Britannio Jarrett' => 'britanniojarrett@gmail.com' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'FlutterMacOS'
|
||||
|
||||
s.platform = :osx, '10.11'
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
||||
s.swift_version = '5.0'
|
||||
end
|
46
components/in_app_review/pubspec.yaml
Normal file
@ -0,0 +1,46 @@
|
||||
name: in_app_review
|
||||
description: Flutter plugin for showing the In-App Review/System Rating pop up on Android, iOS and MacOS. It makes it easy for users to rate your app.
|
||||
version: 2.0.6
|
||||
homepage: https://github.com/britannio/in_app_review/tree/master/in_app_review
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
flutter: ">=2.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
in_app_review_platform_interface: ^2.0.4
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mockito: ^5.0.0
|
||||
plugin_platform_interface: ^2.0.0
|
||||
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
# This section identifies this Flutter project as a plugin project.
|
||||
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
|
||||
# which should be registered in the plugin registry. This is required for
|
||||
# using method channels.
|
||||
# The Android 'package' specifies package in which the registered class is.
|
||||
# This is required for using method channels on Android.
|
||||
# The 'ffiPlugin' specifies that native code should be built and bundled.
|
||||
# This is required for using `dart:ffi`.
|
||||
# All these are used by the tooling to maintain consistency when
|
||||
# adding or updating assets for this project.
|
||||
plugin:
|
||||
platforms:
|
||||
android:
|
||||
package: dev.britannio.in_app_review
|
||||
pluginClass: InAppReviewPlugin
|
||||
ios:
|
||||
pluginClass: InAppReviewPlugin
|
||||
macos:
|
||||
pluginClass: InAppReviewPlugin
|
12
components/in_app_review_platform_interface/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
.DS_Store
|
||||
.dart_tool/
|
||||
|
||||
.packages
|
||||
.pub/
|
||||
|
||||
build/
|
||||
|
||||
|
||||
pubspec.lock
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
46
components/in_app_review_platform_interface/CHANGELOG.md
Normal file
@ -0,0 +1,46 @@
|
||||
# [2.0.4]
|
||||
|
||||
- Update usage of `pkg:url_launcher` to address deprecations.
|
||||
|
||||
# [2.0.3]
|
||||
|
||||
- `isAvailable()` now returns `false` on web.
|
||||
|
||||
# [2.0.2]
|
||||
|
||||
- Bump the minimum Flutter version to `2.0.0`.
|
||||
|
||||
# [2.0.1]
|
||||
|
||||
- Bump the minimum Dart SDK version from `2.12.0-0` to `2.12.0`.
|
||||
|
||||
# [2.0.0]
|
||||
|
||||
- Migrate to null safety.
|
||||
|
||||
# [1.0.5]
|
||||
|
||||
- Remove dependency on `package_info`.
|
||||
- Handle `openStoreListing()` with native code for Android, iOS and MacOS.
|
||||
|
||||
# [1.0.4]
|
||||
|
||||
- Lower dependency version constraints
|
||||
|
||||
# [1.0.3]
|
||||
|
||||
- Open the App Store directly instead of via the Safari View Controller.
|
||||
- Add automated tests.
|
||||
|
||||
# [1.0.2]
|
||||
|
||||
- Rename `openStoreListing(windowsStoreId: '')` to `openStoreListing(microsoftStoreId: '')`.
|
||||
- Update dependencies.
|
||||
|
||||
# [1.0.1]
|
||||
|
||||
- Remove unnecessary files.
|
||||
|
||||
# [1.0.0]
|
||||
|
||||
- Initial release.
|
21
components/in_app_review_platform_interface/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Britannio Jarrett
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
26
components/in_app_review_platform_interface/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# in_app_review_platform_interface
|
||||
|
||||
A common platform interface for the [`in_app_review`][1] plugin.
|
||||
|
||||
This interface allows platform-specific implementations of the `in_app_review`
|
||||
plugin, as well as the plugin itself, to ensure they are supporting the
|
||||
same interface.
|
||||
|
||||
# Usage
|
||||
|
||||
To implement a new platform-specific implementation of `in_app_review`, extend
|
||||
[`InAppReviewPlatform`][2] with an implementation that performs the
|
||||
platform-specific behavior, and when you register your plugin, set the default
|
||||
`InAppReviewPlatform` by calling
|
||||
`InAppReviewPlatform.instance = MyInAppReview()`.
|
||||
|
||||
# Note on breaking changes
|
||||
|
||||
Strongly prefer non-breaking changes (such as adding a method to the interface)
|
||||
over breaking changes for this package.
|
||||
|
||||
See https://flutter.dev/go/platform-interface-breaking-changes for a discussion
|
||||
on why a less-clean interface is preferable to a breaking change.
|
||||
|
||||
[1]: ../in_app_review
|
||||
[2]: lib/in_app_review_platform_interface.dart
|
@ -0,0 +1,71 @@
|
||||
import 'package:in_app_review_platform_interface/method_channel_in_app_review.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
/// The interface that implementations of in_app_review must implement.
|
||||
///
|
||||
/// Platform implementations should extend this class rather than implement it
|
||||
/// as `in_app_review` does not consider newly added methods to be breaking
|
||||
/// changes. Extending this class (using `extends`) ensures that the subclass
|
||||
/// will get the default implementation, while platform implementations that
|
||||
/// `implements` this interface will be broken by newly added
|
||||
/// [InAppReviewPlatform] methods.
|
||||
abstract class InAppReviewPlatform extends PlatformInterface {
|
||||
InAppReviewPlatform() : super(token: _token);
|
||||
|
||||
static InAppReviewPlatform _instance = MethodChannelInAppReview();
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static InAppReviewPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific plugins should set this with their own platform-specific
|
||||
/// class that extends [InAppReviewPlatform] when they register themselves.
|
||||
static set instance(InAppReviewPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
/// Checks if the device is able to show a review dialog.
|
||||
///
|
||||
/// On Android the Google Play Store must be installed and the device must be
|
||||
/// running **Android 5 Lollipop(API 21)** or higher.
|
||||
///
|
||||
/// iOS devices must be running **iOS version 10.3** or higher.
|
||||
///
|
||||
/// MacOS devices must be running **MacOS version 10.14** or higher
|
||||
Future<bool> isAvailable() {
|
||||
throw UnimplementedError('isAvailable() has not been implemented.');
|
||||
}
|
||||
|
||||
/// Attempts to show the review dialog. It's recommended to first check if
|
||||
/// this cannot be done via [isAvailable]. If it is not available then
|
||||
/// you can open the store listing via [openStoreListing].
|
||||
///
|
||||
/// To improve the users experience, iOS and Android enforce limitations
|
||||
/// that might prevent this from working after a few tries. iOS & MacOS users
|
||||
/// can also disable this feature entirely in the App Store settings.
|
||||
///
|
||||
/// More info and guidance:
|
||||
/// https://developer.android.com/guide/playcore/in-app-review#when-to-request
|
||||
/// https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/ratings-and-reviews/
|
||||
/// https://developer.apple.com/design/human-interface-guidelines/macos/system-capabilities/ratings-and-reviews/
|
||||
Future<void> requestReview() {
|
||||
throw UnimplementedError('requestReview() has not been implemented.');
|
||||
}
|
||||
|
||||
/// Opens the Play Store on Android, the App Store with a review
|
||||
/// screen on iOS & MacOS and the Microsoft Store on Windows.
|
||||
///
|
||||
/// [appStoreId] is required for iOS & MacOS.
|
||||
///
|
||||
/// [microsoftStoreId] is required for Windows.
|
||||
Future<void> openStoreListing({
|
||||
/// Required for iOS & MacOS.
|
||||
String? appStoreId,
|
||||
|
||||
/// Required for Windows.
|
||||
String? microsoftStoreId,
|
||||
}) {
|
||||
throw UnimplementedError('openStoreListing() has not been implemented.');
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import 'in_app_review_platform_interface.dart';
|
||||
|
||||
/// An implementation of [InAppReviewPlatform] that uses method channels.
|
||||
class MethodChannelInAppReview extends InAppReviewPlatform {
|
||||
MethodChannel _channel = MethodChannel('dev.britannio.in_app_review');
|
||||
Platform _platform = const LocalPlatform();
|
||||
|
||||
@visibleForTesting
|
||||
set channel(MethodChannel channel) => _channel = channel;
|
||||
|
||||
@visibleForTesting
|
||||
set platform(Platform platform) => _platform = platform;
|
||||
|
||||
@override
|
||||
Future<bool> isAvailable() async {
|
||||
if (kIsWeb) return false;
|
||||
return _channel
|
||||
.invokeMethod<bool>('isAvailable')
|
||||
.then((bool? available) => available ?? false, onError: (_) => false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> requestReview() => _channel.invokeMethod('requestReview');
|
||||
|
||||
@override
|
||||
Future<void> openStoreListing({
|
||||
String? appStoreId,
|
||||
String? microsoftStoreId,
|
||||
}) async {
|
||||
final bool isiOS = _platform.isIOS;
|
||||
final bool isMacOS = _platform.isMacOS;
|
||||
final bool isAndroid = _platform.isAndroid;
|
||||
final bool isWindows = _platform.isWindows;
|
||||
|
||||
if (isiOS || isMacOS) {
|
||||
await _channel.invokeMethod(
|
||||
'openStoreListing',
|
||||
ArgumentError.checkNotNull(appStoreId, 'appStoreId'),
|
||||
);
|
||||
} else if (isAndroid) {
|
||||
await _channel.invokeMethod('openStoreListing');
|
||||
} else if (isWindows) {
|
||||
ArgumentError.checkNotNull(microsoftStoreId, 'microsoftStoreId');
|
||||
await _launchUrl(
|
||||
'ms-windows-store://review/?ProductId=$microsoftStoreId',
|
||||
);
|
||||
} else {
|
||||
throw UnsupportedError(
|
||||
'Platform(${_platform.operatingSystem}) not supported',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _launchUrl(String url) async {
|
||||
if (!await canLaunchUrlString(url)) return;
|
||||
await launchUrlString(url, mode: LaunchMode.externalNonBrowserApplication);
|
||||
}
|
||||
}
|
24
components/in_app_review_platform_interface/pubspec.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
name: in_app_review_platform_interface
|
||||
description: A common platform interface for the in_app_review plugin.
|
||||
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
|
||||
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
|
||||
version: 2.0.4
|
||||
homepage: https://github.com/britannio/in_app_review/tree/master/in_app_review_platform_interface
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
flutter: ">=2.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
url_launcher: ^6.1.0
|
||||
plugin_platform_interface: ^2.0.0
|
||||
platform: ^3.0.0
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
@ -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
fastlane/metadata/android/en-US/changelogs/108.txt
Normal file
@ -0,0 +1 @@
|
||||
- Navigation shortcuts.
|
1
fastlane/metadata/android/en-US/changelogs/121.txt
Normal file
@ -0,0 +1 @@
|
||||
- Ability to mark a story as read once scrolling past.
|
2
fastlane/metadata/android/en-US/changelogs/125.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Ability to customize text scale factor.
|
||||
- Ability to customize app's accent color.
|
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.
|
5
fastlane/metadata/android/en-US/changelogs/91.txt
Normal file
@ -0,0 +1,5 @@
|
||||
- Customization of tab bar.
|
||||
- Option to enable swipe gesture for switching between tabs.
|
||||
- Access to action menu from home screen.
|
||||
- Access to Wikipedia and Wiktionary from text selection toolbar.
|
||||
- Quotes and emphasis rendering.
|
5
fastlane/metadata/android/en-US/changelogs/92.txt
Normal file
@ -0,0 +1,5 @@
|
||||
- Customization of tab bar.
|
||||
- Option to enable swipe gesture for switching between tabs.
|
||||
- Access to action menu from home screen.
|
||||
- Access to Wikipedia and Wiktionary from text selection toolbar.
|
||||
- Quotes and emphasis rendering.
|
@ -1,46 +0,0 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hacki/main.dart' as app;
|
||||
import 'package:hacki/screens/widgets/story_tile.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('performance test', () {
|
||||
testWidgets('scrolling performance on ItemScreen',
|
||||
(WidgetTester tester) async {
|
||||
await app.main(testing: true);
|
||||
await tester.pump();
|
||||
|
||||
final Finder bestStoryTabFinder = find.text('BEST');
|
||||
|
||||
await tester.tap(bestStoryTabFinder);
|
||||
await tester.pumpAndSettle(const Duration(seconds: 3));
|
||||
|
||||
final Finder storyTileFinder = find.byType(StoryTile);
|
||||
|
||||
await tester.tap(storyTileFinder.first);
|
||||
await tester.pumpAndSettle(const Duration(seconds: 3));
|
||||
|
||||
TestGesture gesture = await tester.startGesture(const Offset(0, 300));
|
||||
await gesture.moveBy(const Offset(0, -300));
|
||||
await tester.pump();
|
||||
|
||||
gesture = await tester.startGesture(const Offset(0, 300));
|
||||
await gesture.moveBy(const Offset(0, -300));
|
||||
await tester.pump();
|
||||
|
||||
gesture = await tester.startGesture(const Offset(0, 300));
|
||||
await gesture.moveBy(const Offset(0, -300));
|
||||
await tester.pump();
|
||||
|
||||
gesture = await tester.startGesture(const Offset(0, 300));
|
||||
await gesture.moveBy(const Offset(0, 900));
|
||||
await tester.pump();
|
||||
|
||||
gesture = await tester.startGesture(const Offset(0, 300));
|
||||
await gesture.moveBy(const Offset(0, -900));
|
||||
await tester.pump();
|
||||
});
|
||||
});
|
||||
}
|
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
|
||||
|
@ -2,7 +2,11 @@ PODS:
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- ReachabilitySwift
|
||||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_email_sender (0.0.1):
|
||||
- Flutter
|
||||
- flutter_inappwebview (0.0.1):
|
||||
- Flutter
|
||||
- flutter_inappwebview/Core (= 0.0.1)
|
||||
@ -12,26 +16,36 @@ 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)
|
||||
- in_app_review (0.2.0):
|
||||
- Flutter
|
||||
- integration_test (0.0.1):
|
||||
- Flutter
|
||||
- MTBBarcodeScanner (5.0.11)
|
||||
- OrderedSet (5.0.0)
|
||||
- path_provider_ios (0.0.1):
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- qr_code_scanner (0.2.0):
|
||||
- Flutter
|
||||
- MTBBarcodeScanner
|
||||
- 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
|
||||
- sqflite (0.0.2):
|
||||
- FlutterMacOS
|
||||
- sqflite (0.0.3):
|
||||
- Flutter
|
||||
- FMDB (>= 2.7.5)
|
||||
- synced_shared_preferences (0.0.1):
|
||||
@ -47,16 +61,21 @@ PODS:
|
||||
|
||||
DEPENDENCIES:
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_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`)
|
||||
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
||||
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
||||
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/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/darwin`)
|
||||
- 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`)
|
||||
@ -67,14 +86,19 @@ DEPENDENCIES:
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- FMDB
|
||||
- MTBBarcodeScanner
|
||||
- OrderedSet
|
||||
- ReachabilitySwift
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
connectivity_plus:
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
device_info_plus:
|
||||
:path: ".symlinks/plugins/device_info_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,16 +107,22 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
flutter_siri_suggestions:
|
||||
:path: ".symlinks/plugins/flutter_siri_suggestions/ios"
|
||||
in_app_review:
|
||||
:path: ".symlinks/plugins/in_app_review/ios"
|
||||
integration_test:
|
||||
:path: ".symlinks/plugins/integration_test/ios"
|
||||
path_provider_ios:
|
||||
:path: ".symlinks/plugins/path_provider_ios/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
qr_code_scanner:
|
||||
:path: ".symlinks/plugins/qr_code_scanner/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/darwin"
|
||||
sqflite:
|
||||
:path: ".symlinks/plugins/sqflite/ios"
|
||||
synced_shared_preferences:
|
||||
@ -107,27 +137,33 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/workmanager/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
|
||||
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
|
||||
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
|
||||
flutter_inappwebview: 3d32228f1304635e7c028b0d4252937730bbc6cf
|
||||
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
|
||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
||||
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||
flutter_siri_suggestions: 226fb7ef33d25d3fe0d4aa2a8bcf4b72730c466f
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5
|
||||
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
|
||||
integration_test: 13825b8a9334a850581300559b8839134b124670
|
||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
|
||||
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1
|
||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
|
||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
||||
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
|
||||
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||
synced_shared_preferences: f722742b06d65c7315b8e9f56b794c9fbd5597f7
|
||||
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
|
||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||
webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f
|
||||
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
|
||||
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
|
||||
|
||||
PODFILE CHECKSUM: e4c97c7a9aacaeda4b952f7ef9ea29e47660f622
|
||||
PODFILE CHECKSUM: d28e9a1c7bee335d05ddd795703aad5bf05bb937
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
COCOAPODS: 1.13.0
|
||||
|
@ -3,13 +3,14 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 51;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
7A6CD5D595D5F4E8710804C0 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BF0A917F40A838BF30D8F4C /* Pods_Runner.framework */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
@ -21,9 +22,7 @@
|
||||
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 */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -56,7 +55,7 @@
|
||||
};
|
||||
E51D52B8283B464E00FC8DD8 /* Embed App Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
buildActionMask = 8;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
@ -64,19 +63,19 @@
|
||||
E530B1B4283B54DA004E8EB6 /* Action Extension.appex in Embed App Extensions */,
|
||||
);
|
||||
name = "Embed App Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
027B292CC58CF92F11FC0A69 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
0E63A5CE3FDBCCD054072136 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
4449F5D4D39C23F292D07005 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
8BF0A917F40A838BF30D8F4C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -84,8 +83,8 @@
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
BFB5AA41D6C22D228077D166 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DF5D5FFF325B7D5DFEE88A3F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
B9EC882BDD04A309C317E416 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
D73EA9FA5E6F35364DCA0CD1 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
E51D52AD283B464E00FC8DD8 /* Share Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Share Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E51D52AF283B464E00FC8DD8 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||
E51D52B2283B464E00FC8DD8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||
@ -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>"; };
|
||||
@ -109,7 +107,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E575B6F127EBC6DB002B1508 /* CloudKit.framework in Frameworks */,
|
||||
FC507E94AA7767C155787DB3 /* Pods_Runner.framework in Frameworks */,
|
||||
7A6CD5D595D5F4E8710804C0 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -177,7 +175,6 @@
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
E54B4752282B3B8900579261 /* HackiCore.swift */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
@ -186,8 +183,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E575B6F027EBC6DA002B1508 /* CloudKit.framework */,
|
||||
BFB5AA41D6C22D228077D166 /* Pods_Runner.framework */,
|
||||
E530B1A7283B54DA004E8EB6 /* UniformTypeIdentifiers.framework */,
|
||||
8BF0A917F40A838BF30D8F4C /* Pods_Runner.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@ -195,9 +192,9 @@
|
||||
D79CD63C88FF49EF451AFDDF /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DF5D5FFF325B7D5DFEE88A3F /* Pods-Runner.debug.xcconfig */,
|
||||
4449F5D4D39C23F292D07005 /* Pods-Runner.release.xcconfig */,
|
||||
027B292CC58CF92F11FC0A69 /* Pods-Runner.profile.xcconfig */,
|
||||
0E63A5CE3FDBCCD054072136 /* Pods-Runner.debug.xcconfig */,
|
||||
D73EA9FA5E6F35364DCA0CD1 /* Pods-Runner.release.xcconfig */,
|
||||
B9EC882BDD04A309C317E416 /* Pods-Runner.profile.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
@ -232,15 +229,15 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
41DC8215F9CFD708C36ECBA8 /* [CP] Check Pods Manifest.lock */,
|
||||
E2E6E097A94005D9196D0A71 /* [CP] Check Pods Manifest.lock */,
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
7714A105B2069B720D0DF18E /* [CP] Embed Pods Frameworks */,
|
||||
E51D52B8283B464E00FC8DD8 /* Embed App Extensions */,
|
||||
F1959755D5521D58CA193498 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -294,7 +291,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1330;
|
||||
LastUpgradeCheck = 1300;
|
||||
LastUpgradeCheck = 1430;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
97C146ED1CF9000F007C117D = {
|
||||
@ -363,10 +360,12 @@
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
|
||||
);
|
||||
name = "Thin Binary";
|
||||
outputPaths = (
|
||||
@ -375,7 +374,22 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||
};
|
||||
41DC8215F9CFD708C36ECBA8 /* [CP] Check Pods Manifest.lock */ = {
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
E2E6E097A94005D9196D0A71 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@ -397,7 +411,7 @@
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
7714A105B2069B720D0DF18E /* [CP] Embed Pods Frameworks */ = {
|
||||
F1959755D5521D58CA193498 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@ -414,20 +428,6 @@
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@ -437,7 +437,6 @@
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
E54B4753282B3B8900579261 /* HackiCore.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -549,7 +548,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;
|
||||
@ -568,16 +567,18 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 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.28;
|
||||
MARKETING_VERSION = 0.3.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -635,7 +636,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 +685,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;
|
||||
@ -705,16 +706,18 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 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.28;
|
||||
MARKETING_VERSION = 0.3.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -735,20 +738,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.28;
|
||||
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";
|
||||
@ -768,7 +776,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Share Extension/Share Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@ -786,6 +794,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jiaqi.hacki.Share-Extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@ -806,9 +815,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 +835,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;
|
||||
@ -843,7 +856,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Share Extension/Share Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@ -860,6 +873,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jiaqi.hacki.Share-Extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -881,7 +895,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Action Extension/Action Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@ -899,6 +913,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jiaqi.hacki.Action-Extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@ -921,9 +936,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 +956,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;
|
||||
@ -960,7 +979,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Action Extension/Action Extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@ -977,6 +996,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.jiaqi.hacki.Action-Extension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1300"
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -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,10 +16,10 @@ import flutter_local_notifications
|
||||
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.delegate = self
|
||||
|
||||
HackiCore.start()
|
||||
|
||||
WorkmanagerPlugin.register(with: self.registrar(forPlugin: "be.tramckrijte.workmanager.WorkmanagerPlugin")!)
|
||||
|
||||
WorkmanagerPlugin.registerTask(withIdentifier: "workmanager.background.task")
|
||||
|
||||
if #available(iOS 10.0, *) {
|
||||
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
|
||||
@ -28,8 +28,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,13 @@
|
||||
<array>
|
||||
<string>applinks:example.com</string>
|
||||
</array>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app needs camera access to scan QR codes</string>
|
||||
<key>io.flutter.embedded_views_preview</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(
|
||||
"/Users/runner/work/Hacki/Hacki/submodules/flutter/bin/flutter", "build", "ios", "--config-only",
|
||||
"--release", "--no-pub", "--no-codesign",
|
||||
"--build-number", new_build_number.to_s
|
||||
)
|
||||
|
||||
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);
|
||||
@ -41,20 +41,25 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
await _authRepository.loggedIn.then((bool loggedIn) async {
|
||||
if (loggedIn) {
|
||||
final String? username = await _authRepository.username;
|
||||
final User user =
|
||||
await _storiesRepository.fetchUserBy(userId: username!);
|
||||
User? user = await _storiesRepository.fetchUser(id: username!);
|
||||
|
||||
/// According to Hacker News' API documentation,
|
||||
/// if user has no public activity (posting a comment or story),
|
||||
/// then it will not be available from the API.
|
||||
user ??= User.emptyWithId(username);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
isLoggedIn: true,
|
||||
user: user,
|
||||
status: Status.success,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: AuthStatus.loaded,
|
||||
isLoggedIn: false,
|
||||
status: Status.success,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -76,7 +81,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
}
|
||||
|
||||
Future<void> onLogin(AuthLogin event, Emitter<AuthState> emit) async {
|
||||
emit(state.copyWith(status: AuthStatus.loading));
|
||||
emit(state.copyWith(status: Status.inProgress));
|
||||
|
||||
final bool successful = await _authRepository.login(
|
||||
username: event.username,
|
||||
@ -84,24 +89,23 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
);
|
||||
|
||||
if (successful) {
|
||||
final User user =
|
||||
await _storiesRepository.fetchUserBy(userId: event.username);
|
||||
final User? user = await _storiesRepository.fetchUser(id: event.username);
|
||||
emit(
|
||||
state.copyWith(
|
||||
user: user,
|
||||
user: user ?? User.emptyWithId(event.username),
|
||||
isLoggedIn: true,
|
||||
status: AuthStatus.loaded,
|
||||
status: Status.success,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
emit(state.copyWith(status: AuthStatus.failure));
|
||||
emit(state.copyWith(status: Status.failure));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onLogout(AuthLogout event, Emitter<AuthState> emit) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
user: User.empty(),
|
||||
user: const User.empty(),
|
||||
isLoggedIn: false,
|
||||
agreedToEULA: false,
|
||||
),
|
||||
|
@ -1,11 +1,5 @@
|
||||
part of 'auth_bloc.dart';
|
||||
|
||||
enum AuthStatus {
|
||||
loading,
|
||||
loaded,
|
||||
failure,
|
||||
}
|
||||
|
||||
class AuthState extends Equatable {
|
||||
const AuthState({
|
||||
required this.user,
|
||||
@ -14,16 +8,16 @@ class AuthState extends Equatable {
|
||||
required this.agreedToEULA,
|
||||
});
|
||||
|
||||
AuthState.init()
|
||||
: user = User.empty(),
|
||||
const AuthState.init()
|
||||
: user = const User.empty(),
|
||||
isLoggedIn = false,
|
||||
status = AuthStatus.loaded,
|
||||
status = Status.success,
|
||||
agreedToEULA = false;
|
||||
|
||||
final User user;
|
||||
final bool isLoggedIn;
|
||||
final bool agreedToEULA;
|
||||
final AuthStatus status;
|
||||
final Status status;
|
||||
|
||||
String get username => user.id;
|
||||
|
||||
@ -31,7 +25,7 @@ class AuthState extends Equatable {
|
||||
User? user,
|
||||
bool? isLoggedIn,
|
||||
bool? agreedToEULA,
|
||||
AuthStatus? status,
|
||||
Status? status,
|
||||
}) {
|
||||
return AuthState(
|
||||
user: user ?? this.user,
|
||||
|