diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index 44119b5..d80afae 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -188,11 +188,14 @@ jobs: - name: Sign APK (Android) if: steps.check_asset.outputs.skip_build != 'true' && ( matrix.config.target == 'android-arm32' || matrix.config.target == 'android-arm64' || matrix.config.target == 'android-x86_64' ) - env: - KEY_FILE_BASE64: ${{ secrets.KEY_FILE_BASE64 }} - KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} - run: | - sh scripts/sign-apk-github-actions.sh + uses: r0adkll/sign-android-release@v1 + id: sign_app + with: + releaseDirectory: build/app/outputs/flutter-apk + signingKeyBase64: ${{ secrets.SIGN_FILE_BASE64 }} + alias: ${{ secrets.KEY_ALIAS }} + keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }} + keyPassword: ${{ secrets.KEY_PASSWORD }} - name: Upload Asset (All) if: steps.check_asset.outputs.skip_build != 'true' diff --git a/README.md b/README.md index 1eb0091..f60cc0e 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,6 @@ VPN->代理->分流, 这三个功能如果同时设置, 您会在您手机的VPN - 在 macos 数据文件将会"~/Library/Application Support/pikapika" - 在 linux 数据文件将会"~/.pikapika" - ## 运行 / 构建 这个应用程序使用golang和dart(flutter)作为主要语言, 可以兼容Windows, linux, MacOS, Android, IOS @@ -75,6 +74,10 @@ VPN->代理->分流, 这三个功能如果同时设置, 您会在您手机的VPN ![平台](images/platforms.png) +### 开发环境准备 + +- [golang](https://golang.org/) (1.16以上版本) +- [flutter](https://flutter.dev/) (stable-2.10.3) ## 请您遵守使用规则 @@ -85,3 +88,9 @@ VPN->代理->分流, 这三个功能如果同时设置, 您会在您手机的VPN - 使用本软件进行继续开发形成的软件。 - 引入本软件部分内容为依赖/参考本软件/使用本软件内代码的同时, 包含本软件内一致内容或功能的软件。 - 直接对本软件进行打包发布 + +软件副本分发规则 + +- 不要在任何其他 **二次元软件** 的 **聊天社区** 或 **开发社区** 内, 发布有关本软件的链接或信息 +- 尽可能 **不要** 发送本软件安装包到任何社区内, 即不要将APK/IPA/ZIP/DMG发送包括任何聊天软件内的群聊功能中, +- 若您有意分享本软件的副本, 应使用私聊窗口发送, 或粘贴github中releases页面的链接. diff --git a/ci/cmd/check_asset/main.go b/ci/cmd/check_asset/main.go index 33c86e2..eb57a31 100644 --- a/ci/cmd/check_asset/main.go +++ b/ci/cmd/check_asset/main.go @@ -1,13 +1,13 @@ package main import ( - "ci/commons" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "os" - "strings" + "ci/commons" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "strings" ) const owner = "niuhuan" @@ -15,82 +15,82 @@ const repo = "pikapika" const ua = "niuhuan pikapika ci" func main() { - // get ghToken - ghToken := os.Getenv("GH_TOKEN") - if ghToken == "" { - println("Env ${GH_TOKEN} is not set") - os.Exit(1) - } - // get version - var version commons.Version - codeFile, err := ioutil.ReadFile("version.code.txt") - if err != nil { - panic(err) - } - version.Code = strings.TrimSpace(string(codeFile)) - infoFile, err := ioutil.ReadFile("version.info.txt") - if err != nil { - panic(err) - } - version.Info = strings.TrimSpace(string(infoFile)) - // get target - target := os.Getenv("TARGET") - if ghToken == "" { - println("Env ${TARGET} is not set") - os.Exit(1) - } - // - var releaseFileName string - switch target { - case "macos": - releaseFileName = fmt.Sprintf("pikapika-%v-macos-intel.dmg", version.Code) - case "ios": - releaseFileName = fmt.Sprintf("pikapika-%v-ios-nosign.ipa", version.Code) - case "windows": - releaseFileName = fmt.Sprintf("pikapika-%v-windows-x86_64.zip", version.Code) - case "linux": - releaseFileName = fmt.Sprintf("pikapika-%v-linux-x86_64.AppImage", version.Code) - case "android-arm32": - releaseFileName = fmt.Sprintf("pikapika-%v-android-arm32.apk", version.Code) - case "android-arm64": - releaseFileName = fmt.Sprintf("pikapika-%v-android-arm64.apk", version.Code) - case "android-x86_64": - releaseFileName = fmt.Sprintf("pikapika-%v-android-x86_64.apk", version.Code) - } - // get version - getReleaseRequest, err := http.NewRequest( - "GET", - fmt.Sprintf("https://api.github.com/repos/%v/%v/releases/tags/%v", owner, repo, version.Code), - nil, - ) - if err != nil { - panic(err) - } - getReleaseRequest.Header.Set("User-Agent", ua) - getReleaseRequest.Header.Set("Authorization", ghToken) - getReleaseResponse, err := http.DefaultClient.Do(getReleaseRequest) - if err != nil { - panic(err) - } - defer getReleaseResponse.Body.Close() - if getReleaseResponse.StatusCode == 404 { - panic("NOT FOUND RELEASE") - } - buff, err := ioutil.ReadAll(getReleaseResponse.Body) - if err != nil { - panic(err) - } - var release commons.Release - err = json.Unmarshal(buff, &release) - if err != nil { - println(string(buff)) - panic(err) - } - for _, asset := range release.Assets { - if asset.Name == releaseFileName { - println("::set-output name=skip_build::true") - os.Exit(0) - } - } - print("::set-output name=skip_build::false") + // get ghToken + ghToken := os.Getenv("GH_TOKEN") + if ghToken == "" { + println("Env ${GH_TOKEN} is not set") + os.Exit(1) + } + // get version + var version commons.Version + codeFile, err := ioutil.ReadFile("version.code.txt") + if err != nil { + panic(err) + } + version.Code = strings.TrimSpace(string(codeFile)) + infoFile, err := ioutil.ReadFile("version.info.txt") + if err != nil { + panic(err) + } + version.Info = strings.TrimSpace(string(infoFile)) + // get target + target := os.Getenv("TARGET") + if ghToken == "" { + println("Env ${TARGET} is not set") + os.Exit(1) + } + // + var releaseFileName string + switch target { + case "macos": + releaseFileName = fmt.Sprintf("pikapika-%v-macos-intel.dmg", version.Code) + case "ios": + releaseFileName = fmt.Sprintf("pikapika-%v-ios-nosign.ipa", version.Code) + case "windows": + releaseFileName = fmt.Sprintf("pikapika-%v-windows-x86_64.zip", version.Code) + case "linux": + releaseFileName = fmt.Sprintf("pikapika-%v-linux-x86_64.AppImage", version.Code) + case "android-arm32": + releaseFileName = fmt.Sprintf("pikapika-%v-android-arm32.apk", version.Code) + case "android-arm64": + releaseFileName = fmt.Sprintf("pikapika-%v-android-arm64.apk", version.Code) + case "android-x86_64": + releaseFileName = fmt.Sprintf("pikapika-%v-android-x86_64.apk", version.Code) + } + // get version + getReleaseRequest, err := http.NewRequest( + "GET", + fmt.Sprintf("https://api.github.com/repos/%v/%v/releases/tags/%v", owner, repo, version.Code), + nil, + ) + if err != nil { + panic(err) + } + getReleaseRequest.Header.Set("User-Agent", ua) + getReleaseRequest.Header.Set("Authorization", "token "+ghToken) + getReleaseResponse, err := http.DefaultClient.Do(getReleaseRequest) + if err != nil { + panic(err) + } + defer getReleaseResponse.Body.Close() + if getReleaseResponse.StatusCode == 404 { + panic("NOT FOUND RELEASE") + } + buff, err := ioutil.ReadAll(getReleaseResponse.Body) + if err != nil { + panic(err) + } + var release commons.Release + err = json.Unmarshal(buff, &release) + if err != nil { + println(string(buff)) + panic(err) + } + for _, asset := range release.Assets { + if asset.Name == releaseFileName { + println("::set-output name=skip_build::true") + os.Exit(0) + } + } + print("::set-output name=skip_build::false") } diff --git a/ci/cmd/check_release/main.go b/ci/cmd/check_release/main.go index 81bf461..5e653b9 100644 --- a/ci/cmd/check_release/main.go +++ b/ci/cmd/check_release/main.go @@ -45,7 +45,7 @@ func main() { panic(nil) } getReleaseRequest.Header.Set("User-Agent", ua) - getReleaseRequest.Header.Set("Authorization", ghToken) + getReleaseRequest.Header.Set("Authorization", "token "+ghToken) getReleaseResponse, err := http.DefaultClient.Do(getReleaseRequest) if err != nil { panic(nil) @@ -70,7 +70,7 @@ func main() { panic(nil) } createReleaseRequest.Header.Set("User-Agent", ua) - createReleaseRequest.Header.Set("Authorization", ghToken) + createReleaseRequest.Header.Set("Authorization", "token "+ghToken) var createReleaseResponse *http.Response createReleaseResponse, err = http.DefaultClient.Do(createReleaseRequest) if err != nil { diff --git a/ci/cmd/upload_asset/main.go b/ci/cmd/upload_asset/main.go index 8cbe062..65296ea 100644 --- a/ci/cmd/upload_asset/main.go +++ b/ci/cmd/upload_asset/main.go @@ -91,7 +91,7 @@ func main() { panic(err) } getReleaseRequest.Header.Set("User-Agent", ua) - getReleaseRequest.Header.Set("Authorization", ghToken) + getReleaseRequest.Header.Set("Authorization", "token "+ghToken) getReleaseResponse, err := http.DefaultClient.Do(getReleaseRequest) if err != nil { panic(err) @@ -121,7 +121,7 @@ func main() { panic(err) } uploadRequest.Header.Set("User-Agent", ua) - uploadRequest.Header.Set("Authorization", ghToken) + uploadRequest.Header.Set("Authorization", "token "+ghToken) uploadRequest.Header.Set("Content-Type", contentType) uploadRequest.ContentLength = contentLength uploadResponse, err := http.DefaultClient.Do(uploadRequest) diff --git a/ci/version.code.txt b/ci/version.code.txt index 75e9e45..44c3405 100644 --- a/ci/version.code.txt +++ b/ci/version.code.txt @@ -1 +1 @@ -v1.4.5 \ No newline at end of file +v1.4.4 \ No newline at end of file diff --git a/ci/version.info.txt b/ci/version.info.txt index 84a5dd8..c9eba11 100644 --- a/ci/version.info.txt +++ b/ci/version.info.txt @@ -1,2 +1,2 @@ -- [x] 修复签名不兼容的问题 -- [x] 修复下一章不显示的问题 +- [x] 大部分页面已经支持鼠标右键退回上一页的功能, 但是需要从设置中开启 (并不是所有人都需要) +- [x] 列表中标记出看过的漫画 diff --git a/go/.gitignore b/go/.gitignore index 460ee0c..596bcc6 100644 --- a/go/.gitignore +++ b/go/.gitignore @@ -2,4 +2,3 @@ build .last_goflutter_check .last_go-flutter_check .last_go-flutter_check -.last_go-flutter_check diff --git a/go/go.mod b/go/go.mod index 518bbce..e2a7248 100644 --- a/go/go.mod +++ b/go/go.mod @@ -9,10 +9,11 @@ require ( github.com/go-gl/glfw/v3.3/glfw v0.0.0-20201108214237-06ea97f0c265 github.com/miguelpruivo/flutter_file_picker/go v0.0.0-20210622152105-9f0a811028a0 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/niuhuan/pica-go v0.0.0-20220224154849-76bf750f8c4d + github.com/niuhuan/pica-go v0.0.0-20220415110443-b5c4a97b0103 github.com/pkg/errors v0.9.1 golang.org/x/image v0.0.0-20190802002840-cff245a6509b - golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f + golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13 // indirect + golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 // indirect golang.org/x/text v0.3.7 // indirect gorm.io/driver/sqlite v1.1.4 diff --git a/go/go.sum b/go/go.sum index 8076d9b..9dbc2de 100644 --- a/go/go.sum +++ b/go/go.sum @@ -1,3 +1,4 @@ +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/PuerkitoBio/goquery v1.7.1 h1:oE+T06D+1T7LNrn91B4aERsRIeCLJ/oPSa6xB9FPnz4= github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY= github.com/Xuanwo/go-locale v1.0.0 h1:oqC32Kyiu2XZq+fxtwEg0mWiv9WyDhyHu+sT5cDkgME= @@ -44,6 +45,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niuhuan/pica-go v0.0.0-20220224154849-76bf750f8c4d h1:f3V6V1Y+5j/AvhsIGA6aQ/K2Ez1AeYbuCG9uI4fGC6M= github.com/niuhuan/pica-go v0.0.0-20220224154849-76bf750f8c4d/go.mod h1:r76zBgH9AYkv0ptyEVoPUIdt33sT0Ts7xgcg742OZtw= +github.com/niuhuan/pica-go v0.0.0-20220415110443-b5c4a97b0103 h1:Qc40/rqbtY0fXAlPEzlfBdQk7wIHTjcTmejmBvdKeco= +github.com/niuhuan/pica-go v0.0.0-20220415110443-b5c4a97b0103/go.mod h1:r76zBgH9AYkv0ptyEVoPUIdt33sT0Ts7xgcg742OZtw= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -58,18 +61,42 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20220224134551-8a0a1e50732f h1:G/wQ/Mbs60nXhRM80J4DOzy7FEIZjNprzOneArSgOl0= +golang.org/x/mobile v0.0.0-20220224134551-8a0a1e50732f/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= +golang.org/x/mobile v0.0.0-20220307220422-55113b94f09c h1:9J0m/JcA5YXYbamDhF5I3T7cJnR7V75OCLnMCPb5gl4= +golang.org/x/mobile v0.0.0-20220307220422-55113b94f09c/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= +golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 h1:ZDL7hDvJEQEcHVkoZawKmRUgbqn1pOIzb8EinBh5csU= +golang.org/x/mobile v0.0.0-20220325161704-447654d348e3/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= +golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13 h1:C4eR3yV9J3RkP8WKkcQzpcPyr/foOcUtxW72DvJEOlI= +golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200802091954-4b90ce9b60b3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 h1:BXxu8t6QN0G1uff4bzZzSkpsax8+ALqTGUtz08QrV00= golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -80,7 +107,15 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098 h1:YuekqPskqwCCPM79F1X5Dhv4ezTCj+Ki1oNwiafxkA0= +golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/go/pikapika/client.go b/go/pikapika/client.go index ee1f139..681520e 100644 --- a/go/pikapika/client.go +++ b/go/pikapika/client.go @@ -138,6 +138,24 @@ func categories() (string, error) { return cache, nil } + +func collections(_ string) (string, error) { + key := "COLLECTIONS" + expire := time.Hour * 3 + cache := network_cache.LoadCache(key, expire) + if cache != "" { + return cache, nil + } + collections, err := client.Collections() + if err != nil { + return "", err + } + buff, _ := json.Marshal(&collections) + cache = string(buff) + network_cache.SaveCache(key, cache) + return cache, nil +} + func comics(params string) (string, error) { var paramsStruct struct { Category string `json:"category"` diff --git a/go/pikapika/pikapika.go b/go/pikapika/pikapika.go index 58ee1e7..b1436de 100644 --- a/go/pikapika/pikapika.go +++ b/go/pikapika/pikapika.go @@ -748,6 +748,8 @@ func FlatInvoke(method string, params string) (string, error) { return defaultHttpClientGet(params) case "loadViewedList": return loadViewedList(params) + case "collections": + return collections(params) } return "", errors.New("method not found : " + method) } diff --git a/images/ic.png b/images/ic.png new file mode 100644 index 0000000..f745851 Binary files /dev/null and b/images/ic.png differ diff --git a/lib/basic/Entities.dart b/lib/basic/Entities.dart index fed8a12..ee66e1a 100644 --- a/lib/basic/Entities.dart +++ b/lib/basic/Entities.dart @@ -674,3 +674,16 @@ class GameCommentChild extends ChildOfComment { this.game = json["_game"]; } } + +class Collection { + late String title; + late List comics; + + Collection.fromJson(Map json) { + this.title = json["title"]; + this.comics = List.from(json["comics"]) + .map((e) => Map.from(e)) + .map((e) => ComicSimple.fromJson(e)) + .toList(); + } +} diff --git a/lib/basic/Method.dart b/lib/basic/Method.dart index 357d4f3..6e2ee33 100644 --- a/lib/basic/Method.dart +++ b/lib/basic/Method.dart @@ -716,4 +716,10 @@ class Method { return List.of(jsonDecode(await _flatInvoke("loadViewedList", list))) .cast(); } + + Future> collections() async { + String rsp = await _flatInvoke("collections", ""); + List list = json.decode(rsp); + return list.map((e) => Collection.fromJson(e)).toList(); + } } diff --git a/lib/basic/config/FullScreenAction.dart b/lib/basic/config/FullScreenAction.dart index ed9fba6..6e58363 100644 --- a/lib/basic/config/FullScreenAction.dart +++ b/lib/basic/config/FullScreenAction.dart @@ -18,7 +18,7 @@ Map _fullScreenActionMap = { "使用控制器全屏": FullScreenAction.CONTROLLER, "双击屏幕全屏": FullScreenAction.TOUCH_DOUBLE, "双击屏幕全屏 + 单击屏幕下一页": FullScreenAction.TOUCH_DOUBLE_ONCE_NEXT, - "将屏幕划分成三个区域 (上一页, 下一页, 全屏) (不能使用快捷下一页按钮)": FullScreenAction.THREE_AREA, + "将屏幕划分成三个区域 (上一页, 下一页, 全屏)": FullScreenAction.THREE_AREA, }; const _defaultController = FullScreenAction.TOUCH_ONCE; diff --git a/lib/screens/CategoriesScreen.dart b/lib/screens/CategoriesScreen.dart index 96ca3d9..8038da1 100644 --- a/lib/screens/CategoriesScreen.dart +++ b/lib/screens/CategoriesScreen.dart @@ -6,6 +6,7 @@ import 'package:pikapika/basic/config/ShadowCategoriesEvent.dart'; import 'package:pikapika/basic/config/shadowCategoriesMode.dart'; import 'package:pikapika/basic/store/Categories.dart'; import 'package:pikapika/basic/config/ShadowCategories.dart'; +import 'package:pikapika/screens/ComicCollectionsScreen.dart'; import 'package:pikapika/screens/RankingsScreen.dart'; import 'package:pikapika/screens/SearchScreen.dart'; import 'package:pikapika/screens/components/ContentError.dart'; @@ -182,6 +183,23 @@ class _CategoriesScreenState extends State { () => _navigateToCategory(null), ); + append( + Icon( + Icons.recommend_outlined, + size: imageSize, + color: Colors.grey, + ), + "推荐", + () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ComicCollectionsScreen(), + ), + ); + }, + ); + for (var i = 0; i < cList.length; i++) { var c = cList[i]; if (c.isWeb) continue; diff --git a/lib/screens/ComicCollectionsScreen.dart b/lib/screens/ComicCollectionsScreen.dart new file mode 100644 index 0000000..4c3d5f0 --- /dev/null +++ b/lib/screens/ComicCollectionsScreen.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:pikapika/basic/Entities.dart'; +import 'package:pikapika/basic/Method.dart'; +import 'package:pikapika/screens/components/ComicList.dart'; +import 'package:pikapika/screens/components/ContentBuilder.dart'; +import 'package:pikapika/screens/components/ContentMessage.dart'; + +import 'components/RightClickPop.dart'; + +class ComicCollectionsScreen extends StatefulWidget { + const ComicCollectionsScreen({Key? key}) : super(key: key); + + @override + State createState() => _ComicCollectionsScreenState(); +} + +class _ComicCollectionsScreenState extends State { + late Future> _future; + + @override + void initState() { + _future = method.collections(); + super.initState(); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return rightClickPop( + child: buildScreen(context), + context: context, + canPop: true, + ); + } + + Widget buildScreen(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("推荐")), + body: ContentBuilder( + future: _future, + onRefresh: () async { + setState(() { + _future = method.collections(); + }); + }, + successBuilder: ( + BuildContext context, + AsyncSnapshot> snapshot, + ) { + final collection = snapshot.requireData; + if (collection.isEmpty) { + return ContentMessage( + message: "这里没有资源呀", + icon: Icons.no_sim_outlined, + onRefresh: () async { + setState(() { + _future = method.collections(); + }); + }, + ); + } + final ThemeData theme = Theme.of(context); + final AppBarTheme appBarTheme = AppBarTheme.of(context); + return DefaultTabController( + length: collection.length, + child: Scaffold( + appBar: PreferredSizeContainer( + color: appBarTheme.backgroundColor, + child: TabBar( + indicatorColor: theme.dividerColor, + tabs: collection.map((e) => Tab(text: e.title)).toList(), + ), + ), + body: TabBarView( + children: collection.map((e) => ComicList(e.comics)).toList(), + ), + ), + ); + }, + ), + ); + } +} + +class PreferredSizeContainer extends StatelessWidget + implements PreferredSizeWidget { + final PreferredSizeWidget child; + final Color? color; + + const PreferredSizeContainer({ + required this.child, + this.color, + Key? key, + }) : super(key: key); + + @override + Size get preferredSize => child.preferredSize; + + @override + Widget build(BuildContext context) { + return Container( + color: color, + child: child, + ); + } +} diff --git a/lib/screens/components/ContentMessage.dart b/lib/screens/components/ContentMessage.dart new file mode 100644 index 0000000..b5cad79 --- /dev/null +++ b/lib/screens/components/ContentMessage.dart @@ -0,0 +1,132 @@ +import 'package:flutter/material.dart'; + +import '../../basic/config/ContentFailedReloadAction.dart'; + +class ContentMessage extends StatelessWidget { + final RefreshCallback? onRefresh; + final IconData icon; + final String message; + + const ContentMessage({ + required this.message, + required this.icon, + this.onRefresh, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + if (onRefresh != null) { + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + var width = constraints.maxWidth; + var height = constraints.maxHeight; + var min = width < height ? width : height; + var iconSize = min / 2.3; + var textSize = min / 16; + var tipSize = min / 20; + var infoSize = min / 30; + if (contentFailedReloadAction == + ContentFailedReloadAction.TOUCH_LOADER) { + return GestureDetector( + onTap: () { + onRefresh!(); + }, + child: ListView( + children: [ + SizedBox( + height: height, + child: Column( + children: [ + Expanded(child: Container()), + Icon( + icon, + size: iconSize, + color: Colors.grey.shade600, + ), + Container(height: min / 10), + Container( + padding: const EdgeInsets.only( + left: 30, + right: 30, + ), + child: Text( + message, + style: TextStyle(fontSize: textSize), + textAlign: TextAlign.center, + ), + ), + Text('(点击刷新)', style: TextStyle(fontSize: tipSize)), + Expanded(child: Container()), + ], + ), + ), + ], + ), + ); + } + return RefreshIndicator( + onRefresh: () async { + onRefresh!(); + }, + child: ListView( + children: [ + SizedBox( + height: height, + child: Column( + children: [ + Expanded(child: Container()), + Icon( + icon, + size: iconSize, + color: Colors.grey.shade600, + ), + Container(height: min / 10), + Container( + padding: const EdgeInsets.only( + left: 30, + right: 30, + ), + child: Text( + message, + style: TextStyle(fontSize: textSize), + textAlign: TextAlign.center, + ), + ), + Text('(下拉刷新)', style: TextStyle(fontSize: tipSize)), + Container(height: min / 15), + Expanded(child: Container()), + ], + ), + ), + ], + ), + ); + }, + ); + } + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + var width = constraints.maxWidth; + var height = constraints.maxHeight; + var min = width < height ? width : height; + var theme = Theme.of(context); + return Center( + child: Column( + children: [ + Expanded(child: Container()), + SizedBox( + width: min / 2, + height: min / 2, + child: Icon(icon, color: Colors.grey[100]), + ), + Container(height: min / 10), + Text(message, style: TextStyle(fontSize: min / 15)), + Expanded(child: Container()), + ], + ), + ); + }, + ); + } +} diff --git a/lib/screens/components/ImageReader.dart b/lib/screens/components/ImageReader.dart index 4726c76..0d5cf4a 100644 --- a/lib/screens/components/ImageReader.dart +++ b/lib/screens/components/ImageReader.dart @@ -289,53 +289,21 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> { @override Widget build(BuildContext context) { - switch (currentFullScreenAction()) { - // 按钮 - case FullScreenAction.CONTROLLER: - return Stack( - children: [ - _buildViewer(), - _buildBar(_buildFullScreenControllerStackItem()), - ], - ); - case FullScreenAction.TOUCH_ONCE: - return Stack( - children: [ - _buildTouchOnceControllerAction(_buildViewer()), - _buildBar(Container()), - ], - ); - case FullScreenAction.TOUCH_DOUBLE: - return Stack( - children: [ - _buildTouchDoubleControllerAction(_buildViewer()), - _buildBar(Container()), - ], - ); - case FullScreenAction.TOUCH_DOUBLE_ONCE_NEXT: - return Stack( - children: [ - _buildTouchDoubleOnceNextControllerAction(_buildViewer()), - _buildBar(Container()), - ], - ); - case FullScreenAction.THREE_AREA: - return Stack( - children: [ - _buildViewer(), - _buildBar(_buildThreeAreaControllerAction()), - ], - ); - } + return Stack( + children: [ + _buildViewer(), + _buildBar(), + ], + ); } - Widget _buildBar(Widget child) { + Widget _buildBar() { switch (widget.readerSliderPosition) { case ReaderSliderPosition.BOTTOM: return Column( children: [ _buildAppBar(), - Expanded(child: child), + Expanded(child: _buildController()), widget.struct.fullScreen ? Container() : Container( @@ -379,7 +347,7 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> { Expanded( child: Stack( children: [ - child, + _buildController(), _buildSliderRight(), ], ), @@ -393,7 +361,7 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> { Expanded( child: Stack( children: [ - child, + _buildController(), _buildSliderLeft(), ], ), @@ -533,7 +501,24 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> { ); } - Widget _buildFullScreenControllerStackItem() { + Widget _buildController() { + switch (currentFullScreenAction()) { + case FullScreenAction.CONTROLLER: + return _buildFullScreenController(); + case FullScreenAction.TOUCH_ONCE: + return _buildTouchOnceController(); + case FullScreenAction.TOUCH_DOUBLE: + return _buildTouchDoubleController(); + case FullScreenAction.TOUCH_DOUBLE_ONCE_NEXT: + return _buildTouchDoubleOnceNextController(); + case FullScreenAction.THREE_AREA: + return _buildThreeAreaController(); + default: + return Container(); + } + } + + Widget _buildFullScreenController() { if (widget.readerSliderPosition == ReaderSliderPosition.BOTTOM && !widget.struct.fullScreen) { return Container(); @@ -570,27 +555,27 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> { ); } - Widget _buildTouchOnceControllerAction(Widget child) { + Widget _buildTouchOnceController() { return GestureDetector( behavior: HitTestBehavior.translucent, onTap: () { widget.struct.onFullScreenChange(!widget.struct.fullScreen); }, - child: child, + child: Container(), ); } - Widget _buildTouchDoubleControllerAction(Widget child) { + Widget _buildTouchDoubleController() { return GestureDetector( behavior: HitTestBehavior.translucent, onDoubleTap: () { widget.struct.onFullScreenChange(!widget.struct.fullScreen); }, - child: child, + child: Container(), ); } - Widget _buildTouchDoubleOnceNextControllerAction(Widget child) { + Widget _buildTouchDoubleOnceNextController() { return GestureDetector( behavior: HitTestBehavior.translucent, onTap: () { @@ -599,11 +584,11 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> { onDoubleTap: () { widget.struct.onFullScreenChange(!widget.struct.fullScreen); }, - child: child, + child: Container(), ); } - Widget _buildThreeAreaControllerAction() { + Widget _buildThreeAreaController() { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { var up = Expanded( @@ -718,11 +703,11 @@ abstract class _ImageReaderContentState extends State<_ImageReaderContent> { case FullScreenAction.CONTROLLER: return false; case FullScreenAction.TOUCH_ONCE: - return false; + return true; case FullScreenAction.TOUCH_DOUBLE: - return false; + return true; case FullScreenAction.TOUCH_DOUBLE_ONCE_NEXT: - return false; + return true; case FullScreenAction.THREE_AREA: return true; } @@ -968,7 +953,7 @@ class _WebToonReaderState extends _ImageReaderContentState { } _controllerTime = DateTime.now().millisecondsSinceEpoch + 400; _itemScrollController.scrollTo( - index: index, + index: index, // 减1 当前position 再减少1 前一个 duration: const Duration(milliseconds: 400), ); } diff --git a/pubspec.lock b/pubspec.lock index 0b1b4b9..7c0e6fd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -216,13 +216,20 @@ packages: name: image_cropper url: "https://pub.dartlang.org" source: hosted - version: "1.5.0" + version: "1.5.1" image_picker: dependency: "direct main" description: name: image_picker url: "https://pub.dartlang.org" source: hosted + version: "0.8.5" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + url: "https://pub.dartlang.org" + source: hosted version: "0.8.4+11" image_picker_for_web: dependency: transitive @@ -231,6 +238,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.6" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.4+11" image_picker_platform_interface: dependency: transitive description: diff --git a/scripts/sign-apk-github-actions.sh b/scripts/sign-apk-github-actions.sh deleted file mode 100644 index 0bf7622..0000000 --- a/scripts/sign-apk-github-actions.sh +++ /dev/null @@ -1,6 +0,0 @@ - -cd "$( cd "$( dirname "$0" )" && pwd )/.." - -echo $KEY_FILE_BASE64 > key.jks.base64 -base64 -d key.jks.base64 > key.jks -echo $KEY_PASSWORD | $ANDROID_HOME/build-tools/30.0.2/apksigner sign --ks key.jks build/app/outputs/flutter-apk/app-release.apk