Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
e98a804efc
|
|||
|
1f9abb8574
|
|||
|
9352ff5c2c
|
|||
|
1dafab8f5c
|
|||
|
b5e098a694
|
|||
|
08fd34632a
|
|||
|
a7a2474e2a
|
|||
|
e15d70dd0e
|
|||
|
e98882a0c6
|
|||
|
24bf7cd434
|
|||
|
54c4f873fc
|
|||
|
b2de793758
|
|||
|
55a91956ce
|
|||
|
202603d16c
|
|||
|
c81143ecdc
|
|||
|
2b7950f5dc
|
|||
|
c554479604
|
|||
|
8e7a540c97
|
|||
|
c66cd0568f
|
|||
|
0800d25b30
|
|||
|
6d180aea38
|
|||
|
3c45191d11
|
|||
|
9db56f6db6
|
|||
|
5e6060e537
|
|||
|
4b8ebf15d2
|
|||
|
d26f18f356
|
|||
|
6090319b5f
|
|||
|
3ed323e056
|
|||
|
f41ef30121
|
|||
|
01df2b49f6
|
|||
|
8306992533
|
|||
|
d983737239
|
|||
|
7c88281f03
|
|||
|
308d6bbba0
|
|||
|
85e6e4adfb
|
|||
|
c860ef9c30
|
|||
|
e7f613b5dc
|
|||
| d932410802 | |||
| 26d2854617 | |||
|
b521f74951
|
|||
|
acc23c0d10
|
|||
|
693d2ad79e
|
|||
|
f19e8950e8
|
|||
|
64d0541dc6
|
|||
|
dfb4d9d9e5
|
|||
| 6306555a30 | |||
| b6b1743285 | |||
|
f6a48140b4
|
|||
|
cd79cf4449
|
|||
|
1aadd9c368
|
|||
|
febc0a8f43
|
|||
|
fd5e714074
|
|||
|
c108859899
|
|||
|
b3083d37c3
|
|||
|
521c1e94c0
|
|||
|
31a45bc4c3
|
|||
|
b6944d1dbb
|
|||
|
32ef2c5023
|
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
name: Build Docker and Deploy
|
name: Build Docker and Deploy
|
||||||
run-name: Build & Deploy ${{ gitea.ref }} on ${{ gitea.actor }}
|
run-name: "[cicd-server]: ${{ github.event.head_commit.message }}"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -30,6 +30,7 @@ jobs:
|
|||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- run: cd "${{ gitea.workspace }}/scnserver" && make clean
|
- run: cd "${{ gitea.workspace }}/scnserver" && make clean
|
||||||
|
- run: cd "${{ gitea.workspace }}/scnserver" && make dgi
|
||||||
- run: cd "${{ gitea.workspace }}/scnserver" && make docker
|
- run: cd "${{ gitea.workspace }}/scnserver" && make docker
|
||||||
- run: cd "${{ gitea.workspace }}/scnserver" && make push-docker
|
- run: cd "${{ gitea.workspace }}/scnserver" && make push-docker
|
||||||
|
|
||||||
@@ -60,21 +61,7 @@ jobs:
|
|||||||
run: go version
|
run: go version
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cd "${{ gitea.workspace }}/scnserver" && make dgi && make swagger && SCN_TEST_LOGLEVEL=WARN make test
|
run: cd "${{ gitea.workspace }}/scnserver" && make generate && SCN_TEST_LOGLEVEL=WARN make test
|
||||||
|
|
||||||
- name: Send failure mail
|
|
||||||
if: failure()
|
|
||||||
uses: dawidd6/action-send-mail@v3
|
|
||||||
with:
|
|
||||||
server_address: smtp.fastmail.com
|
|
||||||
server_port: 465
|
|
||||||
secure: true
|
|
||||||
username: ${{secrets.MAIL_USERNAME}}
|
|
||||||
password: ${{secrets.MAIL_PASSWORD}}
|
|
||||||
subject: Pipeline on '${{ gitea.repository }}' failed
|
|
||||||
to: ${{ steps.commiter_info.outputs.MAIL }}
|
|
||||||
from: Gitea Actions <gitea_actions@blackforestbytes.de>
|
|
||||||
body: "Go to https://gogs.blackforestbytes.com/${{ gitea.repository }}/actions"
|
|
||||||
|
|
||||||
deploy_server:
|
deploy_server:
|
||||||
name: Deploy to Server
|
name: Deploy to Server
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
# https://docs.gitea.com/next/usage/actions/quickstart
|
||||||
|
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
|
||||||
|
# https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
|
||||||
|
|
||||||
|
# Configurable with a few commit messages:
|
||||||
|
# - [skip-tests] Skip the test stage
|
||||||
|
# - [skip-deployment] Skip the deployment stage
|
||||||
|
# - [skip-ci] Skip all stages (the whole ci/cd)
|
||||||
|
#
|
||||||
|
|
||||||
|
name: Build Docker and Deploy
|
||||||
|
run-name: "[cicd-webapp]: ${{ github.event.head_commit.message }}"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['master']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_webapp:
|
||||||
|
name: Build Docker Container
|
||||||
|
runs-on: bfb-cicd-latest
|
||||||
|
if: >-
|
||||||
|
!contains(github.event.head_commit.message, '[skip-ci]') &&
|
||||||
|
!contains(github.event.head_commit.message, '[skip-deployment]')
|
||||||
|
steps:
|
||||||
|
- run: echo -n "${{ secrets.DOCKER_REG_PASS }}" | docker login registry.blackforestbytes.com -u docker --password-stdin
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- run: cd "${{ gitea.workspace }}/webapp" && make clean
|
||||||
|
- run: cd "${{ gitea.workspace }}/webapp" && make docker
|
||||||
|
- run: cd "${{ gitea.workspace }}/webapp" && make push-docker
|
||||||
|
|
||||||
|
deploy_webapp:
|
||||||
|
name: Deploy to Server
|
||||||
|
needs: [build_webapp]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: >-
|
||||||
|
!cancelled() &&
|
||||||
|
!contains(github.event.head_commit.message, '[skip-ci]') &&
|
||||||
|
!contains(github.event.head_commit.message, '[skip-deployment]') &&
|
||||||
|
needs.build_webapp.result == 'success'
|
||||||
|
steps:
|
||||||
|
- name: Execute deploy on remote (via ssh)
|
||||||
|
uses: appleboy/ssh-action@v1.0.0
|
||||||
|
with:
|
||||||
|
host: simplecloudnotifier.de
|
||||||
|
username: bfb-deploy-bot
|
||||||
|
port: 4477
|
||||||
|
key: "${{ secrets.SSH_KEY_BFBDEPLOYBOT }}"
|
||||||
|
script: cd /var/docker/deploy-scripts/simplecloudnotifier && ./deploy-webapp.sh master "${{ gitea.sha }}" || exit 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
@@ -20,9 +20,11 @@ _releases/*
|
|||||||
*.swp
|
*.swp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.atom/
|
.atom/
|
||||||
|
.build/
|
||||||
.buildlog/
|
.buildlog/
|
||||||
.history
|
.history
|
||||||
.svn/
|
.svn/
|
||||||
|
.swiftpm/
|
||||||
migrate_working_dir/
|
migrate_working_dir/
|
||||||
|
|
||||||
# IntelliJ related
|
# IntelliJ related
|
||||||
|
|||||||
+15
-15
@@ -4,7 +4,7 @@
|
|||||||
# This file should be version controlled and should not be manually edited.
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: "41456452f29d64e8deb623a3c927524bcf9f111b"
|
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
@@ -13,26 +13,26 @@ project_type: app
|
|||||||
migration:
|
migration:
|
||||||
platforms:
|
platforms:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
- platform: android
|
- platform: android
|
||||||
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
- platform: ios
|
- platform: ios
|
||||||
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
- platform: linux
|
- platform: linux
|
||||||
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
- platform: macos
|
- platform: macos
|
||||||
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
- platform: web
|
- platform: web
|
||||||
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
- platform: windows
|
- platform: windows
|
||||||
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
|
||||||
# User provided section
|
# User provided section
|
||||||
|
|
||||||
|
|||||||
+11
-23
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
HASH=$(shell git rev-parse HEAD)
|
HASH=$(shell git rev-parse HEAD)
|
||||||
VERS=$(shell cat pubspec.yaml | grep -oP '(?<=version: ).*' | sed 's/[\s]*//' | tr -d '\n') # lazy evaluated!
|
VERS=$(shell cat pubspec.yaml | grep -oP '(?<=version: ).*' | sed 's/[\s]*//' | tr -d '\n' | tr -d '') # lazy evaluated!
|
||||||
|
|
||||||
java:
|
java:
|
||||||
sudo archlinux-java set java-17-openjdk
|
sudo archlinux-java set java-17-openjdk
|
||||||
@@ -21,7 +21,7 @@ run-web: java gen
|
|||||||
run-android: java gen
|
run-android: java gen
|
||||||
ping -c1 10.10.10.177
|
ping -c1 10.10.10.177
|
||||||
adb connect 10.10.10.177:5555
|
adb connect 10.10.10.177:5555
|
||||||
flutter pub run build_runner build
|
dart run build_runner build
|
||||||
_JAVA_OPTIONS="" flutter run -d 10.10.10.177:5555
|
_JAVA_OPTIONS="" flutter run -d 10.10.10.177:5555
|
||||||
|
|
||||||
install-release: java gen
|
install-release: java gen
|
||||||
@@ -30,25 +30,7 @@ install-release: java gen
|
|||||||
flutter run --release -d 35221JEHN07157
|
flutter run --release -d 35221JEHN07157
|
||||||
|
|
||||||
release: java gen
|
release: java gen
|
||||||
flutter build apk --release
|
@_utils/release.sh
|
||||||
cp build/app/outputs/flutter-apk/app-release.apk "_releases/v$(VERS).apk"
|
|
||||||
@echo ""
|
|
||||||
@echo "--> copied APK to _releases ( Version: $(VERS) )"
|
|
||||||
@echo ""
|
|
||||||
flutter build appbundle --release
|
|
||||||
cp build/app/outputs/bundle/release/app-release.aab "_releases/v$(VERS).aab"
|
|
||||||
cd "build/app/intermediates/merged_native_libs/release/out/lib" && zip -r "../../../../../../../_releases/v$(VERS).symbols.zip" .
|
|
||||||
@echo ""
|
|
||||||
@echo "--> copied AAB to _releases ( Version: $(VERS) )"
|
|
||||||
@echo ""
|
|
||||||
flutter build linux --release
|
|
||||||
tar -czf "_releases/v$(VERS).tar.gz" -C build/linux/x64/release/bundle .
|
|
||||||
@echo ""
|
|
||||||
@echo "--> copied linux-binary to _releases ( Version: $(VERS) )"
|
|
||||||
@echo ""
|
|
||||||
@echo "#=> file://$(shell pwd)/_releases"
|
|
||||||
@echo ""
|
|
||||||
@echo "Done."
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
dart analyze
|
dart analyze
|
||||||
@@ -63,15 +45,21 @@ gen: java
|
|||||||
|
|
||||||
# run `make run` in another terminal (or another variant of flutter run)
|
# run `make run` in another terminal (or another variant of flutter run)
|
||||||
autoreload:
|
autoreload:
|
||||||
@
|
|
||||||
@_utils/autoreload.sh
|
@_utils/autoreload.sh
|
||||||
|
|
||||||
icons:
|
icons:
|
||||||
flutter pub run flutter_launcher_icons -f "flutter_launcher_icons.yaml"
|
flutter pub run flutter_launcher_icons -f "flutter_launcher_icons.yaml"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
cd android && ./gradlew clean
|
|
||||||
flutter clean
|
flutter clean
|
||||||
|
flutter pub get
|
||||||
|
|
||||||
|
clean-ios: clean
|
||||||
|
cd ios && xcodebuild clean && rm -rf Pods Podfile.lock && pod install
|
||||||
|
|
||||||
|
clean-android: clean
|
||||||
|
cd android && ./gradlew clean
|
||||||
|
|
||||||
|
|
||||||
# upgrade all packages (add --major-versions even updates across new major versions)
|
# upgrade all packages (add --major-versions even updates across new major versions)
|
||||||
# https://docs.flutter.dev/release/upgrade
|
# https://docs.flutter.dev/release/upgrade
|
||||||
|
|||||||
Executable
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ -d ".git" ]]; then
|
||||||
|
|
||||||
|
echo "Must be called in project root"
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERS="$(cat pubspec.yaml | grep -oP '(?<=version: ).*' | sed 's/[\s]*//' | tr -d '\n' | tr -d '')"
|
||||||
|
|
||||||
|
VERS_BY_SPEC="$( echo -n "$VERS" | awk -F'+' '{print "v"$1}' )"
|
||||||
|
VERS_BY_TAG="$(git describe --abbrev=0 --tags)"
|
||||||
|
|
||||||
|
if [[ "$VERS_BY_TAG" != "$VERS_BY_SPEC" ]]; then
|
||||||
|
echo "Version in pubspec.yaml ($VERS_BY_SPEC) does not match latest git tag ($VERS_BY_TAG)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "(!) Make sure you've updated version-number in pubspec.yaml (current = ${VERS}) and created a tag (current = ${VERS_BY_TAG}) !"
|
||||||
|
echo '> Press Enter to confirm...' && read -r
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
flutter build apk --release
|
||||||
|
cp build/app/outputs/flutter-apk/app-release.apk "_releases/v${VERS}.apk"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "--> copied APK to _releases ( Version: ${VERS} )"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
flutter build appbundle --release
|
||||||
|
cp build/app/outputs/bundle/release/app-release.aab "_releases/v${VERS}.aab"
|
||||||
|
|
||||||
|
pushd "build/app/intermediates/merged_native_libs/release/out/lib" || exit 1
|
||||||
|
zip -r "../../../../../../../_releases/v${VERS}.symbols.zip" .
|
||||||
|
popd || exit 1
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "--> copied AAB to _releases ( Version: ${VERS} )"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
flutter build linux --release
|
||||||
|
tar -czf "_releases/v${VERS}.tar.gz" -C build/linux/x64/release/bundle .
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "--> copied linux-binary to _releases ( Version: ${VERS} )"
|
||||||
|
echo ""
|
||||||
|
echo "#=> file://$(pwd)/_releases"
|
||||||
|
echo ""
|
||||||
|
echo "Done."
|
||||||
@@ -3,6 +3,10 @@ include:
|
|||||||
- package:lints/recommended.yaml
|
- package:lints/recommended.yaml
|
||||||
- package:flutter_lints/flutter.yaml
|
- package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
formatter:
|
||||||
|
page_width: 512
|
||||||
|
trailing_commas: preserve
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ if (keystorePropertiesFile.exists()) {
|
|||||||
android {
|
android {
|
||||||
namespace "com.blackforestbytes.simplecloudnotifier"
|
namespace "com.blackforestbytes.simplecloudnotifier"
|
||||||
compileSdkVersion flutter.compileSdkVersion
|
compileSdkVersion flutter.compileSdkVersion
|
||||||
ndkVersion "27.0.12077973" // should be `flutter.ndkVersion` - but some plugins need 27, even though flutter still has the default value of 26 (flutter 3.29 | 2025-04-12)
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
coreLibraryDesugaringEnabled true
|
coreLibraryDesugaringEnabled true
|
||||||
|
|||||||
@@ -21,6 +21,6 @@
|
|||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
<string>12.0</string>
|
<string>13.0</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
|
|||||||
@@ -8,12 +8,14 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
|
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
483C144A8FD9FBE0BE4CA31D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20818F4FAA848E7B0E1981A1 /* Pods_RunnerTests.framework */; };
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
F0CF5BB613220E8F87B9D978 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDC5CDC3E1AFF7C5BC49C07E /* Pods_Runner.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -40,12 +42,17 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
12F3806130EB6FDA0BB8224F /* 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>"; };
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; 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>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
20818F4FAA848E7B0E1981A1 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
|
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; 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>"; };
|
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>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
7CD878882EBBB898002F3C7D /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
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>"; };
|
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; };
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -53,8 +60,12 @@
|
|||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
A4D58F2C21BD1C00F0A1FE7D /* 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>"; };
|
||||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
AE13A81982696D2690FB1CAB /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
D208168B6EAB5683BAD27E86 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
DF96ECE6A0AC0BFA8724174B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
EDC5CDC3E1AFF7C5BC49C07E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
EE5AE3E3797C8A45B5AFA537 /* 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>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -62,12 +73,51 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
F0CF5BB613220E8F87B9D978 /* Pods_Runner.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
985FE5E285D4547AB0054913 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
483C144A8FD9FBE0BE4CA31D /* Pods_RunnerTests.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
04ACC4FC139085193359BE10 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A4D58F2C21BD1C00F0A1FE7D /* Pods-Runner.debug.xcconfig */,
|
||||||
|
EE5AE3E3797C8A45B5AFA537 /* Pods-Runner.release.xcconfig */,
|
||||||
|
12F3806130EB6FDA0BB8224F /* Pods-Runner.profile.xcconfig */,
|
||||||
|
DF96ECE6A0AC0BFA8724174B /* Pods-RunnerTests.debug.xcconfig */,
|
||||||
|
AE13A81982696D2690FB1CAB /* Pods-RunnerTests.release.xcconfig */,
|
||||||
|
D208168B6EAB5683BAD27E86 /* Pods-RunnerTests.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
331C807B294A618700263BE5 /* RunnerTests.swift */,
|
||||||
|
);
|
||||||
|
path = RunnerTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
51BA414B0BC240BA4DED076E /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EDC5CDC3E1AFF7C5BC49C07E /* Pods_Runner.framework */,
|
||||||
|
20818F4FAA848E7B0E1981A1 /* Pods_RunnerTests.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -79,14 +129,6 @@
|
|||||||
name = Flutter;
|
name = Flutter;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
331C807B294A618700263BE5 /* RunnerTests.swift */,
|
|
||||||
);
|
|
||||||
path = RunnerTests;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
97C146E51CF9000F007C117D = {
|
97C146E51CF9000F007C117D = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -94,6 +136,8 @@
|
|||||||
97C146F01CF9000F007C117D /* Runner */,
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
97C146EF1CF9000F007C117D /* Products */,
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
331C8082294A63A400263BE5 /* RunnerTests */,
|
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||||
|
04ACC4FC139085193359BE10 /* Pods */,
|
||||||
|
51BA414B0BC240BA4DED076E /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -109,6 +153,7 @@
|
|||||||
97C146F01CF9000F007C117D /* Runner */ = {
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
7CD878882EBBB898002F3C7D /* Runner.entitlements */,
|
||||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
@@ -128,9 +173,10 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
827CC5F079B73B9074C059BC /* [CP] Check Pods Manifest.lock */,
|
||||||
331C807D294A63A400263BE5 /* Sources */,
|
331C807D294A63A400263BE5 /* Sources */,
|
||||||
331C807E294A63A400263BE5 /* Frameworks */,
|
|
||||||
331C807F294A63A400263BE5 /* Resources */,
|
331C807F294A63A400263BE5 /* Resources */,
|
||||||
|
985FE5E285D4547AB0054913 /* Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -146,12 +192,15 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
1A8E37578D7C990159141841 /* [CP] Check Pods Manifest.lock */,
|
||||||
9740EEB61CF901F6004384FC /* Run Script */,
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
97C146EA1CF9000F007C117D /* Sources */,
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
81B31B4B9605351C20E486B9 /* [CP] Embed Pods Frameworks */,
|
||||||
|
14FC8825887E7053BC1CD5F8 /* [CP] Copy Pods Resources */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -169,7 +218,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = YES;
|
BuildIndependentTargetsInParallel = YES;
|
||||||
LastUpgradeCheck = 1430;
|
LastUpgradeCheck = 1510;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
331C8080294A63A400263BE5 = {
|
331C8080294A63A400263BE5 = {
|
||||||
@@ -223,6 +272,45 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
14FC8825887E7053BC1CD5F8 /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
1A8E37578D7C990159141841 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
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;
|
||||||
|
};
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
alwaysOutOfDate = 1;
|
||||||
@@ -239,6 +327,45 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
};
|
};
|
||||||
|
81B31B4B9605351C20E486B9 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
827CC5F079B73B9074C059BC /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
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;
|
||||||
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
alwaysOutOfDate = 1;
|
||||||
@@ -308,6 +435,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
@@ -337,6 +465,7 @@
|
|||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
@@ -345,7 +474,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
@@ -356,19 +485,26 @@
|
|||||||
};
|
};
|
||||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
baseConfigurationReference = 12F3806130EB6FDA0BB8224F /* Pods-Runner.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = H5FLAAV8F2;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = SimpleCloudNotifier;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.blackforestbytes.simplecloudnotifier;
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.blackforestbytes.SimpleCloudNotifier;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
@@ -377,7 +513,7 @@
|
|||||||
};
|
};
|
||||||
331C8088294A63A400263BE5 /* Debug */ = {
|
331C8088294A63A400263BE5 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */;
|
baseConfigurationReference = DF96ECE6A0AC0BFA8724174B /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
@@ -395,7 +531,7 @@
|
|||||||
};
|
};
|
||||||
331C8089294A63A400263BE5 /* Release */ = {
|
331C8089294A63A400263BE5 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */;
|
baseConfigurationReference = AE13A81982696D2690FB1CAB /* Pods-RunnerTests.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
@@ -411,7 +547,7 @@
|
|||||||
};
|
};
|
||||||
331C808A294A63A400263BE5 /* Profile */ = {
|
331C808A294A63A400263BE5 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */;
|
baseConfigurationReference = D208168B6EAB5683BAD27E86 /* Pods-RunnerTests.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
@@ -427,8 +563,10 @@
|
|||||||
};
|
};
|
||||||
97C147031CF9000F007C117D /* Debug */ = {
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9740EEB31CF90195004384FC /* Generated.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
@@ -458,6 +596,7 @@
|
|||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
@@ -472,7 +611,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -482,8 +621,10 @@
|
|||||||
};
|
};
|
||||||
97C147041CF9000F007C117D /* Release */ = {
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9740EEB31CF90195004384FC /* Generated.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
@@ -513,6 +654,7 @@
|
|||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
@@ -521,7 +663,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
@@ -534,19 +676,26 @@
|
|||||||
};
|
};
|
||||||
97C147061CF9000F007C117D /* Debug */ = {
|
97C147061CF9000F007C117D /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
baseConfigurationReference = A4D58F2C21BD1C00F0A1FE7D /* Pods-Runner.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = H5FLAAV8F2;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = SimpleCloudNotifier;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.blackforestbytes.simplecloudnotifier;
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.blackforestbytes.SimpleCloudNotifier;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@@ -556,19 +705,26 @@
|
|||||||
};
|
};
|
||||||
97C147071CF9000F007C117D /* Release */ = {
|
97C147071CF9000F007C117D /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
baseConfigurationReference = EE5AE3E3797C8A45B5AFA537 /* Pods-Runner.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = H5FLAAV8F2;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = SimpleCloudNotifier;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.blackforestbytes.simplecloudnotifier;
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.blackforestbytes.SimpleCloudNotifier;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1430"
|
LastUpgradeVersion = "1510"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
@@ -54,11 +55,13 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
|
enableGPUValidationMode = "1"
|
||||||
allowLocationSimulation = "YES">
|
allowLocationSimulation = "YES">
|
||||||
<BuildableProductRunnable
|
<BuildableProductRunnable
|
||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
|
|||||||
@@ -4,4 +4,7 @@
|
|||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>PreviewsEnabled</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -2,7 +2,7 @@ import UIKit
|
|||||||
import Flutter
|
import Flutter
|
||||||
import flutter_local_notifications
|
import flutter_local_notifications
|
||||||
|
|
||||||
@UIApplicationMain
|
@main
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
|
|
||||||
override func application(
|
override func application(
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
|
<true/>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
@@ -24,6 +26,17 @@
|
|||||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>This app needs camera access to scan QR codes</string>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>This app needs photos access to get QR code from photo library</string>
|
||||||
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
|
<true/>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>remote-notification</string>
|
||||||
|
<string>fetch</string>
|
||||||
|
</array>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>UIMainStoryboardFile</key>
|
||||||
@@ -41,14 +54,5 @@
|
|||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
|
||||||
<true/>
|
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
|
||||||
<true/>
|
|
||||||
<key>NSCameraUsageDescription</key>
|
|
||||||
<string>This app needs camera access to scan QR codes</string>
|
|
||||||
|
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
|
||||||
<string>This app needs photos access to get QR code from photo library</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>IDEDidComputeMac32BitWarning</key>
|
<key>aps-environment</key>
|
||||||
<true/>
|
<string>development</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -54,6 +54,20 @@ class MessageFilter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SubscriptionFilter {
|
||||||
|
static final SubscriptionFilter ALL = SubscriptionFilter('both', 'all', 'all');
|
||||||
|
static final SubscriptionFilter OWNED_INACTIVE = SubscriptionFilter('outgoing', 'unconfirmed', 'false');
|
||||||
|
static final SubscriptionFilter OWNED_ACTIVE = SubscriptionFilter('outgoing', 'confirmed', 'false');
|
||||||
|
static final SubscriptionFilter EXTERNAL_ALL = SubscriptionFilter('outgoing', 'all', 'true');
|
||||||
|
static final SubscriptionFilter INCOMING_ALL = SubscriptionFilter('incoming', 'all', 'true');
|
||||||
|
|
||||||
|
final String direction; // 'outgoing' | 'incoming' | 'both'
|
||||||
|
final String confirmation; // 'confirmed' | 'unconfirmed' | 'all'
|
||||||
|
final String external; // 'true' | 'false' | 'all'
|
||||||
|
|
||||||
|
SubscriptionFilter(this.direction, this.confirmation, this.external) {}
|
||||||
|
}
|
||||||
|
|
||||||
class APIClient {
|
class APIClient {
|
||||||
static const String _base = 'https://simplecloudnotifier.de';
|
static const String _base = 'https://simplecloudnotifier.de';
|
||||||
static const String _prefix = '/api/v2';
|
static const String _prefix = '/api/v2';
|
||||||
@@ -123,7 +137,7 @@ class APIClient {
|
|||||||
|
|
||||||
RequestLog.addRequestAPIError(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders, apierr);
|
RequestLog.addRequestAPIError(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders, apierr);
|
||||||
Toaster.error("Error", apierr.message);
|
Toaster.error("Error", apierr.message);
|
||||||
throw APIException(responseStatusCode, apierr.error, apierr.errhighlight, apierr.message);
|
throw APIException(responseStatusCode, apierr.error, apierr.errhighlight, apierr.message, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -191,7 +205,7 @@ class APIClient {
|
|||||||
fn: User.fromJson,
|
fn: User.fromJson,
|
||||||
authToken: auth.getToken(),
|
authToken: auth.getToken(),
|
||||||
query: {
|
query: {
|
||||||
'confirm': ['true']
|
'confirm': ['true'],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -216,7 +230,7 @@ class APIClient {
|
|||||||
static Future<Client> updateClient(TokenSource auth, String clientID, {String? fcmToken, String? agentModel, String? name, String? agentVersion}) async {
|
static Future<Client> updateClient(TokenSource auth, String clientID, {String? fcmToken, String? agentModel, String? name, String? agentVersion}) async {
|
||||||
return await _request(
|
return await _request(
|
||||||
name: 'updateClient',
|
name: 'updateClient',
|
||||||
method: 'PUT',
|
method: 'PATCH',
|
||||||
relURL: 'users/${auth.getUserID()}/clients/$clientID',
|
relURL: 'users/${auth.getUserID()}/clients/$clientID',
|
||||||
jsonBody: {
|
jsonBody: {
|
||||||
if (fcmToken != null) 'fcm_token': fcmToken,
|
if (fcmToken != null) 'fcm_token': fcmToken,
|
||||||
@@ -245,7 +259,7 @@ class APIClient {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
relURL: 'users/${auth.getUserID()}/channels',
|
relURL: 'users/${auth.getUserID()}/channels',
|
||||||
query: {
|
query: {
|
||||||
'selector': [sel.apiKey]
|
'selector': [sel.apiKey],
|
||||||
},
|
},
|
||||||
fn: (json) => ChannelWithSubscription.fromJsonArray(json['channels'] as List<dynamic>),
|
fn: (json) => ChannelWithSubscription.fromJsonArray(json['channels'] as List<dynamic>),
|
||||||
authToken: auth.getToken(),
|
authToken: auth.getToken(),
|
||||||
@@ -345,15 +359,15 @@ class APIClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<Subscription>> getSubscriptionList(TokenSource auth) async {
|
static Future<List<Subscription>> getSubscriptionList(TokenSource auth, SubscriptionFilter filter) async {
|
||||||
return await _request(
|
return await _request(
|
||||||
name: 'getSubscriptionList',
|
name: 'getSubscriptionList',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
relURL: 'users/${auth.getUserID()}/subscriptions',
|
relURL: 'users/${auth.getUserID()}/subscriptions',
|
||||||
query: {
|
query: {
|
||||||
'direction': ['both'],
|
'direction': [filter.direction],
|
||||||
'confirmation': ['all'],
|
'confirmation': [filter.confirmation],
|
||||||
'external': ['all'],
|
'external': [filter.external],
|
||||||
},
|
},
|
||||||
fn: (json) => Subscription.fromJsonArray(json['subscriptions'] as List<dynamic>),
|
fn: (json) => Subscription.fromJsonArray(json['subscriptions'] as List<dynamic>),
|
||||||
authToken: auth.getToken(),
|
authToken: auth.getToken(),
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ class APIException implements Exception {
|
|||||||
final int error;
|
final int error;
|
||||||
final int errHighlight;
|
final int errHighlight;
|
||||||
final String message;
|
final String message;
|
||||||
|
final bool toastShown;
|
||||||
|
|
||||||
APIException(this.httpStatus, this.error, this.errHighlight, this.message);
|
APIException(this.httpStatus, this.error, this.errHighlight, this.message, this.toastShown);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
|||||||
@@ -2,50 +2,104 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
enum BadgeMode { error, warn, info }
|
enum BadgeMode { error, warn, info }
|
||||||
|
|
||||||
class BadgeDisplay extends StatelessWidget {
|
class BadgeDisplay extends StatefulWidget {
|
||||||
final String text;
|
final String text;
|
||||||
final BadgeMode mode;
|
final BadgeMode mode;
|
||||||
final IconData? icon;
|
final IconData? icon;
|
||||||
|
final TextAlign textAlign;
|
||||||
|
final bool closable;
|
||||||
|
final EdgeInsets extraPadding;
|
||||||
|
final bool hidden;
|
||||||
|
|
||||||
const BadgeDisplay({
|
const BadgeDisplay({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.text,
|
required this.text,
|
||||||
required this.mode,
|
required this.mode,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
|
this.textAlign = TextAlign.center,
|
||||||
|
this.closable = false,
|
||||||
|
this.extraPadding = EdgeInsets.zero,
|
||||||
|
this.hidden = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BadgeDisplay> createState() => _BadgeDisplayState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BadgeDisplayState extends State<BadgeDisplay> {
|
||||||
|
bool _isVisible = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (!_isVisible || widget.hidden) return const SizedBox.shrink();
|
||||||
|
|
||||||
var col = Colors.grey;
|
var col = Colors.grey;
|
||||||
var colFG = Colors.black;
|
var colFG = Colors.black;
|
||||||
|
|
||||||
if (mode == BadgeMode.error) col = Colors.red;
|
if (widget.mode == BadgeMode.error) col = Colors.red;
|
||||||
if (mode == BadgeMode.warn) col = Colors.orange;
|
if (widget.mode == BadgeMode.warn) col = Colors.orange;
|
||||||
if (mode == BadgeMode.info) col = Colors.blue;
|
if (widget.mode == BadgeMode.info) col = Colors.blue;
|
||||||
|
|
||||||
if (mode == BadgeMode.error) colFG = Colors.red[900]!;
|
if (widget.mode == BadgeMode.error) colFG = Colors.red[900]!;
|
||||||
if (mode == BadgeMode.warn) colFG = Colors.black;
|
if (widget.mode == BadgeMode.warn) colFG = Colors.black;
|
||||||
if (mode == BadgeMode.info) colFG = Colors.black;
|
if (widget.mode == BadgeMode.info) colFG = Colors.black;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
|
margin: widget.extraPadding,
|
||||||
decoration: BoxDecoration(
|
child: Stack(
|
||||||
color: col[100],
|
clipBehavior: Clip.none,
|
||||||
border: Border.all(color: col[300]!),
|
|
||||||
borderRadius: BorderRadius.circular(4.0),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
if (icon != null) Icon(icon!, color: colFG, size: 16.0),
|
// The badge itself
|
||||||
Expanded(
|
Container(
|
||||||
child: Text(
|
padding: EdgeInsets.fromLTRB(8, 2, widget.closable ? 16 : 8, 2),
|
||||||
text,
|
decoration: BoxDecoration(
|
||||||
textAlign: TextAlign.center,
|
color: col[100],
|
||||||
style: TextStyle(color: colFG, fontSize: 14.0),
|
border: Border.all(color: col[300]!),
|
||||||
|
borderRadius: BorderRadius.circular(4.0),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
if (widget.icon != null) Icon(widget.icon!, color: colFG, size: 16.0),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
widget.text,
|
||||||
|
textAlign: widget.textAlign,
|
||||||
|
style: TextStyle(color: colFG, fontSize: 14.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
if (widget.closable) _buildCloseButton(context, colFG),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Positioned _buildCloseButton(BuildContext context, Color colFG) {
|
||||||
|
return Positioned(
|
||||||
|
top: 4,
|
||||||
|
right: 4,
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_isVisible = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(2),
|
||||||
|
child: Icon(
|
||||||
|
Icons.close,
|
||||||
|
size: 14,
|
||||||
|
color: colFG,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FilterChips<T extends Enum> extends StatelessWidget {
|
||||||
|
final List<(T, String)> options;
|
||||||
|
final T value;
|
||||||
|
final void Function(T)? onChanged;
|
||||||
|
|
||||||
|
const FilterChips({
|
||||||
|
Key? key,
|
||||||
|
required this.options,
|
||||||
|
required this.value,
|
||||||
|
required this.onChanged,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
for (var opt in options) _buildChiplet(context, opt.$1, opt.$2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildChiplet(BuildContext context, T optValue, String optText) {
|
||||||
|
final isSelected = optValue == value;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 2, 4, 2),
|
||||||
|
child: InputChip(
|
||||||
|
label: Text(
|
||||||
|
optText,
|
||||||
|
style: isSelected ? TextStyle(color: Theme.of(context).colorScheme.onPrimary) : null,
|
||||||
|
),
|
||||||
|
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
||||||
|
isEnabled: true,
|
||||||
|
selected: true,
|
||||||
|
showCheckmark: false,
|
||||||
|
onPressed: () {
|
||||||
|
if (!isSelected) onChanged?.call(optValue);
|
||||||
|
},
|
||||||
|
selectedColor: isSelected ? Theme.of(context).colorScheme.primary : null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,20 +20,30 @@ class SCNNavLayout extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SCNNavLayoutState extends State<SCNNavLayout> {
|
class _SCNNavLayoutState extends State<SCNNavLayout> {
|
||||||
int _selectedIndex = 0; // 4 == FAB
|
static const INDEX_MESSAGES = 0;
|
||||||
|
static const INDEX_CHANNELS = 1;
|
||||||
|
static const INDEX_ACCOUNT = 2;
|
||||||
|
static const INDEX_CONFIG = 3;
|
||||||
|
static const INDEX_SEND = 4; // FAB
|
||||||
|
|
||||||
|
int _selectedIndex = INDEX_MESSAGES;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
final userAcc = Provider.of<AppAuth>(context, listen: false);
|
final userAcc = Provider.of<AppAuth>(context, listen: false);
|
||||||
if (!userAcc.isAuth()) _selectedIndex = 2;
|
if (!userAcc.isAuth()) _selectedIndex = INDEX_ACCOUNT;
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onItemTapped(int index) {
|
void _onItemTapped(int index) {
|
||||||
final userAcc = Provider.of<AppAuth>(context, listen: false);
|
final userAcc = Provider.of<AppAuth>(context, listen: false);
|
||||||
if (!userAcc.isAuth()) {
|
|
||||||
|
if (!userAcc.isAuth() && [INDEX_MESSAGES, INDEX_CHANNELS, INDEX_SEND].contains(index)) {
|
||||||
Toaster.info("Not logged in", "Please login or create a new account first");
|
Toaster.info("Not logged in", "Please login or create a new account first");
|
||||||
|
setState(() {
|
||||||
|
_selectedIndex = INDEX_ACCOUNT;
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +60,7 @@ class _SCNNavLayoutState extends State<SCNNavLayout> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedIndex = 4;
|
_selectedIndex = INDEX_SEND;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,17 +69,17 @@ class _SCNNavLayoutState extends State<SCNNavLayout> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: SCNAppBar(
|
appBar: SCNAppBar(
|
||||||
title: null,
|
title: null,
|
||||||
showSearch: _selectedIndex == 0,
|
showSearch: _selectedIndex == INDEX_MESSAGES,
|
||||||
showShare: false,
|
showShare: false,
|
||||||
showThemeSwitch: true,
|
showThemeSwitch: true,
|
||||||
),
|
),
|
||||||
body: IndexedStack(
|
body: IndexedStack(
|
||||||
children: [
|
children: [
|
||||||
ExcludeFocus(excluding: _selectedIndex != 0, child: MessageListPage(isVisiblePage: _selectedIndex == 0)),
|
ExcludeFocus(excluding: _selectedIndex != INDEX_MESSAGES, child: MessageListPage(isVisiblePage: _selectedIndex == INDEX_MESSAGES)),
|
||||||
ExcludeFocus(excluding: _selectedIndex != 1, child: ChannelRootPage(isVisiblePage: _selectedIndex == 1)),
|
ExcludeFocus(excluding: _selectedIndex != INDEX_CHANNELS, child: ChannelRootPage(isVisiblePage: _selectedIndex == INDEX_CHANNELS)),
|
||||||
ExcludeFocus(excluding: _selectedIndex != 2, child: AccountRootPage(isVisiblePage: _selectedIndex == 2)),
|
ExcludeFocus(excluding: _selectedIndex != INDEX_ACCOUNT, child: AccountRootPage(isVisiblePage: _selectedIndex == INDEX_ACCOUNT)),
|
||||||
ExcludeFocus(excluding: _selectedIndex != 3, child: SettingsRootPage(isVisiblePage: _selectedIndex == 3)),
|
ExcludeFocus(excluding: _selectedIndex != INDEX_CONFIG, child: SettingsRootPage(isVisiblePage: _selectedIndex == INDEX_CONFIG)),
|
||||||
ExcludeFocus(excluding: _selectedIndex != 4, child: SendRootPage(isVisiblePage: _selectedIndex == 4)),
|
ExcludeFocus(excluding: _selectedIndex != INDEX_SEND, child: SendRootPage(isVisiblePage: _selectedIndex == INDEX_SEND)),
|
||||||
],
|
],
|
||||||
index: _selectedIndex,
|
index: _selectedIndex,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class _FilterModalPriorityState extends State<FilterModalPriority> {
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Priority'),
|
title: const Text('Priority'),
|
||||||
content: Container(
|
content: Container(
|
||||||
width: 0,
|
width: 9000,
|
||||||
height: 200,
|
height: 200,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
|
|||||||
+38
-14
@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:in_app_purchase/in_app_purchase.dart';
|
import 'package:in_app_purchase/in_app_purchase.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
import 'package:simplecloudnotifier/main_messaging.dart';
|
import 'package:simplecloudnotifier/main_messaging.dart';
|
||||||
import 'package:simplecloudnotifier/main_utils.dart';
|
import 'package:simplecloudnotifier/main_utils.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/nav_layout.dart';
|
import 'package:simplecloudnotifier/components/layout/nav_layout.dart';
|
||||||
@@ -68,15 +69,17 @@ void main() async {
|
|||||||
print('[INIT] Request Notification permissions...');
|
print('[INIT] Request Notification permissions...');
|
||||||
await FirebaseMessaging.instance.requestPermission(provisional: true);
|
await FirebaseMessaging.instance.requestPermission(provisional: true);
|
||||||
|
|
||||||
FirebaseMessaging.instance.onTokenRefresh.listen((fcmToken) {
|
FirebaseMessaging.instance.onTokenRefresh
|
||||||
try {
|
.listen((fcmToken) {
|
||||||
setFirebaseToken(fcmToken);
|
try {
|
||||||
} catch (exc, trace) {
|
setFirebaseToken(fcmToken);
|
||||||
ApplicationLog.error('Failed to set firebase token: ' + exc.toString(), trace: trace);
|
} catch (exc, trace) {
|
||||||
}
|
ApplicationLog.error('Failed to set firebase token: ' + exc.toString(), trace: trace);
|
||||||
}).onError((dynamic err) {
|
}
|
||||||
ApplicationLog.error('Failed to listen to token refresh events: ' + (err?.toString() ?? ''));
|
})
|
||||||
});
|
.onError((dynamic err) {
|
||||||
|
ApplicationLog.error('Failed to listen to token refresh events: ' + (err?.toString() ?? ''));
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
print('[INIT] Query firebase token...');
|
print('[INIT] Query firebase token...');
|
||||||
@@ -96,6 +99,25 @@ void main() async {
|
|||||||
|
|
||||||
await appAuth.tryMigrateFromV1();
|
await appAuth.tryMigrateFromV1();
|
||||||
|
|
||||||
|
if (appAuth.isAuth()) {
|
||||||
|
print('[INIT] Load Client and potentially update...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
var client = await appAuth.loadClient(onlyCached: true);
|
||||||
|
if (client != null) {
|
||||||
|
if (client.agentModel != Globals().deviceModel || client.name != Globals().nameForClient() || client.agentVersion != Globals().version) {
|
||||||
|
print('[INIT] Update Client info...');
|
||||||
|
|
||||||
|
final newClient = await APIClient.updateClient(appAuth, client.clientID, agentModel: Globals().deviceModel, name: Globals().nameForClient(), agentVersion: Globals().version);
|
||||||
|
appAuth.setClientAndClientID(newClient);
|
||||||
|
await appAuth.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to get client (on init): ' + exc.toString(), trace: trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
print('[INIT] Load Notifications...');
|
print('[INIT] Load Notifications...');
|
||||||
|
|
||||||
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||||
@@ -167,11 +189,13 @@ class _SCNAppState extends State<SCNApp> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_purchaseSubscription = InAppPurchase.instance.purchaseStream.listen(
|
if (Globals().clientType == 'IOS' || Globals().clientType == 'ANDROID') {
|
||||||
purchaseUpdated,
|
_purchaseSubscription = InAppPurchase.instance.purchaseStream.listen(
|
||||||
onDone: () => _purchaseSubscription?.cancel(),
|
purchaseUpdated,
|
||||||
onError: purchaseError,
|
onDone: () => _purchaseSubscription?.cancel(),
|
||||||
);
|
onError: purchaseError,
|
||||||
|
);
|
||||||
|
}
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ void setFirebaseToken(String fcmToken) async {
|
|||||||
|
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
// should not really happen - perhaps someone externally deleted the client?
|
// should not really happen - perhaps someone externally deleted the client?
|
||||||
final newClient = await APIClient.addClient(acc, fcmToken, Globals().deviceModel, Globals().version, Globals().hostname, Globals().clientType);
|
final newClient = await APIClient.addClient(acc, fcmToken, Globals().deviceModel, Globals().version, Globals().nameForClient(), Globals().clientType);
|
||||||
acc.setClientAndClientID(newClient);
|
acc.setClientAndClientID(newClient);
|
||||||
await acc.save();
|
await acc.save();
|
||||||
} else {
|
} else {
|
||||||
final newClient = await APIClient.updateClient(acc, client.clientID, fcmToken: fcmToken, agentModel: Globals().deviceModel, name: Globals().hostname, agentVersion: Globals().version);
|
final newClient = await APIClient.updateClient(acc, client.clientID, fcmToken: fcmToken, agentModel: Globals().deviceModel, name: Globals().nameForClient(), agentVersion: Globals().version);
|
||||||
acc.setClientAndClientID(newClient);
|
acc.setClientAndClientID(newClient);
|
||||||
await acc.save();
|
await acc.save();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||||
import 'package:simplecloudnotifier/models/user.dart';
|
import 'package:simplecloudnotifier/models/user.dart';
|
||||||
import 'package:simplecloudnotifier/pages/account/login.dart';
|
import 'package:simplecloudnotifier/pages/account/login.dart';
|
||||||
|
import 'package:simplecloudnotifier/pages/account/show_token_modal.dart';
|
||||||
import 'package:simplecloudnotifier/pages/channel_list/channel_list_extended.dart';
|
import 'package:simplecloudnotifier/pages/channel_list/channel_list_extended.dart';
|
||||||
import 'package:simplecloudnotifier/pages/client_list/client_list.dart';
|
import 'package:simplecloudnotifier/pages/client_list/client_list.dart';
|
||||||
import 'package:simplecloudnotifier/pages/filtered_message_view/filtered_message_view.dart';
|
import 'package:simplecloudnotifier/pages/filtered_message_view/filtered_message_view.dart';
|
||||||
@@ -115,7 +117,7 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
|
|
||||||
_futureSubscriptionCount = ImmediateFuture.ofFuture(() async {
|
_futureSubscriptionCount = ImmediateFuture.ofFuture(() async {
|
||||||
if (!userAcc.isAuth()) throw new Exception('not logged in');
|
if (!userAcc.isAuth()) throw new Exception('not logged in');
|
||||||
final subs = await APIClient.getSubscriptionList(userAcc);
|
final subs = await APIClient.getSubscriptionList(userAcc, SubscriptionFilter.ALL);
|
||||||
return subs.length;
|
return subs.length;
|
||||||
}());
|
}());
|
||||||
|
|
||||||
@@ -151,7 +153,7 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
// refresh all data and then replace teh futures used in build()
|
// refresh all data and then replace teh futures used in build()
|
||||||
|
|
||||||
final channelsAll = await APIClient.getChannelList(userAcc, ChannelSelector.all);
|
final channelsAll = await APIClient.getChannelList(userAcc, ChannelSelector.all);
|
||||||
final subs = await APIClient.getSubscriptionList(userAcc);
|
final subs = await APIClient.getSubscriptionList(userAcc, SubscriptionFilter.ALL);
|
||||||
final clients = await APIClient.getClientList(userAcc);
|
final clients = await APIClient.getClientList(userAcc);
|
||||||
final keys = await APIClient.getKeyTokenList(userAcc);
|
final keys = await APIClient.getKeyTokenList(userAcc);
|
||||||
final senderNames = await APIClient.getSenderNameList(userAcc);
|
final senderNames = await APIClient.getSenderNameList(userAcc);
|
||||||
@@ -165,6 +167,9 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
_futureSenderNamesCount = ImmediateFuture.ofValue(senderNames.length);
|
_futureSenderNamesCount = ImmediateFuture.ofValue(senderNames.length);
|
||||||
_futureUser = ImmediateFuture.ofValue(user);
|
_futureUser = ImmediateFuture.ofValue(user);
|
||||||
});
|
});
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to refresh account data: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to refresh account data');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to refresh account data: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to refresh account data: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to refresh account data');
|
Toaster.error("Error", 'Failed to refresh account data');
|
||||||
@@ -268,7 +273,20 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
_buildHeader(context, user),
|
_buildHeader(context, user),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(user.username ?? user.userID, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)),
|
Text(user.username ?? user.userID, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 8),
|
||||||
|
if (acc.tokenSend != null || acc.tokenAdmin != null)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: UI.button(
|
||||||
|
text: 'UserID & Token kopieren',
|
||||||
|
onPressed: () => _showTokenModal(context, acc),
|
||||||
|
icon: FontAwesomeIcons.copy,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
..._buildCards(context, user),
|
..._buildCards(context, user),
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
_buildFooter(context, user),
|
_buildFooter(context, user),
|
||||||
@@ -403,7 +421,13 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: "All Messages", filter: MessageFilter(senderUserID: [user.userID])));
|
Navi.push(
|
||||||
|
context,
|
||||||
|
() => FilteredMessageViewPage(
|
||||||
|
title: "All Messages",
|
||||||
|
alertText: "All messages sent from your account",
|
||||||
|
filter: MessageFilter(senderUserID: [user.userID]),
|
||||||
|
));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@@ -456,18 +480,20 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: UI.button(
|
child: UI.button(
|
||||||
text: 'Logout',
|
text: 'Logout',
|
||||||
onPressed: _logout,
|
onPressed: _logout,
|
||||||
color: Colors.orange,
|
color: Colors.orange,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: UI.button(
|
child: UI.button(
|
||||||
text: 'Delete Account',
|
text: 'Delete Account',
|
||||||
onPressed: _deleteAccount,
|
onPressed: _deleteAccount,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -501,15 +527,23 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
|
|
||||||
await Globals().setPrefFCMToken(fcmToken);
|
await Globals().setPrefFCMToken(fcmToken);
|
||||||
|
|
||||||
final user = await APIClient.createUserWithClient(null, fcmToken, Globals().platform, Globals().version, Globals().hostname, Globals().clientType);
|
final user = await APIClient.createUserWithClient(null, fcmToken, Globals().platform, Globals().version, Globals().nameForClient(), Globals().clientType);
|
||||||
|
|
||||||
acc.set(user.user, user.clients[0], user.adminKey, user.sendKey);
|
acc.set(user.user, user.clients[0], user.adminKey, user.sendKey);
|
||||||
|
|
||||||
await acc.save();
|
await acc.save();
|
||||||
Toaster.success("Success", 'Successfully Created a new account');
|
Toaster.success("Success", 'Successfully Created a new account');
|
||||||
} catch (exc, trace) {
|
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => ShowTokenModal(account: acc, isAfterRegister: true),
|
||||||
|
);
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to create user account');
|
||||||
ApplicationLog.error('Failed to create user account: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to create user account: ' + exc.toString(), trace: trace);
|
||||||
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to create user account');
|
Toaster.error("Error", 'Failed to create user account');
|
||||||
|
ApplicationLog.error('Failed to create user account: ' + exc.toString(), trace: trace);
|
||||||
} finally {
|
} finally {
|
||||||
setState(() => loading = false);
|
setState(() => loading = false);
|
||||||
}
|
}
|
||||||
@@ -553,6 +587,9 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
//TODO clear messages/channels/etc in open views
|
//TODO clear messages/channels/etc in open views
|
||||||
acc.clear();
|
acc.clear();
|
||||||
await acc.save();
|
await acc.save();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to delete user');
|
||||||
|
ApplicationLog.error('Failed to delete user: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to delete user');
|
Toaster.error("Error", 'Failed to delete user');
|
||||||
ApplicationLog.error('Failed to delete user: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to delete user: ' + exc.toString(), trace: trace);
|
||||||
@@ -580,6 +617,9 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
Toaster.success("Success", 'Username changed');
|
Toaster.success("Success", 'Username changed');
|
||||||
|
|
||||||
_backgroundRefresh();
|
_backgroundRefresh();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to update username');
|
||||||
|
ApplicationLog.error('Failed to update username: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to update username');
|
Toaster.error("Error", 'Failed to update username');
|
||||||
ApplicationLog.error('Failed to update username: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to update username: ' + exc.toString(), trace: trace);
|
||||||
@@ -717,4 +757,11 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
|
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showTokenModal(BuildContext context, AppAuth acc) {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => ShowTokenModal(account: acc, isAfterRegister: false),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/globals.dart';
|
import 'package:simplecloudnotifier/state/globals.dart';
|
||||||
@@ -155,16 +156,19 @@ class _AccountLoginPageState extends State<AccountLoginPage> {
|
|||||||
|
|
||||||
final user = await APIClient.getUser(DirectTokenSource(uid, atokv), uid);
|
final user = await APIClient.getUser(DirectTokenSource(uid, atokv), uid);
|
||||||
|
|
||||||
final client = await APIClient.addClient(DirectTokenSource(uid, atokv), fcmToken, Globals().deviceModel, Globals().version, Globals().hostname, Globals().clientType);
|
final client = await APIClient.addClient(DirectTokenSource(uid, atokv), fcmToken, Globals().deviceModel, Globals().version, Globals().nameForClient(), Globals().clientType);
|
||||||
|
|
||||||
acc.set(user, client, atokv, stokv);
|
acc.set(user, client, atokv, stokv);
|
||||||
await acc.save();
|
await acc.save();
|
||||||
|
|
||||||
Toaster.success("Login", "Successfully logged in");
|
Toaster.success("Login", "Successfully logged in");
|
||||||
Navi.popToRoot(context);
|
Navi.popToRoot(context);
|
||||||
} catch (exc, trace) {
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to verify token');
|
||||||
ApplicationLog.error('Failed to verify token: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to verify token: ' + exc.toString(), trace: trace);
|
||||||
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to verify token');
|
Toaster.error("Error", 'Failed to verify token');
|
||||||
|
ApplicationLog.error('Failed to verify token: ' + exc.toString(), trace: trace);
|
||||||
} finally {
|
} finally {
|
||||||
setState(() => loading = false);
|
setState(() => loading = false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
|
import 'package:simplecloudnotifier/utils/toaster.dart';
|
||||||
|
import 'package:simplecloudnotifier/utils/ui.dart';
|
||||||
|
|
||||||
|
class ShowTokenModal extends StatelessWidget {
|
||||||
|
final AppAuth account;
|
||||||
|
final bool isAfterRegister;
|
||||||
|
|
||||||
|
const ShowTokenModal({Key? key, required this.account, required this.isAfterRegister}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var alertText = "Use your UserID and Send-Token to send messages to your account.\n\nThe Admin-Token should not be shared.";
|
||||||
|
if (this.isAfterRegister) {
|
||||||
|
alertText = "These are your UserID and tokens.\n\nBackup your Admin-Token safely.\n\nUse UserId & Send-Token to send yourself messages.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('UserID & Token'),
|
||||||
|
content: Container(
|
||||||
|
width: 9000,
|
||||||
|
height: 450,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
BadgeDisplay(
|
||||||
|
text: alertText,
|
||||||
|
icon: null,
|
||||||
|
mode: BadgeMode.info,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
if (this.account.userID != null)
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidUser,
|
||||||
|
title: 'UserID',
|
||||||
|
values: [this.account.userID!],
|
||||||
|
iconActions: [(FontAwesomeIcons.copy, null, () => _copy('UserID', this.account.userID!))],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
if (this.account.tokenSend != null)
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidKey,
|
||||||
|
title: 'Send-Token',
|
||||||
|
values: [this.account.tokenSend!],
|
||||||
|
iconActions: [(FontAwesomeIcons.copy, null, () => _copy('Send-Token', this.account.tokenSend!))],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
if (this.account.tokenSend != null)
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidKey,
|
||||||
|
title: 'Admin-Token',
|
||||||
|
values: [this.account.tokenAdmin!],
|
||||||
|
iconActions: [(FontAwesomeIcons.copy, null, () => _copy('Admin-Token', this.account.tokenAdmin!))],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Close'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _copy(String txt, String v) {
|
||||||
|
Clipboard.setData(new ClipboardData(text: v));
|
||||||
|
Toaster.info("Clipboard", 'Copied ${txt} to Clipboard');
|
||||||
|
print('================= [CLIPBOARD] =================\n${v}\n================= [/CLIPBOARD] =================');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -144,28 +144,7 @@ class _ChannelRootPageState extends State<ChannelRootPage> with RouteAware {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: RefreshIndicator(
|
body: _buildList(context),
|
||||||
onRefresh: () => Future.sync(
|
|
||||||
() => _pagingController.refresh(),
|
|
||||||
),
|
|
||||||
child: PagedListView<int, ChannelWithSubscription>(
|
|
||||||
pagingController: _pagingController,
|
|
||||||
builderDelegate: PagedChildBuilderDelegate<ChannelWithSubscription>(
|
|
||||||
itemBuilder: (context, item, index) => ChannelListItem(
|
|
||||||
channel: item.channel,
|
|
||||||
subscription: item.subscription,
|
|
||||||
mode: ChannelListItemMode.Messages,
|
|
||||||
onChannelListReloadTrigger: _enqueueReload,
|
|
||||||
onSubscriptionChanged: (channelID, subscription) {
|
|
||||||
setState(() {
|
|
||||||
final idx = _pagingController.itemList?.indexWhere((p) => p.channel.channelID == channelID);
|
|
||||||
if (idx != null && idx >= 0) _pagingController.itemList![idx] = ChannelWithSubscription(channel: _pagingController.itemList![idx].channel, subscription: subscription);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
heroTag: 'fab_channel_list_qr',
|
heroTag: 'fab_channel_list_qr',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -176,6 +155,31 @@ class _ChannelRootPageState extends State<ChannelRootPage> with RouteAware {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => Future.sync(
|
||||||
|
() => _pagingController.refresh(),
|
||||||
|
),
|
||||||
|
child: PagedListView<int, ChannelWithSubscription>(
|
||||||
|
pagingController: _pagingController,
|
||||||
|
builderDelegate: PagedChildBuilderDelegate<ChannelWithSubscription>(
|
||||||
|
itemBuilder: (context, item, index) => ChannelListItem(
|
||||||
|
channel: item.channel,
|
||||||
|
subscription: item.subscription,
|
||||||
|
mode: ChannelListItemMode.Messages,
|
||||||
|
onChannelListReloadTrigger: _enqueueReload,
|
||||||
|
onSubscriptionChanged: (channelID, subscription) {
|
||||||
|
setState(() {
|
||||||
|
final idx = _pagingController.itemList?.indexWhere((p) => p.channel.channelID == channelID);
|
||||||
|
if (idx != null && idx >= 0) _pagingController.itemList![idx] = ChannelWithSubscription(channel: _pagingController.itemList![idx].channel, subscription: subscription);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _enqueueReload() {
|
void _enqueueReload() {
|
||||||
_reloadEnqueued = true;
|
_reloadEnqueued = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
import 'package:simplecloudnotifier/pages/channel_scanner/channel_scanner.dart';
|
import 'package:simplecloudnotifier/pages/channel_scanner/channel_scanner.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/pages/channel_list/channel_list_item.dart';
|
import 'package:simplecloudnotifier/pages/channel_list/channel_list_item.dart';
|
||||||
@@ -121,27 +123,21 @@ class _ChannelListExtendedPageState extends State<ChannelListExtendedPage> with
|
|||||||
showShare: false,
|
showShare: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
child: RefreshIndicator(
|
child: Column(
|
||||||
onRefresh: () => Future.sync(
|
children: [
|
||||||
() => _pagingController.refresh(),
|
BadgeDisplay(
|
||||||
),
|
text: "All channels accessible from this account\n(Your own channels and subscribed channels)",
|
||||||
child: PagedListView<int, ChannelWithSubscription>(
|
icon: null,
|
||||||
pagingController: _pagingController,
|
mode: BadgeMode.info,
|
||||||
builderDelegate: PagedChildBuilderDelegate<ChannelWithSubscription>(
|
textAlign: TextAlign.left,
|
||||||
itemBuilder: (context, item, index) => ChannelListItem(
|
closable: true,
|
||||||
channel: item.channel,
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
subscription: item.subscription,
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
mode: ChannelListItemMode.Extended,
|
|
||||||
onChannelListReloadTrigger: _enqueueReload,
|
|
||||||
onSubscriptionChanged: (channelID, subscription) {
|
|
||||||
setState(() {
|
|
||||||
final idx = _pagingController.itemList?.indexWhere((p) => p.channel.channelID == channelID);
|
|
||||||
if (idx != null && idx >= 0) _pagingController.itemList![idx] = ChannelWithSubscription(channel: _pagingController.itemList![idx].channel, subscription: subscription);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
|
child: _buildList(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
@@ -154,6 +150,31 @@ class _ChannelListExtendedPageState extends State<ChannelListExtendedPage> with
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => Future.sync(
|
||||||
|
() => _pagingController.refresh(),
|
||||||
|
),
|
||||||
|
child: PagedListView<int, ChannelWithSubscription>(
|
||||||
|
pagingController: _pagingController,
|
||||||
|
builderDelegate: PagedChildBuilderDelegate<ChannelWithSubscription>(
|
||||||
|
itemBuilder: (context, item, index) => ChannelListItem(
|
||||||
|
channel: item.channel,
|
||||||
|
subscription: item.subscription,
|
||||||
|
mode: ChannelListItemMode.Extended,
|
||||||
|
onChannelListReloadTrigger: _enqueueReload,
|
||||||
|
onSubscriptionChanged: (channelID, subscription) {
|
||||||
|
setState(() {
|
||||||
|
final idx = _pagingController.itemList?.indexWhere((p) => p.channel.channelID == channelID);
|
||||||
|
if (idx != null && idx >= 0) _pagingController.itemList![idx] = ChannelWithSubscription(channel: _pagingController.itemList![idx].channel, subscription: subscription);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _enqueueReload() {
|
void _enqueueReload() {
|
||||||
_reloadEnqueued = true;
|
_reloadEnqueued = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
import 'package:simplecloudnotifier/models/scn_message.dart';
|
import 'package:simplecloudnotifier/models/scn_message.dart';
|
||||||
import 'package:simplecloudnotifier/models/subscription.dart';
|
import 'package:simplecloudnotifier/models/subscription.dart';
|
||||||
@@ -220,6 +221,9 @@ class _ChannelListItemState extends State<ChannelListItem> {
|
|||||||
} else {
|
} else {
|
||||||
Toaster.success("Success", 'Requested widget.subscription to channel');
|
Toaster.success("Success", 'Requested widget.subscription to channel');
|
||||||
}
|
}
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to subscribe to channel');
|
Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -238,6 +242,9 @@ class _ChannelListItemState extends State<ChannelListItem> {
|
|||||||
widget.onSubscriptionChanged.call(sub.channelID, null);
|
widget.onSubscriptionChanged.call(sub.channelID, null);
|
||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -256,6 +263,9 @@ class _ChannelListItemState extends State<ChannelListItem> {
|
|||||||
widget.onSubscriptionChanged.call(sub.channelID, newSub);
|
widget.onSubscriptionChanged.call(sub.channelID, newSub);
|
||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -274,6 +284,9 @@ class _ChannelListItemState extends State<ChannelListItem> {
|
|||||||
widget.onSubscriptionChanged.call(sub.channelID, newSub);
|
widget.onSubscriptionChanged.call(sub.channelID, newSub);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscribed to channel');
|
Toaster.success("Success", 'Subscribed to channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to subscribe to channel');
|
Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ import 'dart:convert';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/scan_result.dart';
|
import 'package:simplecloudnotifier/models/scan_result.dart';
|
||||||
import 'package:simplecloudnotifier/pages/channel_scanner/channel_scanner_result_channelsubscribe.dart';
|
import 'package:simplecloudnotifier/pages/channel_scanner/channel_scanner_result_channelsubscribe.dart';
|
||||||
import 'package:simplecloudnotifier/pages/channel_scanner/channel_scanner_result_channelview.dart';
|
import 'package:simplecloudnotifier/pages/channel_scanner/channel_scanner_result_channelview.dart';
|
||||||
import 'package:simplecloudnotifier/pages/channel_scanner/channel_scanner_result_messagesend.dart';
|
import 'package:simplecloudnotifier/pages/channel_scanner/channel_scanner_result_messagesend.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/utils/ui.dart';
|
import 'package:simplecloudnotifier/utils/ui.dart';
|
||||||
|
|
||||||
class ChannelScannerPage extends StatefulWidget {
|
class ChannelScannerPage extends StatefulWidget {
|
||||||
@@ -40,6 +42,16 @@ class _ChannelScannerPageState extends State<ChannelScannerPage> {
|
|||||||
padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
|
padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
SizedBox(height: 16),
|
||||||
|
BadgeDisplay(
|
||||||
|
text: "Scan the QR Code of an account to send messages to this account,\nor the QR Code of an channel to request a subscription to this channel.",
|
||||||
|
icon: null,
|
||||||
|
mode: BadgeMode.info,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
|
),
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
if (scanResult == null) ...[
|
if (scanResult == null) ...[
|
||||||
Center(
|
Center(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:simplecloudnotifier/state/app_auth.dart';
|
|||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/utils/navi.dart';
|
import 'package:simplecloudnotifier/utils/navi.dart';
|
||||||
import 'package:simplecloudnotifier/utils/toaster.dart';
|
import 'package:simplecloudnotifier/utils/toaster.dart';
|
||||||
|
import 'package:simplecloudnotifier/utils/dialogs.dart';
|
||||||
import 'package:simplecloudnotifier/utils/ui.dart';
|
import 'package:simplecloudnotifier/utils/ui.dart';
|
||||||
|
|
||||||
class ChannelScannerResultChannelSubscribe extends StatefulWidget {
|
class ChannelScannerResultChannelSubscribe extends StatefulWidget {
|
||||||
@@ -172,6 +173,38 @@ class _ChannelScannerResultChannelSubscribeState extends State<ChannelScannerRes
|
|||||||
|
|
||||||
void _onSubscribe() async {
|
void _onSubscribe() async {
|
||||||
final auth = Provider.of<AppAuth>(context, listen: false);
|
final auth = Provider.of<AppAuth>(context, listen: false);
|
||||||
|
|
||||||
|
// Check if username is set
|
||||||
|
try {
|
||||||
|
final user = await auth.loadUser();
|
||||||
|
if (user.username == null || user.username!.isEmpty) {
|
||||||
|
// Show modal to set username
|
||||||
|
var newusername = await UIDialogs.showUsernameRequiredDialog(context);
|
||||||
|
|
||||||
|
if (newusername == null) return; // User cancelled
|
||||||
|
|
||||||
|
newusername = newusername.trim();
|
||||||
|
if (newusername.isEmpty) {
|
||||||
|
Toaster.error("Error", 'Username cannot be empty');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update username via API
|
||||||
|
try {
|
||||||
|
await APIClient.updateUser(auth, auth.userID!, username: newusername);
|
||||||
|
await auth.loadUser(force: true); // Refresh cached user
|
||||||
|
Toaster.success("Success", 'Username set');
|
||||||
|
} catch (e) {
|
||||||
|
Toaster.error("Error", 'Failed to set username: ${e.toString()}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Toaster.error("Error", 'Failed to load user data: ${e.toString()}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proceed with subscription
|
||||||
try {
|
try {
|
||||||
var sub = await APIClient.subscribeToChannelbyID(auth, widget.value.channelID, subscribeKey: widget.value.subscribeKey);
|
var sub = await APIClient.subscribeToChannelbyID(auth, widget.value.channelID, subscribeKey: widget.value.subscribeKey);
|
||||||
if (sub.confirmed) {
|
if (sub.confirmed) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
@@ -14,6 +15,7 @@ import 'package:simplecloudnotifier/pages/filtered_message_view/filtered_message
|
|||||||
import 'package:simplecloudnotifier/pages/subscription_view/subscription_view.dart';
|
import 'package:simplecloudnotifier/pages/subscription_view/subscription_view.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/types/immediate_future.dart';
|
import 'package:simplecloudnotifier/types/immediate_future.dart';
|
||||||
import 'package:simplecloudnotifier/utils/dialogs.dart';
|
import 'package:simplecloudnotifier/utils/dialogs.dart';
|
||||||
@@ -181,18 +183,20 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
children: [
|
children: [
|
||||||
_buildQRCode(context),
|
_buildQRCode(context),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidIdCardClip,
|
context: context,
|
||||||
title: 'ChannelID',
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
values: [channel.channelID],
|
title: 'ChannelID',
|
||||||
),
|
values: [channel.channelID],
|
||||||
UI.metaCard(
|
),
|
||||||
context: context,
|
if (AppSettings().showExtendedAttributes)
|
||||||
icon: FontAwesomeIcons.solidInputNumeric,
|
UI.metaCard(
|
||||||
title: 'InternalName',
|
context: context,
|
||||||
values: [channel.internalName],
|
icon: FontAwesomeIcons.solidInputNumeric,
|
||||||
),
|
title: 'InternalName',
|
||||||
|
values: [channel.internalName],
|
||||||
|
),
|
||||||
_buildDisplayNameCard(context, true),
|
_buildDisplayNameCard(context, true),
|
||||||
_buildDescriptionNameCard(context, true),
|
_buildDescriptionNameCard(context, true),
|
||||||
UI.metaCard(
|
UI.metaCard(
|
||||||
@@ -283,18 +287,20 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidIdCardClip,
|
context: context,
|
||||||
title: 'ChannelID',
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
values: [channel.channelID],
|
title: 'ChannelID',
|
||||||
),
|
values: [channel.channelID],
|
||||||
UI.metaCard(
|
),
|
||||||
context: context,
|
if (AppSettings().showExtendedAttributes)
|
||||||
icon: FontAwesomeIcons.solidInputNumeric,
|
UI.metaCard(
|
||||||
title: 'InternalName',
|
context: context,
|
||||||
values: [channel.internalName],
|
icon: FontAwesomeIcons.solidInputNumeric,
|
||||||
),
|
title: 'InternalName',
|
||||||
|
values: [channel.internalName],
|
||||||
|
),
|
||||||
_buildDisplayNameCard(context, false),
|
_buildDisplayNameCard(context, false),
|
||||||
_buildDescriptionNameCard(context, false),
|
_buildDescriptionNameCard(context, false),
|
||||||
subCard,
|
subCard,
|
||||||
@@ -305,7 +311,7 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
icon: FontAwesomeIcons.solidEnvelope,
|
icon: FontAwesomeIcons.solidEnvelope,
|
||||||
title: 'Messages',
|
title: 'Messages',
|
||||||
values: [channel.messagesSent.toString()],
|
values: [channel.messagesSent.toString()],
|
||||||
mainAction: (subscription != null && subscription!.confirmed) ? () => Navi.push(context, () => FilteredMessageViewPage(title: channel.displayName, filter: MessageFilter(channelIDs: [channel.channelID]))) : null,
|
mainAction: (subscription != null && subscription!.confirmed) ? () => Navi.push(context, () => FilteredMessageViewPage(title: channel.displayName, alertText: null, filter: MessageFilter(channelIDs: [channel.channelID]))) : null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -344,12 +350,20 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
future: _futureOwner.future,
|
future: _futureOwner.future,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
return UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidUser,
|
context: context,
|
||||||
title: 'Owner',
|
icon: FontAwesomeIcons.solidUser,
|
||||||
values: [channelPreview!.ownerUserID + (isOwned ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
|
title: 'Owner',
|
||||||
);
|
values: [channelPreview!.ownerUserID + (isOwned ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
|
||||||
|
);
|
||||||
|
else
|
||||||
|
return UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidUser,
|
||||||
|
title: 'Owner',
|
||||||
|
values: [(snapshot.data?.username ?? channelPreview!.ownerUserID) + (isOwned ? ' (you)' : '')],
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return UI.metaCard(
|
return UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -537,6 +551,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
widget.needsReload?.call();
|
widget.needsReload?.call();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to save DisplayName: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to save DisplayName');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to save DisplayName: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to save DisplayName: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to save DisplayName');
|
Toaster.error("Error", 'Failed to save DisplayName');
|
||||||
@@ -569,9 +586,12 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
widget.needsReload?.call();
|
widget.needsReload?.call();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to save DescriptionName: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to save description');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to save DescriptionName: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to save DescriptionName: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to save DescriptionName');
|
Toaster.error("Error", 'Failed to save description');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,6 +609,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
} else {
|
} else {
|
||||||
Toaster.success("Success", 'Requested subscription to channel');
|
Toaster.success("Success", 'Requested subscription to channel');
|
||||||
}
|
}
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to subscribe to channel');
|
Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -612,6 +635,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -630,6 +656,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -648,6 +677,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscribed to channel');
|
Toaster.success("Success", 'Subscribed to channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to subscribe to channel');
|
Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -664,6 +696,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscription succesfully revoked');
|
Toaster.success("Success", 'Subscription succesfully revoked');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to revoke subscription');
|
||||||
|
ApplicationLog.error('Failed to revoke subscription: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to revoke subscription');
|
Toaster.error("Error", 'Failed to revoke subscription');
|
||||||
ApplicationLog.error('Failed to revoke subscription: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to revoke subscription: ' + exc.toString(), trace: trace);
|
||||||
@@ -680,6 +715,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscription succesfully confirmed');
|
Toaster.success("Success", 'Subscription succesfully confirmed');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to confirm subscription');
|
||||||
|
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to confirm subscription');
|
Toaster.error("Error", 'Failed to confirm subscription');
|
||||||
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
||||||
@@ -696,6 +734,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscription request succesfully denied');
|
Toaster.success("Success", 'Subscription request succesfully denied');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to deny subscription');
|
||||||
|
ApplicationLog.error('Failed to deny subscription: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to deny subscription');
|
Toaster.error("Error", 'Failed to deny subscription');
|
||||||
ApplicationLog.error('Failed to deny subscription: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to deny subscription: ' + exc.toString(), trace: trace);
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/client.dart';
|
import 'package:simplecloudnotifier/models/client.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/pages/client_list/client_list_item.dart';
|
import 'package:simplecloudnotifier/pages/client_list/client_list_item.dart';
|
||||||
@@ -69,16 +71,35 @@ class _ClientListPageState extends State<ClientListPage> {
|
|||||||
showShare: false,
|
showShare: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
child: RefreshIndicator(
|
child: Column(
|
||||||
onRefresh: () => Future.sync(
|
children: [
|
||||||
() => _pagingController.refresh(),
|
BadgeDisplay(
|
||||||
),
|
text: "All clients connected with this account",
|
||||||
child: PagedListView<int, Client>(
|
icon: null,
|
||||||
pagingController: _pagingController,
|
mode: BadgeMode.info,
|
||||||
builderDelegate: PagedChildBuilderDelegate<Client>(
|
textAlign: TextAlign.left,
|
||||||
itemBuilder: (context, item, index) => ClientListItem(item: item),
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
|
child: _buildList(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => Future.sync(
|
||||||
|
() => _pagingController.refresh(),
|
||||||
|
),
|
||||||
|
child: PagedListView<int, Client>(
|
||||||
|
pagingController: _pagingController,
|
||||||
|
builderDelegate: PagedChildBuilderDelegate<Client>(
|
||||||
|
itemBuilder: (context, item, index) => ClientListItem(item: item),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:simplecloudnotifier/api/api_client.dart';
|
|||||||
import 'package:simplecloudnotifier/state/app_settings.dart';
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/globals.dart';
|
||||||
import 'package:simplecloudnotifier/utils/notifier.dart';
|
import 'package:simplecloudnotifier/utils/notifier.dart';
|
||||||
import 'package:simplecloudnotifier/utils/toaster.dart';
|
import 'package:simplecloudnotifier/utils/toaster.dart';
|
||||||
import 'package:simplecloudnotifier/utils/ui.dart';
|
import 'package:simplecloudnotifier/utils/ui.dart';
|
||||||
@@ -65,22 +66,31 @@ class _DebugActionsPageState extends State<DebugActionsPage> {
|
|||||||
onPressed: _sendTokenToServer,
|
onPressed: _sendTokenToServer,
|
||||||
text: 'Send FCM Token to Server',
|
text: 'Send FCM Token to Server',
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 4),
|
||||||
|
UI.button(
|
||||||
|
big: false,
|
||||||
|
onPressed: _updateClient,
|
||||||
|
text: 'Update Client on Server',
|
||||||
|
),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
UI.button(
|
UI.button(
|
||||||
big: false,
|
big: false,
|
||||||
onPressed: () => Notifier.showLocalNotification('', 'TEST_CHANNEL', "Test Channel", "Channel for testing", "Hello World", "Local Notification test", null, null),
|
onPressed: () => Notifier.showLocalNotification('', 'TEST_CHANNEL', "Test Channel", "Channel for testing", "Hello World", "Local Notification test", null, null),
|
||||||
text: 'Show local notification (generic)',
|
text: 'Show local notification (generic)',
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 4),
|
||||||
UI.button(
|
UI.button(
|
||||||
big: false,
|
big: false,
|
||||||
onPressed: () => Notifier.showLocalNotification('', 'TEST_CHANNEL', "Test Channel", "Channel for testing", "Hello World", "Local Notification test", null, 0),
|
onPressed: () => Notifier.showLocalNotification('', 'TEST_CHANNEL', "Test Channel", "Channel for testing", "Hello World", "Local Notification test", null, 0),
|
||||||
text: 'Show local notification (Prio = 0)',
|
text: 'Show local notification (Prio = 0)',
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 4),
|
||||||
UI.button(
|
UI.button(
|
||||||
big: false,
|
big: false,
|
||||||
onPressed: () => Notifier.showLocalNotification('', 'TEST_CHANNEL', "Test Channel", "Channel for testing", "Hello World", "Local Notification test", null, 1),
|
onPressed: () => Notifier.showLocalNotification('', 'TEST_CHANNEL', "Test Channel", "Channel for testing", "Hello World", "Local Notification test", null, 1),
|
||||||
text: 'Show local notification (Prio = 1)',
|
text: 'Show local notification (Prio = 1)',
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 4),
|
||||||
UI.button(
|
UI.button(
|
||||||
big: false,
|
big: false,
|
||||||
onPressed: () => Notifier.showLocalNotification('', 'TEST_CHANNEL', "Test Channel", "Channel for testing", "Hello World", "Local Notification test", null, 2),
|
onPressed: () => Notifier.showLocalNotification('', 'TEST_CHANNEL', "Test Channel", "Channel for testing", "Hello World", "Local Notification test", null, 2),
|
||||||
@@ -128,6 +138,26 @@ class _DebugActionsPageState extends State<DebugActionsPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _updateClient() async {
|
||||||
|
try {
|
||||||
|
final auth = AppAuth();
|
||||||
|
|
||||||
|
final clientID = auth.getClientID();
|
||||||
|
if (clientID == null) {
|
||||||
|
Toaster.error("Error", "No Client set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final newClient = await APIClient.updateClient(auth, clientID, agentModel: Globals().deviceModel, name: Globals().nameForClient(), agentVersion: Globals().version);
|
||||||
|
auth.setClientAndClientID(newClient);
|
||||||
|
|
||||||
|
Toaster.success("Success", "Client updated");
|
||||||
|
} catch (exc, trace) {
|
||||||
|
Toaster.error("Error", "An error occurred while updating the client: ${exc.toString()}");
|
||||||
|
ApplicationLog.error("An error occurred while updating the client: ${exc.toString()}", trace: trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _copyToken() async {
|
void _copyToken() async {
|
||||||
try {
|
try {
|
||||||
final fcmToken = await FirebaseMessaging.instance.getToken();
|
final fcmToken = await FirebaseMessaging.instance.getToken();
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ class _DebugRequestViewPageState extends State<DebugRequestViewPage> {
|
|||||||
void _copyCurl() {
|
void _copyCurl() {
|
||||||
final method = '-X ${widget.request.method}';
|
final method = '-X ${widget.request.method}';
|
||||||
final header = widget.request.requestHeaders.entries.map((v) => '-H "${v.key}: ${v.value}"').join(' ');
|
final header = widget.request.requestHeaders.entries.map((v) => '-H "${v.key}: ${v.value}"').join(' ');
|
||||||
final body = widget.request.requestBody.isNotEmpty ? '-d "${widget.request.requestBody}"' : '';
|
final body = widget.request.requestBody.isNotEmpty ? '-d \'${widget.request.requestBody}\'' : '';
|
||||||
|
|
||||||
final curlParts = ['curl', method, header, '"${widget.request.url}"', body];
|
final curlParts = ['curl', method, header, '"${widget.request.url}"', body];
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
import 'package:simplecloudnotifier/models/scn_message.dart';
|
import 'package:simplecloudnotifier/models/scn_message.dart';
|
||||||
@@ -17,11 +18,13 @@ class FilteredMessageViewPage extends StatefulWidget {
|
|||||||
const FilteredMessageViewPage({
|
const FilteredMessageViewPage({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.filter,
|
required this.filter,
|
||||||
|
required this.alertText,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
final MessageFilter filter;
|
final MessageFilter filter;
|
||||||
|
final String? alertText;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<FilteredMessageViewPage> createState() => _FilteredMessageViewPageState();
|
State<FilteredMessageViewPage> createState() => _FilteredMessageViewPageState();
|
||||||
@@ -87,31 +90,52 @@ class _FilteredMessageViewPageState extends State<FilteredMessageViewPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
Widget child = _buildMessageList(context);
|
||||||
|
|
||||||
|
if (widget.alertText != null) {
|
||||||
|
child = Column(
|
||||||
|
children: [
|
||||||
|
BadgeDisplay(
|
||||||
|
text: widget.alertText!,
|
||||||
|
icon: null,
|
||||||
|
mode: BadgeMode.info,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: child,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return SCNScaffold(
|
return SCNScaffold(
|
||||||
title: this.widget.title,
|
title: this.widget.title,
|
||||||
showSearch: false,
|
showSearch: false,
|
||||||
showShare: false,
|
showShare: false,
|
||||||
child: _buildMessageList(context),
|
child: Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMessageList(BuildContext context) {
|
Widget _buildMessageList(BuildContext context) {
|
||||||
return Padding(
|
return RefreshIndicator(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
onRefresh: () => Future.sync(
|
||||||
child: RefreshIndicator(
|
() => _pagingController.refresh(),
|
||||||
onRefresh: () => Future.sync(
|
),
|
||||||
() => _pagingController.refresh(),
|
child: PagedListView<String, SCNMessage>(
|
||||||
),
|
pagingController: _pagingController,
|
||||||
child: PagedListView<String, SCNMessage>(
|
builderDelegate: PagedChildBuilderDelegate<SCNMessage>(
|
||||||
pagingController: _pagingController,
|
itemBuilder: (context, item, index) => MessageListItem(
|
||||||
builderDelegate: PagedChildBuilderDelegate<SCNMessage>(
|
message: item,
|
||||||
itemBuilder: (context, item, index) => MessageListItem(
|
allChannels: _channels ?? {},
|
||||||
message: item,
|
onPressed: () {
|
||||||
allChannels: _channels ?? {},
|
Navi.push(context, () => MessageViewPage(messageID: item.messageID, preloadedData: (item,)));
|
||||||
onPressed: () {
|
},
|
||||||
Navi.push(context, () => MessageViewPage(messageID: item.messageID, preloadedData: (item,)));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class _KeyTokenCreateDialogState extends State<KeyTokenCreateDialog> {
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Create new key'),
|
title: const Text('Create new key'),
|
||||||
content: Container(
|
content: Container(
|
||||||
width: 0,
|
width: 9000,
|
||||||
height: 400,
|
height: 400,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/models/keytoken.dart';
|
import 'package:simplecloudnotifier/models/keytoken.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/utils/toaster.dart';
|
import 'package:simplecloudnotifier/utils/toaster.dart';
|
||||||
import 'package:simplecloudnotifier/utils/ui.dart';
|
import 'package:simplecloudnotifier/utils/ui.dart';
|
||||||
|
|
||||||
@@ -18,21 +20,38 @@ class KeyTokenCreatedModal extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final acc = AppAuth();
|
||||||
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('A new key was created'),
|
title: const Text('A new key was created'),
|
||||||
content: Container(
|
content: Container(
|
||||||
width: 0,
|
width: 9000,
|
||||||
height: 350,
|
height: 350,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
UI.metaCard(
|
const BadgeDisplay(
|
||||||
context: context,
|
text: "Please copy and save the token now, it cannot be retrieved later.",
|
||||||
icon: FontAwesomeIcons.solidIdCardClip,
|
icon: null,
|
||||||
title: 'KeyTokenID',
|
mode: BadgeMode.warn,
|
||||||
values: [keytoken.keytokenID],
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
if (acc.userID != null)
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
|
title: 'UserID',
|
||||||
|
values: [acc.userID!],
|
||||||
|
iconActions: [(FontAwesomeIcons.copy, null, () => _copy(acc.userID!))],
|
||||||
|
),
|
||||||
|
if (AppSettings().showExtendedAttributes)
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
|
title: 'KeyTokenID',
|
||||||
|
values: [keytoken.keytokenID],
|
||||||
|
),
|
||||||
UI.metaCard(
|
UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
icon: FontAwesomeIcons.solidInputText,
|
icon: FontAwesomeIcons.solidInputText,
|
||||||
@@ -45,19 +64,12 @@ class KeyTokenCreatedModal extends StatelessWidget {
|
|||||||
title: 'Permissions',
|
title: 'Permissions',
|
||||||
values: _formatPermissions(keytoken.permissions),
|
values: _formatPermissions(keytoken.permissions),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
|
||||||
const BadgeDisplay(
|
|
||||||
text: "Please copy and save the token now, it cannot be retrieved later.",
|
|
||||||
icon: null,
|
|
||||||
mode: BadgeMode.warn,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
UI.metaCard(
|
UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
icon: FontAwesomeIcons.solidKey,
|
icon: FontAwesomeIcons.solidKey,
|
||||||
title: 'Token',
|
title: 'Token',
|
||||||
values: [tokenValue.substring(0, 12) + '...'],
|
values: [tokenValue],
|
||||||
iconActions: [(FontAwesomeIcons.copy, null, _copy)],
|
iconActions: [(FontAwesomeIcons.copy, null, () => _copy(tokenValue))],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -89,9 +101,9 @@ class KeyTokenCreatedModal extends StatelessWidget {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _copy() {
|
void _copy(String v) {
|
||||||
Clipboard.setData(new ClipboardData(text: tokenValue));
|
Clipboard.setData(new ClipboardData(text: v));
|
||||||
Toaster.info("Clipboard", 'Copied text to Clipboard');
|
Toaster.info("Clipboard", 'Copied text to Clipboard');
|
||||||
print('================= [CLIPBOARD] =================\n${tokenValue}\n================= [/CLIPBOARD] =================');
|
print('================= [CLIPBOARD] =================\n${v}\n================= [/CLIPBOARD] =================');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/keytoken.dart';
|
import 'package:simplecloudnotifier/models/keytoken.dart';
|
||||||
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_create_modal.dart';
|
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_create_modal.dart';
|
||||||
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_created_modal.dart';
|
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_created_modal.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_list_item.dart';
|
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_list_item.dart';
|
||||||
@@ -72,16 +74,21 @@ class _KeyTokenListPageState extends State<KeyTokenListPage> {
|
|||||||
showShare: false,
|
showShare: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
child: RefreshIndicator(
|
child: Column(
|
||||||
onRefresh: () => Future.sync(
|
children: [
|
||||||
() => _pagingController.refresh(),
|
BadgeDisplay(
|
||||||
),
|
text: "These are your keys.\nKeys can be used to send messages and access your account.\n\nKeys can have different sets of permissions, the Admin-Key has full-access to your account",
|
||||||
child: PagedListView<int, KeyToken>(
|
icon: null,
|
||||||
pagingController: _pagingController,
|
mode: BadgeMode.info,
|
||||||
builderDelegate: PagedChildBuilderDelegate<KeyToken>(
|
textAlign: TextAlign.left,
|
||||||
itemBuilder: (context, item, index) => KeyTokenListItem(item: item, needsReload: _fullRefresh),
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
|
child: _buildList(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
@@ -97,6 +104,20 @@ class _KeyTokenListPageState extends State<KeyTokenListPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => Future.sync(
|
||||||
|
() => _pagingController.refresh(),
|
||||||
|
),
|
||||||
|
child: PagedListView<int, KeyToken>(
|
||||||
|
pagingController: _pagingController,
|
||||||
|
builderDelegate: PagedChildBuilderDelegate<KeyToken>(
|
||||||
|
itemBuilder: (context, item, index) => KeyTokenListItem(item: item, needsReload: _fullRefresh),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _created(KeyToken token, String tokValue) {
|
void _created(KeyToken token, String tokValue) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_pagingController.itemList?.insert(0, token);
|
_pagingController.itemList?.insert(0, token);
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class KeyTokenListItem extends StatelessWidget {
|
|||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: item.name, filter: MessageFilter(usedKeys: [item.keytokenID])));
|
Navi.push(context, () => FilteredMessageViewPage(title: item.name, alertText: 'All message sent with the key \'${item.name}\'', filter: MessageFilter(usedKeys: [item.keytokenID])));
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class _EditKeyTokenChannelsDialogState extends State<EditKeyTokenChannelsDialog>
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Channels'),
|
title: const Text('Channels'),
|
||||||
content: Container(
|
content: Container(
|
||||||
width: 0,
|
width: 9000,
|
||||||
height: 400,
|
height: 400,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class _EditKeyTokenPermissionsDialogState extends State<EditKeyTokenPermissionsD
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Permissions'),
|
title: const Text('Permissions'),
|
||||||
content: Container(
|
content: Container(
|
||||||
width: 0,
|
width: 9000,
|
||||||
height: 400,
|
height: 400,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
@@ -200,16 +201,17 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidIdCardClip,
|
context: context,
|
||||||
title: 'KeyTokenID',
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
values: [
|
title: 'KeyTokenID',
|
||||||
keytoken.keytokenID,
|
values: [
|
||||||
if (keytokenUserAccAdmin?.keytokenID == keytoken.keytokenID) '(Currently used as Admin-Token)',
|
keytoken.keytokenID,
|
||||||
if (keytokenUserAccSend?.keytokenID == keytoken.keytokenID) '(Currently used as Send-Token)',
|
if (keytokenUserAccAdmin?.keytokenID == keytoken.keytokenID) '(Currently used as Admin-Token)',
|
||||||
],
|
if (keytokenUserAccSend?.keytokenID == keytoken.keytokenID) '(Currently used as Send-Token)',
|
||||||
),
|
],
|
||||||
|
),
|
||||||
_buildNameCard(context, true),
|
_buildNameCard(context, true),
|
||||||
UI.metaCard(
|
UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -230,7 +232,7 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
title: 'Messages',
|
title: 'Messages',
|
||||||
values: [keytoken.messagesSent.toString()],
|
values: [keytoken.messagesSent.toString()],
|
||||||
mainAction: () {
|
mainAction: () {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: keytoken.name, filter: MessageFilter(usedKeys: [keytoken.keytokenID])));
|
Navi.push(context, () => FilteredMessageViewPage(title: keytoken.name, alertText: 'All message sent with the key \'${keytoken.name}\'', filter: MessageFilter(usedKeys: [keytoken.keytokenID])));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
..._buildPermissionCard(context, true, keytoken.toPreview()),
|
..._buildPermissionCard(context, true, keytoken.toPreview()),
|
||||||
@@ -249,12 +251,13 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidIdCardClip,
|
context: context,
|
||||||
title: 'KeyTokenID',
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
values: [keytoken.keytokenID],
|
title: 'KeyTokenID',
|
||||||
),
|
values: [keytoken.keytokenID],
|
||||||
|
),
|
||||||
_buildNameCard(context, false),
|
_buildNameCard(context, false),
|
||||||
_buildOwnerCard(context, false),
|
_buildOwnerCard(context, false),
|
||||||
..._buildPermissionCard(context, false, keytoken),
|
..._buildPermissionCard(context, false, keytoken),
|
||||||
@@ -269,12 +272,20 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
future: _futureOwner.future,
|
future: _futureOwner.future,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
return UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidUser,
|
context: context,
|
||||||
title: 'Owner',
|
icon: FontAwesomeIcons.solidUser,
|
||||||
values: [keytokenPreview!.ownerUserID + (isOwned ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
|
title: 'Owner',
|
||||||
);
|
values: [keytokenPreview!.ownerUserID + (isOwned ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
|
||||||
|
);
|
||||||
|
else
|
||||||
|
return UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidUser,
|
||||||
|
title: 'Owner',
|
||||||
|
values: [(snapshot.data?.username ?? keytokenPreview!.ownerUserID) + (isOwned ? ' (you)' : '')],
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return UI.metaCard(
|
return UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -543,6 +554,9 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
Toaster.info('Logout', 'Successfully deleted the key');
|
Toaster.info('Logout', 'Successfully deleted the key');
|
||||||
|
|
||||||
Navi.pop(context);
|
Navi.pop(context);
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to delete key: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to delete key');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to delete key');
|
Toaster.error("Error", 'Failed to delete key');
|
||||||
ApplicationLog.error('Failed to delete key: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to delete key: ' + exc.toString(), trace: trace);
|
||||||
@@ -563,6 +577,9 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
Toaster.info("Success", "Key updated");
|
Toaster.info("Success", "Key updated");
|
||||||
|
|
||||||
widget.needsReload?.call();
|
widget.needsReload?.call();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to update key');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to update key');
|
Toaster.error("Error", 'Failed to update key');
|
||||||
@@ -583,6 +600,9 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
Toaster.info("Success", "Key updated");
|
Toaster.info("Success", "Key updated");
|
||||||
|
|
||||||
widget.needsReload?.call();
|
widget.needsReload?.call();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to update key');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to update key');
|
Toaster.error("Error", 'Failed to update key');
|
||||||
@@ -603,6 +623,9 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
Toaster.info("Success", "Key updated");
|
Toaster.info("Success", "Key updated");
|
||||||
|
|
||||||
widget.needsReload?.call();
|
widget.needsReload?.call();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to update key');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to update key');
|
Toaster.error("Error", 'Failed to update key');
|
||||||
|
|||||||
@@ -141,6 +141,8 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
Widget _buildMessageView(BuildContext context, SCNMessage message, ChannelPreview? channel, KeyTokenPreview? token, UserPreview? user) {
|
Widget _buildMessageView(BuildContext context, SCNMessage message, ChannelPreview? channel, KeyTokenPreview? token, UserPreview? user) {
|
||||||
final userAccUserID = context.select<AppAuth, String?>((v) => v.userID);
|
final userAccUserID = context.select<AppAuth, String?>((v) => v.userID);
|
||||||
|
|
||||||
|
var cfg = AppSettings();
|
||||||
|
|
||||||
final child = Padding(
|
final child = Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
|
padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -150,6 +152,13 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
if (message.content != null) ..._buildMessageContent(context, message),
|
if (message.content != null) ..._buildMessageContent(context, message),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
|
if (cfg.showExtendedAttributes)
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
|
title: 'MessageID',
|
||||||
|
values: [message.messageID, if (message.userMessageID != null) message.userMessageID!],
|
||||||
|
),
|
||||||
if (message.senderName != null)
|
if (message.senderName != null)
|
||||||
UI.metaCard(
|
UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -157,66 +166,122 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
title: 'Sender',
|
title: 'Sender',
|
||||||
values: [message.senderName!],
|
values: [message.senderName!],
|
||||||
mainAction: () => {
|
mainAction: () => {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: message.senderName!, filter: MessageFilter(senderNames: [message.senderName!])))
|
Navi.push(
|
||||||
|
context,
|
||||||
|
() => FilteredMessageViewPage(
|
||||||
|
title: message.senderName!,
|
||||||
|
alertText: 'All message sent from \'${message.senderName!}\'',
|
||||||
|
filter: MessageFilter(senderNames: [message.senderName!]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (cfg.showExtendedAttributes)
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidGearCode,
|
||||||
|
title: 'Used Key',
|
||||||
|
values: [message.usedKeyID, token?.name ?? '...'],
|
||||||
|
mainAction: () {
|
||||||
|
if (message.senderUserID == userAccUserID) {
|
||||||
|
Navi.push(context, () => KeyTokenViewPage(keytokenID: message.usedKeyID, preloadedData: null, needsReload: null));
|
||||||
|
} else {
|
||||||
|
Navi.push(
|
||||||
|
context,
|
||||||
|
() => FilteredMessageViewPage(
|
||||||
|
title: token?.name ?? message.usedKeyID,
|
||||||
|
alertText: 'All message sent with the specified key',
|
||||||
|
filter: MessageFilter(usedKeys: [message.usedKeyID]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (!cfg.showExtendedAttributes && token != null)
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidGearCode,
|
||||||
|
title: 'Used Key',
|
||||||
|
values: [token.name],
|
||||||
|
mainAction: () {
|
||||||
|
if (message.senderUserID == userAccUserID) {
|
||||||
|
Navi.push(context, () => KeyTokenViewPage(keytokenID: message.usedKeyID, preloadedData: null, needsReload: null));
|
||||||
|
} else {
|
||||||
|
Navi.push(
|
||||||
|
context,
|
||||||
|
() => FilteredMessageViewPage(
|
||||||
|
title: token.name,
|
||||||
|
alertText: 'All message sent with key \'${token.name}\'',
|
||||||
|
filter: MessageFilter(usedKeys: [message.usedKeyID]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
UI.metaCard(
|
|
||||||
context: context,
|
|
||||||
icon: FontAwesomeIcons.solidGearCode,
|
|
||||||
title: 'KeyToken',
|
|
||||||
values: [message.usedKeyID, token?.name ?? '...'],
|
|
||||||
mainAction: () {
|
|
||||||
if (message.senderUserID == userAccUserID) {
|
|
||||||
Navi.push(context, () => KeyTokenViewPage(keytokenID: message.usedKeyID, preloadedData: null, needsReload: null));
|
|
||||||
} else {
|
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: token?.name ?? message.usedKeyID, filter: MessageFilter(usedKeys: [message.usedKeyID])));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
UI.metaCard(
|
|
||||||
context: context,
|
|
||||||
icon: FontAwesomeIcons.solidIdCardClip,
|
|
||||||
title: 'MessageID',
|
|
||||||
values: [message.messageID, message.userMessageID ?? ''],
|
|
||||||
),
|
|
||||||
UI.metaCard(
|
UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
icon: FontAwesomeIcons.solidSnake,
|
icon: FontAwesomeIcons.solidSnake,
|
||||||
title: 'Channel',
|
title: 'Channel',
|
||||||
values: [message.channelID, channel?.displayName ?? message.channelInternalName],
|
values: [if (cfg.showExtendedAttributes) message.channelID, channel?.displayName ?? message.channelInternalName],
|
||||||
mainAction: (channel != null)
|
mainAction: (channel != null)
|
||||||
? () {
|
? () {
|
||||||
Navi.push(context, () => ChannelViewPage(channelID: channel.channelID, preloadedData: null, needsReload: null));
|
Navi.push(context, () => ChannelViewPage(channelID: channel.channelID, preloadedData: null, needsReload: null));
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
UI.metaCard(
|
UI.metaCard(context: context, icon: FontAwesomeIcons.solidTimer, title: 'Timestamp', values: [message.timestamp]),
|
||||||
context: context,
|
if (cfg.showExtendedAttributes)
|
||||||
icon: FontAwesomeIcons.solidTimer,
|
UI.metaCard(
|
||||||
title: 'Timestamp',
|
context: context,
|
||||||
values: [message.timestamp],
|
icon: FontAwesomeIcons.solidUser,
|
||||||
),
|
title: 'User',
|
||||||
UI.metaCard(
|
values: [user?.userID ?? message.senderUserID, if (user?.username != null) user?.username ?? ''],
|
||||||
context: context,
|
mainAction: () => Navi.push(
|
||||||
icon: FontAwesomeIcons.solidUser,
|
context,
|
||||||
title: 'User',
|
() => FilteredMessageViewPage(
|
||||||
values: [user?.userID ?? message.senderUserID, user?.username ?? ''],
|
title: user?.username ?? message.senderUserID,
|
||||||
mainAction: () => Navi.push(context, () => FilteredMessageViewPage(title: user?.username ?? message.senderUserID, filter: MessageFilter(senderUserID: [message.senderUserID]))),
|
alertText: 'All message sent by the specified account',
|
||||||
),
|
filter: MessageFilter(senderUserID: [message.senderUserID]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!cfg.showExtendedAttributes)
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidUser,
|
||||||
|
title: 'User',
|
||||||
|
values: [user?.username ?? user?.userID ?? message.senderUserID],
|
||||||
|
mainAction: () => Navi.push(
|
||||||
|
context,
|
||||||
|
() => FilteredMessageViewPage(
|
||||||
|
title: user?.username ?? message.senderUserID,
|
||||||
|
alertText: 'All message sent by the specified account',
|
||||||
|
filter: MessageFilter(senderUserID: [message.senderUserID]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
UI.metaCard(
|
UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
icon: FontAwesomeIcons.solidBolt,
|
icon: FontAwesomeIcons.solidBolt,
|
||||||
title: 'Priority',
|
title: 'Priority',
|
||||||
values: [_prettyPrintPriority(message.priority)],
|
values: [_prettyPrintPriority(message.priority)],
|
||||||
mainAction: () => Navi.push(context, () => FilteredMessageViewPage(title: "Priority ${message.priority}", filter: MessageFilter(priority: [message.priority]))),
|
mainAction: () => Navi.push(
|
||||||
|
context,
|
||||||
|
() => FilteredMessageViewPage(
|
||||||
|
title: "Priority ${message.priority}",
|
||||||
|
alertText: 'All message sent with priority ' + _prettyPrintPriority(message.priority),
|
||||||
|
filter: MessageFilter(priority: [message.priority]),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (message.senderUserID == userAccUserID)
|
if (message.senderUserID == userAccUserID)
|
||||||
UI.button(
|
UI.button(
|
||||||
text: "Delete Message",
|
text: "Delete Message",
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Toaster.info("Not Implemented", "... will be implemented in a later version"); // TODO
|
Toaster.info("Not Implemented", "... will be implemented in a later version"); // TODO
|
||||||
},
|
},
|
||||||
color: Colors.red[900]),
|
color: Colors.red[900],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -234,16 +299,11 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
thumbVisibility: false,
|
thumbVisibility: false,
|
||||||
interactive: true,
|
interactive: true,
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(controller: _controller, child: child),
|
||||||
controller: _controller,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(child: child);
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,12 +317,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
return [
|
return [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
UI.channelChip(
|
UI.channelChip(context: context, text: _resolveChannelName(channel, message), margin: const EdgeInsets.fromLTRB(0, 0, 4, 0), fontSize: 16),
|
||||||
context: context,
|
|
||||||
text: _resolveChannelName(channel, message),
|
|
||||||
margin: const EdgeInsets.fromLTRB(0, 0, 4, 0),
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
Expanded(child: SizedBox()),
|
Expanded(child: SizedBox()),
|
||||||
Text(dateFormat.format(DateTime.parse(message.timestamp)), style: const TextStyle(fontSize: 14)),
|
Text(dateFormat.format(DateTime.parse(message.timestamp)), style: const TextStyle(fontSize: 14)),
|
||||||
],
|
],
|
||||||
@@ -310,12 +365,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
),
|
),
|
||||||
borderColor: (message.priority == 2) ? Colors.red[900] : null,
|
borderColor: (message.priority == 2) ? Colors.red[900] : null,
|
||||||
)
|
)
|
||||||
: UI.box(
|
: UI.box(context: context, padding: const EdgeInsets.all(4), child: Text(message.content ?? ''), borderColor: (message.priority == 2) ? Colors.red[900] : null),
|
||||||
context: context,
|
|
||||||
padding: const EdgeInsets.all(4),
|
|
||||||
child: Text(message.content ?? ''),
|
|
||||||
borderColor: (message.priority == 2) ? Colors.red[900] : null,
|
|
||||||
)
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/globals.dart';
|
import 'package:simplecloudnotifier/state/globals.dart';
|
||||||
@@ -283,12 +284,17 @@ class _SendRootPageState extends State<SendRootPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await APIClient.sendMessage(acc.userID!, acc.tokenSend!, _msgContent.text);
|
var content = (_msgContent.text != '') ? _msgContent.text : null;
|
||||||
|
|
||||||
|
await APIClient.sendMessage(acc.userID!, acc.tokenSend!, _msgTitle.text, content: content);
|
||||||
Toaster.success("Success", 'Message sent');
|
Toaster.success("Success", 'Message sent');
|
||||||
setState(() {
|
setState(() {
|
||||||
_msgTitle.clear();
|
_msgTitle.clear();
|
||||||
_msgContent.clear();
|
_msgContent.clear();
|
||||||
});
|
});
|
||||||
|
} on APIException catch (e, stackTrace) {
|
||||||
|
if (!e.toastShown) Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
||||||
|
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
||||||
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
||||||
@@ -302,12 +308,19 @@ class _SendRootPageState extends State<SendRootPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await APIClient.sendMessage(acc.userID!, acc.tokenSend!, _msgContent.text, channel: _channelName.text, senderName: _senderName.text, priority: _priority);
|
var content = (_msgContent.text != '') ? _msgContent.text : null;
|
||||||
|
var channel = (_channelName.text != '') ? _channelName.text : null;
|
||||||
|
var sender = (_senderName.text != '') ? _senderName.text : null;
|
||||||
|
|
||||||
|
await APIClient.sendMessage(acc.userID!, acc.tokenSend!, _msgTitle.text, content: content, channel: channel, senderName: sender, priority: _priority);
|
||||||
Toaster.success("Success", 'Message sent');
|
Toaster.success("Success", 'Message sent');
|
||||||
setState(() {
|
setState(() {
|
||||||
_msgTitle.clear();
|
_msgTitle.clear();
|
||||||
_msgContent.clear();
|
_msgContent.clear();
|
||||||
});
|
});
|
||||||
|
} on APIException catch (e, stackTrace) {
|
||||||
|
if (!e.toastShown) Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
||||||
|
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
||||||
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/sender_name_statistics.dart';
|
import 'package:simplecloudnotifier/models/sender_name_statistics.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/pages/sender_list/sender_list_item.dart';
|
import 'package:simplecloudnotifier/pages/sender_list/sender_list_item.dart';
|
||||||
@@ -69,16 +71,35 @@ class _SenderListPageState extends State<SenderListPage> {
|
|||||||
showShare: false,
|
showShare: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
child: RefreshIndicator(
|
child: Column(
|
||||||
onRefresh: () => Future.sync(
|
children: [
|
||||||
() => _pagingController.refresh(),
|
BadgeDisplay(
|
||||||
),
|
text: "All sender used to send messages to this account",
|
||||||
child: PagedListView<int, SenderNameStatistics>(
|
icon: null,
|
||||||
pagingController: _pagingController,
|
mode: BadgeMode.info,
|
||||||
builderDelegate: PagedChildBuilderDelegate<SenderNameStatistics>(
|
textAlign: TextAlign.left,
|
||||||
itemBuilder: (context, item, index) => SenderListItem(item: item),
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
|
child: _buildList(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => Future.sync(
|
||||||
|
() => _pagingController.refresh(),
|
||||||
|
),
|
||||||
|
child: PagedListView<int, SenderNameStatistics>(
|
||||||
|
pagingController: _pagingController,
|
||||||
|
builderDelegate: PagedChildBuilderDelegate<SenderNameStatistics>(
|
||||||
|
itemBuilder: (context, item, index) => SenderListItem(item: item),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class SenderListItem extends StatelessWidget {
|
|||||||
color: Theme.of(context).cardTheme.color,
|
color: Theme.of(context).cardTheme.color,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: item.name, filter: MessageFilter(senderNames: [item.name])));
|
Navi.push(context, () => FilteredMessageViewPage(title: item.name, alertText: 'All message sent from \'${item.name!}\'', filter: MessageFilter(senderNames: [item.name])));
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
@@ -71,7 +71,7 @@ class SenderListItem extends StatelessWidget {
|
|||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: item.name, filter: MessageFilter(senderNames: [item.name])));
|
Navi.push(context, () => FilteredMessageViewPage(title: item.name, alertText: 'All message sent from \'${item.name!}\'', filter: MessageFilter(senderNames: [item.name])));
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
|
|||||||
@@ -146,6 +146,18 @@ class _SettingsRootPageState extends State<SettingsRootPage> {
|
|||||||
title: Text('Refresh messages on app resume'),
|
title: Text('Refresh messages on app resume'),
|
||||||
onToggle: (value) => AppSettings().update((p) => p.alwaysBackgroundRefreshMessageListOnLifecycleResume = !p.alwaysBackgroundRefreshMessageListOnLifecycleResume),
|
onToggle: (value) => AppSettings().update((p) => p.alwaysBackgroundRefreshMessageListOnLifecycleResume = !p.alwaysBackgroundRefreshMessageListOnLifecycleResume),
|
||||||
),
|
),
|
||||||
|
SettingsTile.switchTile(
|
||||||
|
initialValue: cfg.showInfoAlerts,
|
||||||
|
leading: Icon(FontAwesomeIcons.solidCircleInfo),
|
||||||
|
title: Text('Show various helpful info boxes'),
|
||||||
|
onToggle: (value) => AppSettings().update((p) => p.showInfoAlerts = !p.showInfoAlerts),
|
||||||
|
),
|
||||||
|
SettingsTile.switchTile(
|
||||||
|
initialValue: cfg.showExtendedAttributes,
|
||||||
|
leading: Icon(FontAwesomeIcons.solidSheetPlastic),
|
||||||
|
title: Text('Show all attributes of entities'),
|
||||||
|
onToggle: (value) => AppSettings().update((p) => p.showExtendedAttributes = !p.showExtendedAttributes),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
@@ -177,7 +189,7 @@ class _SettingsRootPageState extends State<SettingsRootPage> {
|
|||||||
value: Column(
|
value: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(GitStamp.sha.substring(0, 7) + ' +' + Globals().buildNumber),
|
Text('${GitStamp.sha.substring(0, GitStamp.sha.length.clamp(0, 7))} +${Globals().buildNumber}'),
|
||||||
Text("( " + cfg.dateFormat.dateFormat().format(DateTime.parse(GitStamp.buildDateTime).toLocal()) + " )", style: TextStyle(fontStyle: FontStyle.italic)),
|
Text("( " + cfg.dateFormat.dateFormat().format(DateTime.parse(GitStamp.buildDateTime).toLocal()) + " )", style: TextStyle(fontStyle: FontStyle.italic)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,15 +2,41 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/filter_chips/filter_chips.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
import 'package:simplecloudnotifier/models/subscription.dart';
|
import 'package:simplecloudnotifier/models/subscription.dart';
|
||||||
import 'package:simplecloudnotifier/models/user.dart';
|
import 'package:simplecloudnotifier/models/user.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/pages/subscription_list/subscription_list_item.dart';
|
import 'package:simplecloudnotifier/pages/subscription_list/subscription_list_item.dart';
|
||||||
import 'package:simplecloudnotifier/state/scn_data_cache.dart';
|
import 'package:simplecloudnotifier/state/scn_data_cache.dart';
|
||||||
|
|
||||||
|
enum SubscriptionListFilter {
|
||||||
|
ALL,
|
||||||
|
INACTIVE,
|
||||||
|
OWN,
|
||||||
|
EXTERNAL,
|
||||||
|
INCOMING;
|
||||||
|
|
||||||
|
SubscriptionFilter toAPIFilter() {
|
||||||
|
switch (this) {
|
||||||
|
case ALL:
|
||||||
|
return SubscriptionFilter.ALL;
|
||||||
|
case INACTIVE:
|
||||||
|
return SubscriptionFilter.OWNED_INACTIVE;
|
||||||
|
case OWN:
|
||||||
|
return SubscriptionFilter.OWNED_ACTIVE;
|
||||||
|
case EXTERNAL:
|
||||||
|
return SubscriptionFilter.EXTERNAL_ALL;
|
||||||
|
case INCOMING:
|
||||||
|
return SubscriptionFilter.INCOMING_ALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SubscriptionListPage extends StatefulWidget {
|
class SubscriptionListPage extends StatefulWidget {
|
||||||
const SubscriptionListPage({super.key});
|
const SubscriptionListPage({super.key});
|
||||||
|
|
||||||
@@ -24,6 +50,8 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
|
|||||||
final userCache = Map<String, UserPreview>();
|
final userCache = Map<String, UserPreview>();
|
||||||
final channelCache = Map<String, ChannelPreview>();
|
final channelCache = Map<String, ChannelPreview>();
|
||||||
|
|
||||||
|
SubscriptionListFilter filter = SubscriptionListFilter.ALL;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -58,7 +86,7 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final items = (await APIClient.getSubscriptionList(acc)).toList();
|
final items = (await APIClient.getSubscriptionList(acc, filter.toAPIFilter())).toList();
|
||||||
|
|
||||||
items.sort((a, b) => -1 * a.timestampCreated.compareTo(b.timestampCreated));
|
items.sort((a, b) => -1 * a.timestampCreated.compareTo(b.timestampCreated));
|
||||||
|
|
||||||
@@ -93,16 +121,51 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
|
|||||||
showShare: false,
|
showShare: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
child: RefreshIndicator(
|
child: Column(
|
||||||
onRefresh: () => Future.sync(
|
children: [
|
||||||
() => _pagingController.refresh(),
|
BadgeDisplay(
|
||||||
),
|
text: "These are subscriptions to individual Channels\n\nThey contain to your own channels, subscriptions to foreign channels and subscriptions of other users to your channels (active and requested).",
|
||||||
child: PagedListView<int, Subscription>(
|
icon: null,
|
||||||
pagingController: _pagingController,
|
mode: BadgeMode.info,
|
||||||
builderDelegate: PagedChildBuilderDelegate<Subscription>(
|
textAlign: TextAlign.left,
|
||||||
itemBuilder: (context, item, index) => SubscriptionListItem(item: item, userCache: userCache, channelCache: channelCache, needsReload: fullRefresh),
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
),
|
),
|
||||||
),
|
FilterChips<SubscriptionListFilter>(
|
||||||
|
options: [
|
||||||
|
(SubscriptionListFilter.ALL, 'All'),
|
||||||
|
(SubscriptionListFilter.OWN, 'Own'),
|
||||||
|
(SubscriptionListFilter.EXTERNAL, 'External'),
|
||||||
|
(SubscriptionListFilter.INCOMING, 'Incoming'),
|
||||||
|
(SubscriptionListFilter.INACTIVE, 'Inactive'),
|
||||||
|
],
|
||||||
|
value: filter,
|
||||||
|
onChanged: (newFilter) {
|
||||||
|
setState(() {
|
||||||
|
filter = newFilter;
|
||||||
|
_pagingController.refresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildList(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => Future.sync(
|
||||||
|
() => _pagingController.refresh(),
|
||||||
|
),
|
||||||
|
child: PagedListView<int, Subscription>(
|
||||||
|
pagingController: _pagingController,
|
||||||
|
builderDelegate: PagedChildBuilderDelegate<Subscription>(
|
||||||
|
itemBuilder: (context, item, index) => SubscriptionListItem(item: item, userCache: userCache, channelCache: channelCache, needsReload: fullRefresh),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
@@ -168,12 +169,13 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidIdCardClip,
|
context: context,
|
||||||
title: 'SubscriptionID',
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
values: [subscription.subscriptionID],
|
title: 'SubscriptionID',
|
||||||
),
|
values: [subscription.subscriptionID],
|
||||||
|
),
|
||||||
_buildChannelOwnerCard(context, subscription),
|
_buildChannelOwnerCard(context, subscription),
|
||||||
_buildSubscriberCard(context, subscription),
|
_buildSubscriberCard(context, subscription),
|
||||||
_buildChannelCard(context, subscription),
|
_buildChannelCard(context, subscription),
|
||||||
@@ -201,12 +203,13 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidIdCardClip,
|
context: context,
|
||||||
title: 'SubscriptionID',
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
values: [subscription.subscriptionID],
|
title: 'SubscriptionID',
|
||||||
),
|
values: [subscription.subscriptionID],
|
||||||
|
),
|
||||||
_buildChannelOwnerCard(context, subscription),
|
_buildChannelOwnerCard(context, subscription),
|
||||||
_buildSubscriberCard(context, subscription),
|
_buildSubscriberCard(context, subscription),
|
||||||
_buildChannelCard(context, subscription),
|
_buildChannelCard(context, subscription),
|
||||||
@@ -236,12 +239,13 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidIdCardClip,
|
context: context,
|
||||||
title: 'SubscriptionID',
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
values: [subscription.subscriptionID],
|
title: 'SubscriptionID',
|
||||||
),
|
values: [subscription.subscriptionID],
|
||||||
|
),
|
||||||
_buildChannelOwnerCard(context, subscription),
|
_buildChannelOwnerCard(context, subscription),
|
||||||
_buildSubscriberCard(context, subscription),
|
_buildSubscriberCard(context, subscription),
|
||||||
_buildChannelCard(context, subscription),
|
_buildChannelCard(context, subscription),
|
||||||
@@ -271,12 +275,20 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
future: _futureChannelOwner.future,
|
future: _futureChannelOwner.future,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
return UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidUser,
|
context: context,
|
||||||
title: 'Channel Owner',
|
icon: FontAwesomeIcons.solidUser,
|
||||||
values: [subscription.channelOwnerUserID + (isSelf ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
|
title: 'Channel Owner',
|
||||||
);
|
values: [subscription.channelOwnerUserID + (isSelf ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
|
||||||
|
);
|
||||||
|
else
|
||||||
|
return UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidUser,
|
||||||
|
title: 'Channel Owner',
|
||||||
|
values: [(snapshot.data?.username ?? subscription.channelOwnerUserID) + (isSelf ? ' (you)' : '')],
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return UI.metaCard(
|
return UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -298,12 +310,20 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
future: _futureSubscriber.future,
|
future: _futureSubscriber.future,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
return UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidUser,
|
context: context,
|
||||||
title: 'Subscriber',
|
icon: FontAwesomeIcons.solidUser,
|
||||||
values: [subscription.subscriberUserID + (isSelf ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
|
title: 'Subscriber',
|
||||||
);
|
values: [subscription.subscriberUserID + (isSelf ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
|
||||||
|
);
|
||||||
|
else
|
||||||
|
return UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidUser,
|
||||||
|
title: 'Subscriber',
|
||||||
|
values: [(snapshot.data?.username ?? subscription.subscriberUserID) + (isSelf ? ' (you)' : '')],
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return UI.metaCard(
|
return UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -321,13 +341,22 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
future: _futureChannel.future,
|
future: _futureChannel.future,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return UI.metaCard(
|
if (AppSettings().showExtendedAttributes)
|
||||||
context: context,
|
return UI.metaCard(
|
||||||
icon: FontAwesomeIcons.solidSnake,
|
context: context,
|
||||||
title: 'Channel',
|
icon: FontAwesomeIcons.solidSnake,
|
||||||
values: [subscription.channelID, snapshot.data!.displayName],
|
title: 'Channel',
|
||||||
mainAction: () => Navi.push(context, () => ChannelViewPage(channelID: subscription.channelID, preloadedData: null, needsReload: null)),
|
values: [subscription.channelID, snapshot.data!.displayName],
|
||||||
);
|
mainAction: () => Navi.push(context, () => ChannelViewPage(channelID: subscription.channelID, preloadedData: null, needsReload: null)),
|
||||||
|
);
|
||||||
|
else
|
||||||
|
return UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidSnake,
|
||||||
|
title: 'Channel',
|
||||||
|
values: [snapshot.data!.displayName],
|
||||||
|
mainAction: () => Navi.push(context, () => ChannelViewPage(channelID: subscription.channelID, preloadedData: null, needsReload: null)),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return UI.metaCard(
|
return UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -407,6 +436,9 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscription succesfully confirmed');
|
Toaster.success("Success", 'Subscription succesfully confirmed');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to confirm subscription');
|
||||||
|
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to confirm subscription');
|
Toaster.error("Error", 'Failed to confirm subscription');
|
||||||
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
||||||
@@ -429,6 +461,9 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
Navi.pop(context);
|
Navi.pop(context);
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -447,6 +482,9 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -465,6 +503,9 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscribed to channel');
|
Toaster.success("Success", 'Subscribed to channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to subscribe to channel');
|
Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class AppAuth extends ChangeNotifier implements TokenSource {
|
|||||||
|
|
||||||
final user = await APIClient.getUser(DirectTokenSource(oldUserID, oldUserKey), oldUserID);
|
final user = await APIClient.getUser(DirectTokenSource(oldUserID, oldUserKey), oldUserID);
|
||||||
|
|
||||||
final client = await APIClient.addClient(DirectTokenSource(oldUserID, oldUserKey), fcmToken, Globals().deviceModel, Globals().version, Globals().hostname, Globals().clientType);
|
final client = await APIClient.addClient(DirectTokenSource(oldUserID, oldUserKey), fcmToken, Globals().deviceModel, Globals().version, Globals().nameForClient(), Globals().clientType);
|
||||||
|
|
||||||
set(user, client, oldUserKey, newTokenSend.token);
|
set(user, client, oldUserKey, newTokenSend.token);
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ class AppAuth extends ChangeNotifier implements TokenSource {
|
|||||||
return _user?.$1;
|
return _user?.$1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Client?> loadClient({bool force = false, Duration? forceIfOlder = null}) async {
|
Future<Client?> loadClient({bool force = false, Duration? forceIfOlder = null, bool onlyCached = false}) async {
|
||||||
if (forceIfOlder != null && _client != null && _client!.$2.difference(DateTime.now()) > forceIfOlder) {
|
if (forceIfOlder != null && _client != null && _client!.$2.difference(DateTime.now()) > forceIfOlder) {
|
||||||
force = true;
|
force = true;
|
||||||
}
|
}
|
||||||
@@ -245,6 +245,10 @@ class AppAuth extends ChangeNotifier implements TokenSource {
|
|||||||
throw Exception('Not authenticated');
|
throw Exception('Not authenticated');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (onlyCached) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final client = await APIClient.getClient(this, _clientID!);
|
final client = await APIClient.getClient(this, _clientID!);
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ class AppSettings extends ChangeNotifier {
|
|||||||
bool alwaysBackgroundRefreshMessageListOnLifecycleResume = true;
|
bool alwaysBackgroundRefreshMessageListOnLifecycleResume = true;
|
||||||
AppSettingsDateFormat dateFormat = AppSettingsDateFormat.ISO;
|
AppSettingsDateFormat dateFormat = AppSettingsDateFormat.ISO;
|
||||||
int messagePreviewLength = 3;
|
int messagePreviewLength = 3;
|
||||||
|
bool showInfoAlerts = true;
|
||||||
|
bool showExtendedAttributes = false;
|
||||||
|
|
||||||
AppNotificationSettings notification0 = AppNotificationSettings();
|
AppNotificationSettings notification0 = AppNotificationSettings();
|
||||||
AppNotificationSettings notification1 = AppNotificationSettings();
|
AppNotificationSettings notification1 = AppNotificationSettings();
|
||||||
@@ -80,6 +82,8 @@ class AppSettings extends ChangeNotifier {
|
|||||||
alwaysBackgroundRefreshMessageListOnLifecycleResume = true;
|
alwaysBackgroundRefreshMessageListOnLifecycleResume = true;
|
||||||
dateFormat = AppSettingsDateFormat.ISO;
|
dateFormat = AppSettingsDateFormat.ISO;
|
||||||
messagePreviewLength = 3;
|
messagePreviewLength = 3;
|
||||||
|
showInfoAlerts = true;
|
||||||
|
showExtendedAttributes = false;
|
||||||
|
|
||||||
notification0 = AppNotificationSettings();
|
notification0 = AppNotificationSettings();
|
||||||
notification1 = AppNotificationSettings();
|
notification1 = AppNotificationSettings();
|
||||||
@@ -97,6 +101,8 @@ class AppSettings extends ChangeNotifier {
|
|||||||
alwaysBackgroundRefreshMessageListOnLifecycleResume = Globals().sharedPrefs.getBool('settings.alwaysBackgroundRefreshMessageListOnLifecycleResume') ?? alwaysBackgroundRefreshMessageListOnLifecycleResume;
|
alwaysBackgroundRefreshMessageListOnLifecycleResume = Globals().sharedPrefs.getBool('settings.alwaysBackgroundRefreshMessageListOnLifecycleResume') ?? alwaysBackgroundRefreshMessageListOnLifecycleResume;
|
||||||
dateFormat = AppSettingsDateFormat.parse(Globals().sharedPrefs.getString('settings.dateFormat')) ?? dateFormat;
|
dateFormat = AppSettingsDateFormat.parse(Globals().sharedPrefs.getString('settings.dateFormat')) ?? dateFormat;
|
||||||
messagePreviewLength = Globals().sharedPrefs.getInt('settings.messagePreviewLength') ?? messagePreviewLength;
|
messagePreviewLength = Globals().sharedPrefs.getInt('settings.messagePreviewLength') ?? messagePreviewLength;
|
||||||
|
showInfoAlerts = Globals().sharedPrefs.getBool('settings.showInfoAlerts') ?? showInfoAlerts;
|
||||||
|
showExtendedAttributes = Globals().sharedPrefs.getBool('settings.showExtendedAttributes') ?? showExtendedAttributes;
|
||||||
|
|
||||||
notification0 = AppNotificationSettings.load(Globals().sharedPrefs, 'settings.notification0');
|
notification0 = AppNotificationSettings.load(Globals().sharedPrefs, 'settings.notification0');
|
||||||
notification1 = AppNotificationSettings.load(Globals().sharedPrefs, 'settings.notification1');
|
notification1 = AppNotificationSettings.load(Globals().sharedPrefs, 'settings.notification1');
|
||||||
@@ -112,6 +118,8 @@ class AppSettings extends ChangeNotifier {
|
|||||||
await Globals().sharedPrefs.setBool('settings.alwaysBackgroundRefreshMessageListOnLifecycleResume', alwaysBackgroundRefreshMessageListOnLifecycleResume);
|
await Globals().sharedPrefs.setBool('settings.alwaysBackgroundRefreshMessageListOnLifecycleResume', alwaysBackgroundRefreshMessageListOnLifecycleResume);
|
||||||
await Globals().sharedPrefs.setString('settings.dateFormat', dateFormat.key);
|
await Globals().sharedPrefs.setString('settings.dateFormat', dateFormat.key);
|
||||||
await Globals().sharedPrefs.setInt('settings.messagePreviewLength', messagePreviewLength);
|
await Globals().sharedPrefs.setInt('settings.messagePreviewLength', messagePreviewLength);
|
||||||
|
await Globals().sharedPrefs.setBool('settings.showInfoAlerts', showInfoAlerts);
|
||||||
|
await Globals().sharedPrefs.setBool('settings.showExtendedAttributes', showExtendedAttributes);
|
||||||
|
|
||||||
await notification0.save(Globals().sharedPrefs, 'settings.notification0');
|
await notification0.save(Globals().sharedPrefs, 'settings.notification0');
|
||||||
await notification1.save(Globals().sharedPrefs, 'settings.notification1');
|
await notification1.save(Globals().sharedPrefs, 'settings.notification1');
|
||||||
|
|||||||
@@ -92,4 +92,12 @@ class Globals {
|
|||||||
Future<bool> setPrefFCMToken(String value) {
|
Future<bool> setPrefFCMToken(String value) {
|
||||||
return sharedPrefs.setString("fcm.token", value);
|
return sharedPrefs.setString("fcm.token", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String nameForClient() {
|
||||||
|
if (this.deviceName.isNotEmpty) {
|
||||||
|
return this.deviceName;
|
||||||
|
} else {
|
||||||
|
return this.hostname;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,40 @@ class UIDialogs {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<String?> showUsernameRequiredDialog(BuildContext context) {
|
||||||
|
var _textFieldController = TextEditingController();
|
||||||
|
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text('Username Required'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Please set a public username to subscribe to channels from other users.'),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
autofocus: true,
|
||||||
|
controller: _textFieldController,
|
||||||
|
decoration: InputDecoration(hintText: 'Enter username'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text('Cancel'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(_textFieldController.text),
|
||||||
|
child: Text('OK'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static Future<bool> showConfirmDialog(BuildContext context, String title, {String? text, String? okText, String? cancelText}) {
|
static Future<bool> showConfirmDialog(BuildContext context, String title, {String? text, String? okText, String? cancelText}) {
|
||||||
return showDialog<bool>(
|
return showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||||
|
|||||||
@@ -21,12 +21,14 @@
|
|||||||
/* End PBXAggregateTarget section */
|
/* End PBXAggregateTarget section */
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
30051E098E5DDBD2E1D16F95 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 260FDBA879E00B3BD57336B6 /* Pods_RunnerTests.framework */; };
|
||||||
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
|
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
|
||||||
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
|
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
|
||||||
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
|
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
|
||||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
||||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
||||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
||||||
|
D6E1D80C6733E06BC2484012 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71450153D8FB1F125629A01B /* Pods_Runner.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -60,11 +62,12 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
260FDBA879E00B3BD57336B6 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||||
33CC10ED2044A3C60003C045 /* simplecloudnotifier.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "simplecloudnotifier.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
33CC10ED2044A3C60003C045 /* simplecloudnotifier.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = simplecloudnotifier.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||||
@@ -76,8 +79,15 @@
|
|||||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
|
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
|
||||||
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
||||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
||||||
|
71450153D8FB1F125629A01B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
71DA993D9EDD2A4A54234FF2 /* 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>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9496FF8D8B46D4619EDAA5EF /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
|
B9F3F9EB590C25EBDAE05E03 /* 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>"; };
|
||||||
|
BF6145095420650A6CCA9EE3 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
C0E65636E0666802F744A5E3 /* 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>"; };
|
||||||
|
C71F9C04DE64EB26EF8E0E58 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -85,6 +95,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
30051E098E5DDBD2E1D16F95 /* Pods_RunnerTests.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -92,12 +103,27 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
D6E1D80C6733E06BC2484012 /* Pods_Runner.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
25379216233BB0105FDA3DD4 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C0E65636E0666802F744A5E3 /* Pods-Runner.debug.xcconfig */,
|
||||||
|
71DA993D9EDD2A4A54234FF2 /* Pods-Runner.release.xcconfig */,
|
||||||
|
B9F3F9EB590C25EBDAE05E03 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
C71F9C04DE64EB26EF8E0E58 /* Pods-RunnerTests.debug.xcconfig */,
|
||||||
|
BF6145095420650A6CCA9EE3 /* Pods-RunnerTests.release.xcconfig */,
|
||||||
|
9496FF8D8B46D4619EDAA5EF /* Pods-RunnerTests.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Pods;
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
331C80D6294CF71000263BE5 /* RunnerTests */ = {
|
331C80D6294CF71000263BE5 /* RunnerTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -125,6 +151,7 @@
|
|||||||
331C80D6294CF71000263BE5 /* RunnerTests */,
|
331C80D6294CF71000263BE5 /* RunnerTests */,
|
||||||
33CC10EE2044A3C60003C045 /* Products */,
|
33CC10EE2044A3C60003C045 /* Products */,
|
||||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||||
|
25379216233BB0105FDA3DD4 /* Pods */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -175,6 +202,8 @@
|
|||||||
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
71450153D8FB1F125629A01B /* Pods_Runner.framework */,
|
||||||
|
260FDBA879E00B3BD57336B6 /* Pods_RunnerTests.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -186,6 +215,7 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
DA445DDFEB72AB2901C75BCA /* [CP] Check Pods Manifest.lock */,
|
||||||
331C80D1294CF70F00263BE5 /* Sources */,
|
331C80D1294CF70F00263BE5 /* Sources */,
|
||||||
331C80D2294CF70F00263BE5 /* Frameworks */,
|
331C80D2294CF70F00263BE5 /* Frameworks */,
|
||||||
331C80D3294CF70F00263BE5 /* Resources */,
|
331C80D3294CF70F00263BE5 /* Resources */,
|
||||||
@@ -204,11 +234,14 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
27E4C9B69EF1878B4DAFB474 /* [CP] Check Pods Manifest.lock */,
|
||||||
33CC10E92044A3C60003C045 /* Sources */,
|
33CC10E92044A3C60003C045 /* Sources */,
|
||||||
33CC10EA2044A3C60003C045 /* Frameworks */,
|
33CC10EA2044A3C60003C045 /* Frameworks */,
|
||||||
33CC10EB2044A3C60003C045 /* Resources */,
|
33CC10EB2044A3C60003C045 /* Resources */,
|
||||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||||
|
A4B4F505F9548B10CBCD5F0A /* [CP] Embed Pods Frameworks */,
|
||||||
|
B2787524EFDFEFCFFDA32C4E /* [CP] Copy Pods Resources */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -227,7 +260,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0920;
|
LastSwiftUpdateCheck = 0920;
|
||||||
LastUpgradeCheck = 1430;
|
LastUpgradeCheck = 1510;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
331C80D4294CF70F00263BE5 = {
|
331C80D4294CF70F00263BE5 = {
|
||||||
@@ -290,6 +323,28 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
27E4C9B69EF1878B4DAFB474 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
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;
|
||||||
|
};
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
alwaysOutOfDate = 1;
|
||||||
@@ -328,6 +383,62 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
||||||
};
|
};
|
||||||
|
A4B4F505F9548B10CBCD5F0A /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
B2787524EFDFEFCFFDA32C4E /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
DA445DDFEB72AB2901C75BCA /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
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;
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@@ -379,6 +490,7 @@
|
|||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
331C80DB294CF71000263BE5 /* Debug */ = {
|
331C80DB294CF71000263BE5 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = C71F9C04DE64EB26EF8E0E58 /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@@ -393,6 +505,7 @@
|
|||||||
};
|
};
|
||||||
331C80DC294CF71000263BE5 /* Release */ = {
|
331C80DC294CF71000263BE5 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = BF6145095420650A6CCA9EE3 /* Pods-RunnerTests.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@@ -407,6 +520,7 @@
|
|||||||
};
|
};
|
||||||
331C80DD294CF71000263BE5 /* Profile */ = {
|
331C80DD294CF71000263BE5 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9496FF8D8B46D4619EDAA5EF /* Pods-RunnerTests.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@@ -457,7 +571,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
@@ -536,7 +650,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
@@ -583,7 +697,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1430"
|
LastUpgradeVersion = "1510"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -59,6 +59,7 @@
|
|||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
|
enableGPUValidationMode = "1"
|
||||||
allowLocationSimulation = "YES">
|
allowLocationSimulation = "YES">
|
||||||
<BuildableProductRunnable
|
<BuildableProductRunnable
|
||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
|
|||||||
@@ -4,4 +4,7 @@
|
|||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
|
|
||||||
@NSApplicationMain
|
@main
|
||||||
class AppDelegate: FlutterAppDelegate {
|
class AppDelegate: FlutterAppDelegate {
|
||||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+130
-151
@@ -5,23 +5,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "76.0.0"
|
version: "67.0.0"
|
||||||
_flutterfire_internals:
|
_flutterfire_internals:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _flutterfire_internals
|
name: _flutterfire_internals
|
||||||
sha256: de9ecbb3ddafd446095f7e833c853aff2fa1682b017921fe63a833f9d6f0e422
|
sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.54"
|
version: "1.3.59"
|
||||||
_macros:
|
|
||||||
dependency: transitive
|
|
||||||
description: dart
|
|
||||||
source: sdk
|
|
||||||
version: "0.3.3"
|
|
||||||
action_slider:
|
action_slider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -34,18 +29,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.11.0"
|
version: "6.4.1"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "7dcbd0f87fe5f61cb28da39a1a8b70dbc106e2fe0516f7836eb7bb2948481a12"
|
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.5"
|
version: "4.0.7"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -66,18 +61,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: asn1lib
|
name: asn1lib
|
||||||
sha256: e02d018628c870ef2d7f03e33f9ad179d89ff6ec52ca6c56bcb80bcef979867f
|
sha256: "9a8f69025044eb466b9b60ef3bc3ac99b4dc6c158ae9c56d25eeccf5bc56d024"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.2"
|
version: "1.6.5"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
|
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.12.0"
|
version: "2.13.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -90,10 +85,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build
|
name: build
|
||||||
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
|
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.2"
|
version: "2.4.1"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -106,34 +101,34 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
|
sha256: "409002f1adeea601018715d613115cfaf0e31f512cb80ae4534c79867ae2363d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.4"
|
version: "4.1.0"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.4"
|
version: "2.4.2"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.15"
|
version: "2.4.13"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.0.0"
|
version: "7.3.2"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -146,10 +141,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4
|
sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.9.5"
|
version: "8.12.0"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -162,10 +157,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: checked_yaml
|
name: checked_yaml
|
||||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "2.0.4"
|
||||||
cli_util:
|
cli_util:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -186,10 +181,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: code_builder
|
name: code_builder
|
||||||
sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
|
sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.10.1"
|
version: "4.11.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -210,18 +205,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
sha256: "942a4791cd385a68ccb3b32c71c427aba508a1bb949b86dff2adbe4049f16239"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.4+2"
|
version: "0.3.5"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "3.0.7"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -234,10 +229,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.8"
|
version: "2.3.6"
|
||||||
dbus:
|
dbus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -250,18 +245,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: "306b78788d1bb569edb7c55d622953c2414ca12445b41c9117963e03afc5c513"
|
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "11.3.3"
|
version: "11.5.0"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: device_info_plus_platform_interface
|
name: device_info_plus_platform_interface
|
||||||
sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2"
|
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.2"
|
version: "7.0.3"
|
||||||
encrypt:
|
encrypt:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -282,10 +277,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
|
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.3"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -306,50 +301,50 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_core
|
name: firebase_core
|
||||||
sha256: "017d17d9915670e6117497e640b2859e0b868026ea36bf3a57feb28c3b97debe"
|
sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.13.0"
|
version: "3.15.2"
|
||||||
firebase_core_platform_interface:
|
firebase_core_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_platform_interface
|
name: firebase_core_platform_interface
|
||||||
sha256: d7253d255ff10f85cfd2adaba9ac17bae878fa3ba577462451163bd9f1d1f0bf
|
sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.0"
|
version: "6.0.2"
|
||||||
firebase_core_web:
|
firebase_core_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_web
|
name: firebase_core_web
|
||||||
sha256: "129a34d1e0fb62e2b488d988a1fc26cc15636357e50944ffee2862efe8929b23"
|
sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.22.0"
|
version: "2.24.1"
|
||||||
firebase_messaging:
|
firebase_messaging:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging
|
name: firebase_messaging
|
||||||
sha256: "5f8918848ee0c8eb172fc7698619b2bcd7dda9ade8b93522c6297dd8f9178356"
|
sha256: "60be38574f8b5658e2f22b7e311ff2064bea835c248424a383783464e8e02fcc"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.2.5"
|
version: "15.2.10"
|
||||||
firebase_messaging_platform_interface:
|
firebase_messaging_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging_platform_interface
|
name: firebase_messaging_platform_interface
|
||||||
sha256: "0bbea00680249595fc896e7313a2bd90bd55be6e0abbe8b9a39d81b6b306acb6"
|
sha256: "685e1771b3d1f9c8502771ccc9f91485b376ffe16d553533f335b9183ea99754"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.6.5"
|
version: "4.6.10"
|
||||||
firebase_messaging_web:
|
firebase_messaging_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging_web
|
name: firebase_messaging_web
|
||||||
sha256: ffb392ce2a7e8439cd0a9a80e3c702194e73c927e5c7b4f0adf6faa00b245b17
|
sha256: "0d1be17bc89ed3ff5001789c92df678b2e963a51b6fa2bdb467532cc9dbed390"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.10.5"
|
version: "3.10.10"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -362,10 +357,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fl_chart
|
name: fl_chart
|
||||||
sha256: "74959b99b92b9eebeed1a4049426fd67c4abc3c5a0f4d12e2877097d6a11ae08"
|
sha256: "5276944c6ffc975ae796569a826c38a62d2abcf264e26b88fa6f482e107f4237"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.69.2"
|
version: "0.70.2"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -375,18 +370,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_launcher_icons
|
name: flutter_launcher_icons
|
||||||
sha256: bfa04787c85d80ecb3f8777bde5fc10c3de809240c48fa061a2c2bf15ea5211c
|
sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.14.3"
|
version: "0.14.4"
|
||||||
flutter_lazy_indexed_stack:
|
flutter_lazy_indexed_stack:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_lazy_indexed_stack
|
name: flutter_lazy_indexed_stack
|
||||||
sha256: e5529b516890475465c8c34b23d611e9c46b23c745c08edf471d1b6f899f5c9f
|
sha256: "3e905c0f130538f686e4e07bb8d0bc0dee6890366c65da199fb356aab52e0bff"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.6"
|
version: "0.0.7"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -456,10 +451,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: git_stamp
|
name: git_stamp
|
||||||
sha256: eddda29d15136503af57b5d927393fab2e7fe9660d4dc9ae418eb69c3f542c30
|
sha256: "25b123a0fd214c5d87f2a32f990771da76d2f24d66f6767cc190e174e3670fdd"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.10.0"
|
version: "5.10.1"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -504,10 +499,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.5.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -528,10 +523,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: iconsax_flutter
|
name: iconsax_flutter
|
||||||
sha256: "95b65699da8ea98f87c5d232f06b0debaaf1ec1332b697e4d90969ec9a93037d"
|
sha256: d14b4cec8586025ac15276bdd40f6eea308cb85748135965bb6255f14beb2564
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.1"
|
||||||
image:
|
image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -552,10 +547,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: in_app_purchase_android
|
name: in_app_purchase_android
|
||||||
sha256: fd76e5612da6facadcfe8a3477da092908227260a9f6ec7db9a66dd989c69b02
|
sha256: "2d0e2d27b93bd7457526419d7feb07baf5608d733ecf6cdcd2e3b03ea7248915"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.0+2"
|
version: "0.4.0+6"
|
||||||
in_app_purchase_platform_interface:
|
in_app_purchase_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -568,10 +563,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: in_app_purchase_storekit
|
name: in_app_purchase_storekit
|
||||||
sha256: "9c2b438aa8ef95ac1c1f5ab1aaace8d6edd0ba3745b8b8df832f7baa2e7492f7"
|
sha256: bfdb8d1859b6d19a55aba1046e3a860c631b6e96d36275a358e1caf8b62cfbde
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.1"
|
version: "0.4.6+1"
|
||||||
infinite_scroll_pagination:
|
infinite_scroll_pagination:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -616,26 +611,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
|
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.8"
|
version: "11.0.2"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.9"
|
version: "3.0.10"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_testing
|
name: leak_tracker_testing
|
||||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.2"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -652,14 +647,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
macros:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: macros
|
|
||||||
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.1.3-main.0"
|
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -696,10 +683,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mobile_scanner
|
name: mobile_scanner
|
||||||
sha256: "9cb9e371ee9b5b548714f9ab5fd33b530d799745c83d5729ecd1e8ab2935dbd1"
|
sha256: "0b466a0a8a211b366c2e87f3345715faef9b6011c7147556ad22f37de6ba3173"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.7"
|
version: "6.0.11"
|
||||||
mutex:
|
mutex:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -728,18 +715,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191"
|
sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.3.0"
|
version: "8.3.1"
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_platform_interface
|
name: package_info_plus_platform_interface
|
||||||
sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c"
|
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.2.1"
|
||||||
path:
|
path:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -760,18 +747,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "0ca7359dad67fd7063cb2892ab0c0737b2daafd807cf1acecd62374c8fae6c12"
|
sha256: e122c5ea805bb6773bb12ce667611265980940145be920cd09a4b0ec0285cb16
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.16"
|
version: "2.2.20"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_foundation
|
name: path_provider_foundation
|
||||||
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
|
sha256: efaec349ddfc181528345c56f8eda9d6cccd71c177511b132c6a0ddaefaa2738
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.3"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -808,10 +795,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646"
|
sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.0"
|
version: "7.0.1"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -840,26 +827,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pool
|
name: pool
|
||||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.5.2"
|
||||||
posix:
|
posix:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: posix
|
name: posix
|
||||||
sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a
|
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.1"
|
version: "6.0.3"
|
||||||
provider:
|
provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: provider
|
name: provider
|
||||||
sha256: "489024f942069c2920c844ee18bb3d467c69e48955a4f32d1677f71be103e310"
|
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.4"
|
version: "6.1.5+1"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -928,18 +915,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_android
|
name: shared_preferences_android
|
||||||
sha256: c2c8c46297b5d6a80bed7741ec1f2759742c77d272f1a1698176ae828f8e1a18
|
sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.9"
|
version: "2.4.15"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_foundation
|
name: shared_preferences_foundation
|
||||||
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
|
sha256: "1c33a907142607c40a7542768ec9badfd16293bac51da3a4482623d15845f88b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.4"
|
version: "2.5.5"
|
||||||
shared_preferences_linux:
|
shared_preferences_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -984,10 +971,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "2.0.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -1025,14 +1012,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.1"
|
version: "1.10.1"
|
||||||
sprintf:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sprintf
|
|
||||||
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "7.0.0"
|
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1077,10 +1056,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.4"
|
version: "0.7.6"
|
||||||
timezone:
|
timezone:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1101,10 +1080,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: toastification
|
name: toastification
|
||||||
sha256: "9713989549d60754fd0522425d1251501919cfb7bab4ffbbb36ef40de5ea72b9"
|
sha256: "69db2bff425b484007409650d8bcd5ed1ce2e9666293ece74dcd917dacf23112"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1117,26 +1096,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603"
|
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.1"
|
version: "6.3.2"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: "1d0eae19bd7606ef60fe69ef3b312a437a16549476c42321d5dc1506c9ca3bf4"
|
sha256: "5c8b6c2d89a78f5a1cca70a73d9d5f86c701b36b42f9c9dac7bad592113c28e9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.15"
|
version: "6.3.24"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb"
|
sha256: "6b63f1441e4f653ae799166a72b50b1767321ecc263a57aadf825a7a2a5477d9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.3"
|
version: "6.3.5"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1149,10 +1128,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_macos
|
name: url_launcher_macos
|
||||||
sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2"
|
sha256: "8262208506252a3ed4ff5c0dc1e973d2c0e0ef337d0a074d35634da5d44397c9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.2"
|
version: "3.2.4"
|
||||||
url_launcher_platform_interface:
|
url_launcher_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1165,10 +1144,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9"
|
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.4.1"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1181,34 +1160,34 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.1"
|
version: "4.5.2"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.2.0"
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
|
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.3.1"
|
version: "15.0.2"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: watcher
|
name: watcher
|
||||||
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
|
sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.4"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1221,26 +1200,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket
|
name: web_socket
|
||||||
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.6"
|
version: "1.0.1"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
|
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f
|
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.12.0"
|
version: "5.15.0"
|
||||||
win32_registry:
|
win32_registry:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1269,10 +1248,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xml
|
name: xml
|
||||||
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.5.0"
|
version: "6.6.1"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1282,5 +1261,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.7.0 <4.0.0"
|
dart: ">=3.9.0 <4.0.0"
|
||||||
flutter: ">=3.27.0"
|
flutter: ">=3.35.0"
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ name: simplecloudnotifier
|
|||||||
description: "Receive push messages"
|
description: "Receive push messages"
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 2.0.1+492
|
version: 2.2.2+544
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.2.6 <4.0.0'
|
sdk: '>=3.9.0 <4.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
|
|||||||
@@ -17,11 +17,18 @@ simple_cloud_notifier-*.sql
|
|||||||
identifier.sqlite
|
identifier.sqlite
|
||||||
|
|
||||||
.idea/dataSources.xml
|
.idea/dataSources.xml
|
||||||
|
.idea/copilot*
|
||||||
|
.idea/go.imports.xml
|
||||||
|
|
||||||
.swaggobin
|
.swaggobin
|
||||||
|
|
||||||
scn_send.sh
|
scn_send.sh
|
||||||
|
|
||||||
|
swagger/swagger.json
|
||||||
|
swagger/swagger.yaml
|
||||||
|
|
||||||
|
**/*_gen.go
|
||||||
|
|
||||||
##############
|
##############
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ RUN apt-get update && \
|
|||||||
rm -rf /var/lib/apt/lists
|
rm -rf /var/lib/apt/lists
|
||||||
|
|
||||||
COPY --from=builder /buildsrc/_build/scn_backend /app/server
|
COPY --from=builder /buildsrc/_build/scn_backend /app/server
|
||||||
|
COPY --from=builder /buildsrc/DOCKER_GIT_INFO /DOCKER_GIT_INFO
|
||||||
|
|
||||||
RUN mkdir /data
|
RUN mkdir /data
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ swagger-setup:
|
|||||||
swagger: swagger-setup
|
swagger: swagger-setup
|
||||||
".swaggobin/swag_$(SWAGGO_VERSION)" init -generalInfo ./api/router.go --propertyStrategy camelcase --output ./swagger/ --outputTypes "json,yaml"
|
".swaggobin/swag_$(SWAGGO_VERSION)" init -generalInfo ./api/router.go --propertyStrategy camelcase --output ./swagger/ --outputTypes "json,yaml"
|
||||||
|
|
||||||
|
generate: dgi enums ids swagger
|
||||||
|
|
||||||
pygmentize: website/scn_send.html
|
pygmentize: website/scn_send.html
|
||||||
|
|
||||||
website/scn_send.html: ../scn_send.sh
|
website/scn_send.html: ../scn_send.sh
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
||||||
ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken"
|
ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken"
|
||||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/mathext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/mathext"
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListChannels swaggerdoc
|
// ListChannels swaggerdoc
|
||||||
@@ -487,7 +488,7 @@ func (h APIHandler) ListChannelMessages(pctx ginext.PreContext) ginext.HTTPRespo
|
|||||||
// @Failure 404 {object} ginresp.apiError "channel not found"
|
// @Failure 404 {object} ginresp.apiError "channel not found"
|
||||||
// @Failure 500 {object} ginresp.apiError "internal server error"
|
// @Failure 500 {object} ginresp.apiError "internal server error"
|
||||||
//
|
//
|
||||||
// @Router /api/v2/users/{uid}/channels/{cid} [PATCH]
|
// @Router /api/v2/users/{uid}/channels/{cid} [DELETE]
|
||||||
func (h APIHandler) DeleteChannel(pctx ginext.PreContext) ginext.HTTPResponse {
|
func (h APIHandler) DeleteChannel(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||||
type uri struct {
|
type uri struct {
|
||||||
UserID models.UserID `uri:"uid" binding:"entityid"`
|
UserID models.UserID `uri:"uid" binding:"entityid"`
|
||||||
|
|||||||
@@ -271,6 +271,65 @@ func (h APIHandler) GetMessage(pctx ginext.PreContext) ginext.HTTPResponse {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListMessageDeliveries swaggerdoc
|
||||||
|
//
|
||||||
|
// @Summary List deliveries for a message
|
||||||
|
// @Description The user must own the channel and request the resource with the ADMIN Key
|
||||||
|
// @ID api-messages-deliveries
|
||||||
|
// @Tags API-v2
|
||||||
|
//
|
||||||
|
// @Param mid path string true "MessageID"
|
||||||
|
//
|
||||||
|
// @Success 200 {object} handler.ListMessageDeliveries.response
|
||||||
|
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||||
|
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
|
||||||
|
// @Failure 404 {object} ginresp.apiError "message not found"
|
||||||
|
// @Failure 500 {object} ginresp.apiError "internal server error"
|
||||||
|
//
|
||||||
|
// @Router /api/v2/messages/{mid}/deliveries [GET]
|
||||||
|
func (h APIHandler) ListMessageDeliveries(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||||
|
type uri struct {
|
||||||
|
MessageID models.MessageID `uri:"mid" binding:"entityid"`
|
||||||
|
}
|
||||||
|
type response struct {
|
||||||
|
Deliveries []models.Delivery `json:"deliveries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var u uri
|
||||||
|
ctx, g, errResp := pctx.URI(&u).Start()
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
return h.app.DoRequest(ctx, g, models.TLockRead, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
|
||||||
|
|
||||||
|
if permResp := ctx.CheckPermissionAny(); permResp != nil {
|
||||||
|
return *permResp
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := h.database.GetMessage(ctx, u.MessageID, false)
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return ginresp.APIError(g, 404, apierr.MESSAGE_NOT_FOUND, "message not found", err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query message", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// User must own the channel and have admin key
|
||||||
|
if permResp := ctx.CheckPermissionUserAdmin(msg.ChannelOwnerUserID); permResp != nil {
|
||||||
|
return *permResp
|
||||||
|
}
|
||||||
|
|
||||||
|
deliveries, err := h.database.ListDeliveriesOfMessage(ctx, msg.MessageID)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query deliveries", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return finishSuccess(ginext.JSON(http.StatusOK, response{Deliveries: deliveries}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteMessage swaggerdoc
|
// DeleteMessage swaggerdoc
|
||||||
//
|
//
|
||||||
// @Summary Delete a single message
|
// @Summary Delete a single message
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
||||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetUserPreview swaggerdoc
|
// GetUserPreview swaggerdoc
|
||||||
@@ -52,7 +53,7 @@ func (h APIHandler) GetUserPreview(pctx ginext.PreContext) ginext.HTTPResponse {
|
|||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query user", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query user", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return finishSuccess(ginext.JSON(http.StatusOK, user.JSONPreview()))
|
return finishSuccess(ginext.JSON(http.StatusOK, user.Preview()))
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -92,13 +93,13 @@ func (h APIHandler) GetChannelPreview(pctx ginext.PreContext) ginext.HTTPRespons
|
|||||||
|
|
||||||
userid := *ctx.GetPermissionUserID()
|
userid := *ctx.GetPermissionUserID()
|
||||||
|
|
||||||
channel, err := h.database.GetChannelByID(ctx, u.ChannelID)
|
channel, err := h.database.GetChannelByIDOpt(ctx, u.ChannelID)
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
|
||||||
return ginresp.APIError(g, 404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
||||||
}
|
}
|
||||||
|
if channel == nil {
|
||||||
|
return ginresp.APIError(g, 404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
|
||||||
|
}
|
||||||
|
|
||||||
sub, err := h.database.GetSubscriptionBySubscriber(ctx, userid, channel.ChannelID)
|
sub, err := h.database.GetSubscriptionBySubscriber(ctx, userid, channel.ChannelID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -161,13 +162,13 @@ func (h APIHandler) GetUserKeyPreview(pctx ginext.PreContext) ginext.HTTPRespons
|
|||||||
|
|
||||||
// Query by token.token
|
// Query by token.token
|
||||||
|
|
||||||
keytoken, err := h.database.GetKeyTokenByToken(ctx, u.KeyID)
|
keytoken, err := h.database.GetKeyTokenByTokenOpt(ctx, u.KeyID)
|
||||||
if keytoken == nil {
|
|
||||||
return ginresp.APIError(g, 404, apierr.KEY_NOT_FOUND, "Key not found", err)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err)
|
||||||
}
|
}
|
||||||
|
if keytoken == nil {
|
||||||
|
return ginresp.APIError(g, 404, apierr.KEY_NOT_FOUND, "Key not found", err)
|
||||||
|
}
|
||||||
|
|
||||||
return finishSuccess(ginext.JSON(http.StatusOK, keytoken.Preview()))
|
return finishSuccess(ginext.JSON(http.StatusOK, keytoken.Preview()))
|
||||||
|
|
||||||
@@ -175,3 +176,65 @@ func (h APIHandler) GetUserKeyPreview(pctx ginext.PreContext) ginext.HTTPRespons
|
|||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetClientPreview swaggerdoc
|
||||||
|
//
|
||||||
|
// @Summary Get a client (similar to api-clients-get, but can be called from anyone and only returns a subset of fields)
|
||||||
|
// @ID api-clients-get-preview
|
||||||
|
// @Tags API-v2
|
||||||
|
//
|
||||||
|
// @Param cid path string true "ClientID"
|
||||||
|
//
|
||||||
|
// @Success 200 {object} handler.GetClientPreview.response
|
||||||
|
//
|
||||||
|
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||||
|
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
|
||||||
|
// @Failure 404 {object} ginresp.apiError "client not found"
|
||||||
|
// @Failure 500 {object} ginresp.apiError "internal server error"
|
||||||
|
//
|
||||||
|
// @Router /api/v2/preview/clients/{cid} [GET]
|
||||||
|
func (h APIHandler) GetClientPreview(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||||
|
type uri struct {
|
||||||
|
ClientID models.ClientID `uri:"cid" binding:"entityid"`
|
||||||
|
}
|
||||||
|
type response struct {
|
||||||
|
Client models.ClientPreview `json:"client"`
|
||||||
|
User models.UserPreview `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var u uri
|
||||||
|
ctx, g, errResp := pctx.URI(&u).Start()
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
return h.app.DoRequest(ctx, g, models.TLockRead, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
|
||||||
|
|
||||||
|
if permResp := ctx.CheckPermissionAny(); permResp != nil {
|
||||||
|
return *permResp
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := h.database.GetClientByIDOpt(ctx, u.ClientID)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err)
|
||||||
|
}
|
||||||
|
if client == nil {
|
||||||
|
return ginresp.APIError(g, 404, apierr.CLIENT_NOT_FOUND, "Client not found", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := h.database.GetUser(ctx, client.UserID)
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return ginresp.APIError(g, 404, apierr.USER_NOT_FOUND, "User not found", err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query user", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return finishSuccess(ginext.JSON(http.StatusOK, response{
|
||||||
|
Client: client.Preview(),
|
||||||
|
User: user.Preview(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
||||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListUserSenderNames swaggerdoc
|
// ListUserSenderNames swaggerdoc
|
||||||
@@ -17,13 +18,13 @@ import (
|
|||||||
//
|
//
|
||||||
// @Param uid path string true "UserID"
|
// @Param uid path string true "UserID"
|
||||||
//
|
//
|
||||||
// @Success 200 {object} handler.ListUserKeys.response
|
// @Success 200 {object} handler.ListUserSenderNames.response
|
||||||
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||||
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
|
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
|
||||||
// @Failure 404 {object} ginresp.apiError "message not found"
|
// @Failure 404 {object} ginresp.apiError "message not found"
|
||||||
// @Failure 500 {object} ginresp.apiError "internal server error"
|
// @Failure 500 {object} ginresp.apiError "internal server error"
|
||||||
//
|
//
|
||||||
// @Router /api/v2/users/{uid}/keys [GET]
|
// @Router /api/v2/users/{uid}/sender-names [GET]
|
||||||
func (h APIHandler) ListUserSenderNames(pctx ginext.PreContext) ginext.HTTPResponse {
|
func (h APIHandler) ListUserSenderNames(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||||
type uri struct {
|
type uri struct {
|
||||||
UserID models.UserID `uri:"uid" binding:"entityid"`
|
UserID models.UserID `uri:"uid" binding:"entityid"`
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
||||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListUserSubscriptions swaggerdoc
|
// ListUserSubscriptions swaggerdoc
|
||||||
@@ -39,8 +40,9 @@ import (
|
|||||||
// @ID api-user-subscriptions-list
|
// @ID api-user-subscriptions-list
|
||||||
// @Tags API-v2
|
// @Tags API-v2
|
||||||
//
|
//
|
||||||
// @Param uid path string true "UserID"
|
// @Param uid path string true "UserID"
|
||||||
// @Param selector query string true "Filter subscriptions (default: outgoing_all)" Enums(outgoing_all, outgoing_confirmed, outgoing_unconfirmed, incoming_all, incoming_confirmed, incoming_unconfirmed)
|
//
|
||||||
|
// @Param query_data query handler.ListUserSubscriptions.query false " "
|
||||||
//
|
//
|
||||||
// @Success 200 {object} handler.ListUserSubscriptions.response
|
// @Success 200 {object} handler.ListUserSubscriptions.response
|
||||||
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||||
@@ -360,7 +362,7 @@ func (h APIHandler) CreateSubscription(pctx ginext.PreContext) ginext.HTTPRespon
|
|||||||
|
|
||||||
} else if b.ChannelOwnerUserID == nil && b.ChannelInternalName == nil && b.ChannelID != nil {
|
} else if b.ChannelOwnerUserID == nil && b.ChannelInternalName == nil && b.ChannelID != nil {
|
||||||
|
|
||||||
outchannel, err := h.database.GetChannelByID(ctx, *b.ChannelID)
|
outchannel, err := h.database.GetChannelByIDOpt(ctx, *b.ChannelID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||||
hl "blackforestbytes.com/simplecloudnotifier/api/apihighlight"
|
hl "blackforestbytes.com/simplecloudnotifier/api/apihighlight"
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
||||||
@@ -8,13 +13,9 @@ import (
|
|||||||
primarydb "blackforestbytes.com/simplecloudnotifier/db/impl/primary"
|
primarydb "blackforestbytes.com/simplecloudnotifier/db/impl/primary"
|
||||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/dataext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/dataext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CompatHandler struct {
|
type CompatHandler struct {
|
||||||
@@ -90,7 +91,7 @@ func (h CompatHandler) SendMessage(pctx ginext.PreContext) ginext.HTTPResponse {
|
|||||||
return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
|
return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
okResp, errResp := h.app.SendMessage(g, ctx, langext.Ptr(models.UserID(*newid)), data.UserKey, nil, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, nil)
|
okResp, errResp := h.app.SendMessage(g, ctx, data.UserKey, nil, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
} else {
|
} else {
|
||||||
@@ -304,7 +305,7 @@ func (h CompatHandler) Info(pctx ginext.PreContext) ginext.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.CompatAPIError(0, "Failed to query token")
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
}
|
}
|
||||||
@@ -416,7 +417,7 @@ func (h CompatHandler) Ack(pctx ginext.PreContext) ginext.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.CompatAPIError(0, "Failed to query token")
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
}
|
}
|
||||||
@@ -522,7 +523,7 @@ func (h CompatHandler) Requery(pctx ginext.PreContext) ginext.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.CompatAPIError(0, "Failed to query token")
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
}
|
}
|
||||||
@@ -643,7 +644,7 @@ func (h CompatHandler) Update(pctx ginext.PreContext) ginext.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.CompatAPIError(0, "Failed to query token")
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
}
|
}
|
||||||
@@ -777,7 +778,7 @@ func (h CompatHandler) Expand(pctx ginext.PreContext) ginext.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.CompatAPIError(0, "Failed to query token")
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
}
|
}
|
||||||
@@ -900,7 +901,7 @@ func (h CompatHandler) Upgrade(pctx ginext.PreContext) ginext.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.CompatAPIError(0, "Failed to query token")
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
||||||
primarydb "blackforestbytes.com/simplecloudnotifier/db/impl/primary"
|
primarydb "blackforestbytes.com/simplecloudnotifier/db/impl/primary"
|
||||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"fmt"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ExternalHandler struct {
|
type ExternalHandler struct {
|
||||||
@@ -27,8 +28,10 @@ func NewExternalHandler(app *logic.Application) ExternalHandler {
|
|||||||
|
|
||||||
// UptimeKuma swaggerdoc
|
// UptimeKuma swaggerdoc
|
||||||
//
|
//
|
||||||
// @Summary Send a new message
|
// @Summary Send a new message (uses uptime-kuma notification schema)
|
||||||
// @Description All parameter can be set via query-parameter or the json body. Only UserID, UserKey and Title are required
|
// @Description Set necessary parameter via query (key, channel etc.), title+message are build from uptime-kuma payload
|
||||||
|
// @Description You can specify different channels/priorities for [up] and [down] notifications
|
||||||
|
//
|
||||||
// @Tags External
|
// @Tags External
|
||||||
//
|
//
|
||||||
// @Param query_data query handler.UptimeKuma.query false " "
|
// @Param query_data query handler.UptimeKuma.query false " "
|
||||||
@@ -36,22 +39,21 @@ func NewExternalHandler(app *logic.Application) ExternalHandler {
|
|||||||
//
|
//
|
||||||
// @Success 200 {object} handler.UptimeKuma.response
|
// @Success 200 {object} handler.UptimeKuma.response
|
||||||
// @Failure 400 {object} ginresp.apiError
|
// @Failure 400 {object} ginresp.apiError
|
||||||
// @Failure 401 {object} ginresp.apiError "The user_id was not found or the user_key is wrong"
|
// @Failure 401 {object} ginresp.apiError "The user_key is wrong"
|
||||||
// @Failure 403 {object} ginresp.apiError "The user has exceeded its daily quota - wait 24 hours or upgrade your account"
|
// @Failure 403 {object} ginresp.apiError "The user has exceeded its daily quota - wait 24 hours or upgrade your account"
|
||||||
// @Failure 500 {object} ginresp.apiError "An internal server error occurred - try again later"
|
// @Failure 500 {object} ginresp.apiError "An internal server error occurred - try again later"
|
||||||
//
|
//
|
||||||
// @Router /external/v1/uptime-kuma [POST]
|
// @Router /external/v1/uptime-kuma [POST]
|
||||||
func (h ExternalHandler) UptimeKuma(pctx ginext.PreContext) ginext.HTTPResponse {
|
func (h ExternalHandler) UptimeKuma(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||||
type query struct {
|
type query struct {
|
||||||
UserID *models.UserID `form:"user_id" example:"7725"`
|
KeyToken *string `form:"key" example:"P3TNH8mvv14fm"`
|
||||||
KeyToken *string `form:"key" example:"P3TNH8mvv14fm"`
|
Channel *string `form:"channel"`
|
||||||
Channel *string `form:"channel"`
|
ChannelUp *string `form:"channel_up"`
|
||||||
ChannelUp *string `form:"channel_up"`
|
ChannelDown *string `form:"channel_down"`
|
||||||
ChannelDown *string `form:"channel_down"`
|
Priority *int `form:"priority"`
|
||||||
Priority *int `form:"priority"`
|
PriorityUp *int `form:"priority_up"`
|
||||||
PriorityUp *int `form:"priority_up"`
|
PriorityDown *int `form:"priority_down"`
|
||||||
PriorityDown *int `form:"priority_down"`
|
SenderName *string `form:"senderName"`
|
||||||
SenderName *string `form:"senderName"`
|
|
||||||
}
|
}
|
||||||
type body struct {
|
type body struct {
|
||||||
Heartbeat *struct {
|
Heartbeat *struct {
|
||||||
@@ -125,7 +127,62 @@ func (h ExternalHandler) UptimeKuma(pctx ginext.PreContext) ginext.HTTPResponse
|
|||||||
priority = q.PriorityDown
|
priority = q.PriorityDown
|
||||||
}
|
}
|
||||||
|
|
||||||
okResp, errResp := h.app.SendMessage(g, ctx, q.UserID, q.KeyToken, channel, &title, &content, priority, nil, timestamp, q.SenderName)
|
okResp, errResp := h.app.SendMessage(g, ctx, q.KeyToken, channel, &title, &content, priority, nil, timestamp, q.SenderName)
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
|
||||||
|
return finishSuccess(ginext.JSON(http.StatusOK, response{
|
||||||
|
MessageID: okResp.Message.MessageID,
|
||||||
|
}))
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shoutrrr swaggerdoc
|
||||||
|
//
|
||||||
|
// @Summary Send a new message (uses shoutrrr generic:// format=json schema)
|
||||||
|
// @Description Set necessary parameter via query (key, channel etc.), title+message are set via the shoutrrr payload
|
||||||
|
// @Description Use the shoutrrr format `generic://{{url}}?template=json`
|
||||||
|
//
|
||||||
|
// @Tags External
|
||||||
|
//
|
||||||
|
// @Param query_data query handler.Shoutrrr.query false " "
|
||||||
|
// @Param post_body body handler.Shoutrrr.body false " "
|
||||||
|
//
|
||||||
|
// @Success 200 {object} handler.Shoutrrr.response
|
||||||
|
// @Failure 400 {object} ginresp.apiError
|
||||||
|
// @Failure 401 {object} ginresp.apiError "The user_key is wrong"
|
||||||
|
// @Failure 403 {object} ginresp.apiError "The user has exceeded its daily quota - wait 24 hours or upgrade your account"
|
||||||
|
// @Failure 500 {object} ginresp.apiError "An internal server error occurred - try again later"
|
||||||
|
//
|
||||||
|
// @Router /external/v1/uptime-kuma [POST]
|
||||||
|
func (h ExternalHandler) Shoutrrr(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||||
|
type query struct {
|
||||||
|
KeyToken *string `form:"key" example:"P3TNH8mvv14fm"`
|
||||||
|
Channel *string `form:"channel"`
|
||||||
|
Priority *int `form:"priority"`
|
||||||
|
SenderName *string `form:"senderName"`
|
||||||
|
}
|
||||||
|
type body struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
type response struct {
|
||||||
|
MessageID models.MessageID `json:"message_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var b body
|
||||||
|
var q query
|
||||||
|
ctx, g, errResp := pctx.Query(&q).Body(&b).Start()
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
return h.app.DoRequest(ctx, g, models.TLockReadWrite, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
|
||||||
|
|
||||||
|
okResp, errResp := h.app.SendMessage(g, ctx, q.KeyToken, q.Channel, &b.Title, &b.Message, q.Priority, nil, nil, q.SenderName)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||||
primarydb "blackforestbytes.com/simplecloudnotifier/db/impl/primary"
|
primarydb "blackforestbytes.com/simplecloudnotifier/db/impl/primary"
|
||||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||||
@@ -8,7 +10,6 @@ import (
|
|||||||
"git.blackforestbytes.com/BlackForestBytes/goext/dataext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/dataext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SendMessageResponse struct {
|
type SendMessageResponse struct {
|
||||||
@@ -42,7 +43,7 @@ func NewMessageHandler(app *logic.Application) MessageHandler {
|
|||||||
//
|
//
|
||||||
// @Success 200 {object} handler.SendMessage.response
|
// @Success 200 {object} handler.SendMessage.response
|
||||||
// @Failure 400 {object} ginresp.apiError
|
// @Failure 400 {object} ginresp.apiError
|
||||||
// @Failure 401 {object} ginresp.apiError "The user_id was not found or the user_key is wrong"
|
// @Failure 401 {object} ginresp.apiError "The user_key is wrong"
|
||||||
// @Failure 403 {object} ginresp.apiError "The user has exceeded its daily quota - wait 24 hours or upgrade your account"
|
// @Failure 403 {object} ginresp.apiError "The user has exceeded its daily quota - wait 24 hours or upgrade your account"
|
||||||
// @Failure 500 {object} ginresp.apiError "An internal server error occurred - try again later"
|
// @Failure 500 {object} ginresp.apiError "An internal server error occurred - try again later"
|
||||||
//
|
//
|
||||||
@@ -50,15 +51,14 @@ func NewMessageHandler(app *logic.Application) MessageHandler {
|
|||||||
// @Router /send [POST]
|
// @Router /send [POST]
|
||||||
func (h MessageHandler) SendMessage(pctx ginext.PreContext) ginext.HTTPResponse {
|
func (h MessageHandler) SendMessage(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||||
type combined struct {
|
type combined struct {
|
||||||
UserID *models.UserID `json:"user_id" form:"user_id" example:"7725" `
|
KeyToken *string `json:"key" form:"key" example:"P3TNH8mvv14fm" `
|
||||||
KeyToken *string `json:"key" form:"key" example:"P3TNH8mvv14fm" `
|
Channel *string `json:"channel" form:"channel" example:"test" `
|
||||||
Channel *string `json:"channel" form:"channel" example:"test" `
|
Title *string `json:"title" form:"title" example:"Hello World" `
|
||||||
Title *string `json:"title" form:"title" example:"Hello World" `
|
Content *string `json:"content" form:"content" example:"This is a message" `
|
||||||
Content *string `json:"content" form:"content" example:"This is a message" `
|
Priority *int `json:"priority" form:"priority" example:"1" enums:"0,1,2" `
|
||||||
Priority *int `json:"priority" form:"priority" example:"1" enums:"0,1,2" `
|
UserMessageID *string `json:"msg_id" form:"msg_id" example:"db8b0e6a-a08c-4646" `
|
||||||
UserMessageID *string `json:"msg_id" form:"msg_id" example:"db8b0e6a-a08c-4646" `
|
SendTimestamp *float64 `json:"timestamp" form:"timestamp" example:"1669824037" `
|
||||||
SendTimestamp *float64 `json:"timestamp" form:"timestamp" example:"1669824037" `
|
SenderName *string `json:"sender_name" form:"sender_name" example:"example-server" `
|
||||||
SenderName *string `json:"sender_name" form:"sender_name" example:"example-server" `
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type response struct {
|
type response struct {
|
||||||
@@ -88,7 +88,7 @@ func (h MessageHandler) SendMessage(pctx ginext.PreContext) ginext.HTTPResponse
|
|||||||
// query has highest prio, then form, then json
|
// query has highest prio, then form, then json
|
||||||
data := dataext.ObjectMerge(dataext.ObjectMerge(b, f), q)
|
data := dataext.ObjectMerge(dataext.ObjectMerge(b, f), q)
|
||||||
|
|
||||||
okResp, errResp := h.app.SendMessage(g, ctx, data.UserID, data.KeyToken, data.Channel, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, data.SenderName)
|
okResp, errResp := h.app.SendMessage(g, ctx, data.KeyToken, data.Channel, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, data.SenderName)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
||||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"blackforestbytes.com/simplecloudnotifier/website"
|
"blackforestbytes.com/simplecloudnotifier/website"
|
||||||
"errors"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/rext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/rext"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"net/http"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebsiteHandler struct {
|
type WebsiteHandler struct {
|
||||||
@@ -77,6 +78,18 @@ func (h WebsiteHandler) MessageSent(pctx ginext.PreContext) ginext.HTTPResponse
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h WebsiteHandler) PrivacyPolicy(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||||
|
ctx, g, errResp := pctx.Start()
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
return h.app.DoRequest(ctx, g, models.TLockNone, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
|
||||||
|
return h.serveAsset(g, "privacy_policy.html", true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (h WebsiteHandler) FaviconIco(pctx ginext.PreContext) ginext.HTTPResponse {
|
func (h WebsiteHandler) FaviconIco(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||||
ctx, g, errResp := pctx.Start()
|
ctx, g, errResp := pctx.Start()
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/handler"
|
"blackforestbytes.com/simplecloudnotifier/api/handler"
|
||||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"blackforestbytes.com/simplecloudnotifier/swagger"
|
"blackforestbytes.com/simplecloudnotifier/swagger"
|
||||||
"errors"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
@@ -98,6 +99,10 @@ func (r *Router) Init(e *ginext.GinWrapper) error {
|
|||||||
frontend.GET("/message_sent.php").Handle(r.websiteHandler.MessageSent)
|
frontend.GET("/message_sent.php").Handle(r.websiteHandler.MessageSent)
|
||||||
frontend.GET("/message_sent.html").Handle(r.websiteHandler.MessageSent)
|
frontend.GET("/message_sent.html").Handle(r.websiteHandler.MessageSent)
|
||||||
|
|
||||||
|
frontend.GET("/privacy_policy").Handle(r.websiteHandler.PrivacyPolicy)
|
||||||
|
frontend.GET("/privacy_policy.php").Handle(r.websiteHandler.PrivacyPolicy)
|
||||||
|
frontend.GET("/privacy_policy.html").Handle(r.websiteHandler.PrivacyPolicy)
|
||||||
|
|
||||||
frontend.GET("/favicon.ico").Handle(r.websiteHandler.FaviconIco)
|
frontend.GET("/favicon.ico").Handle(r.websiteHandler.FaviconIco)
|
||||||
frontend.GET("/favicon.png").Handle(r.websiteHandler.FaviconPNG)
|
frontend.GET("/favicon.png").Handle(r.websiteHandler.FaviconPNG)
|
||||||
|
|
||||||
@@ -159,12 +164,14 @@ func (r *Router) Init(e *ginext.GinWrapper) error {
|
|||||||
apiv2.GET("/messages").Handle(r.apiHandler.ListMessages)
|
apiv2.GET("/messages").Handle(r.apiHandler.ListMessages)
|
||||||
apiv2.GET("/messages/:mid").Handle(r.apiHandler.GetMessage)
|
apiv2.GET("/messages/:mid").Handle(r.apiHandler.GetMessage)
|
||||||
apiv2.DELETE("/messages/:mid").Handle(r.apiHandler.DeleteMessage)
|
apiv2.DELETE("/messages/:mid").Handle(r.apiHandler.DeleteMessage)
|
||||||
|
apiv2.GET("/messages/:mid/deliveries").Handle(r.apiHandler.ListMessageDeliveries)
|
||||||
|
|
||||||
apiv2.GET("/sender-names").Handle(r.apiHandler.ListSenderNames)
|
apiv2.GET("/sender-names").Handle(r.apiHandler.ListSenderNames)
|
||||||
|
|
||||||
apiv2.GET("/preview/users/:uid").Handle(r.apiHandler.GetUserPreview)
|
apiv2.GET("/preview/users/:uid").Handle(r.apiHandler.GetUserPreview)
|
||||||
apiv2.GET("/preview/keys/:kid").Handle(r.apiHandler.GetUserKeyPreview)
|
apiv2.GET("/preview/keys/:kid").Handle(r.apiHandler.GetUserKeyPreview)
|
||||||
apiv2.GET("/preview/channels/:cid").Handle(r.apiHandler.GetChannelPreview)
|
apiv2.GET("/preview/channels/:cid").Handle(r.apiHandler.GetChannelPreview)
|
||||||
|
apiv2.GET("/preview/clients/:cid").Handle(r.apiHandler.GetClientPreview)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================ Send API (unversioned) ================
|
// ================ Send API (unversioned) ================
|
||||||
@@ -176,6 +183,7 @@ func (r *Router) Init(e *ginext.GinWrapper) error {
|
|||||||
sendAPI.POST("/send.php").Handle(r.compatHandler.SendMessage)
|
sendAPI.POST("/send.php").Handle(r.compatHandler.SendMessage)
|
||||||
|
|
||||||
sendAPI.POST("/external/v1/uptime-kuma").Handle(r.externalHandler.UptimeKuma)
|
sendAPI.POST("/external/v1/uptime-kuma").Handle(r.externalHandler.UptimeKuma)
|
||||||
|
sendAPI.POST("/external/v1/shoutrrr").Handle(r.externalHandler.Shoutrrr)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,21 +4,27 @@ import (
|
|||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mode string //@enum:type
|
type Mode string //@enum:type
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CTMStart = "START"
|
CTMStart = "START"
|
||||||
CTMNormal = "NORMAL"
|
CTMNormal = "NORMAL"
|
||||||
CTMEnd = "END"
|
CTMPaginated = "PAGINATED"
|
||||||
|
CTMEnd = "END"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CursorToken struct {
|
type CursorToken struct {
|
||||||
Mode Mode
|
Mode Mode
|
||||||
Timestamp int64
|
Timestamp *int64
|
||||||
|
Page *int
|
||||||
Id string
|
Id string
|
||||||
Direction string
|
Direction string
|
||||||
FilterHash string
|
FilterHash string
|
||||||
@@ -34,7 +40,8 @@ type cursorTokenSerialize struct {
|
|||||||
func Start() CursorToken {
|
func Start() CursorToken {
|
||||||
return CursorToken{
|
return CursorToken{
|
||||||
Mode: CTMStart,
|
Mode: CTMStart,
|
||||||
Timestamp: 0,
|
Timestamp: langext.Ptr[int64](0),
|
||||||
|
Page: nil,
|
||||||
Id: "",
|
Id: "",
|
||||||
Direction: "",
|
Direction: "",
|
||||||
FilterHash: "",
|
FilterHash: "",
|
||||||
@@ -44,7 +51,8 @@ func Start() CursorToken {
|
|||||||
func End() CursorToken {
|
func End() CursorToken {
|
||||||
return CursorToken{
|
return CursorToken{
|
||||||
Mode: CTMEnd,
|
Mode: CTMEnd,
|
||||||
Timestamp: 0,
|
Timestamp: nil,
|
||||||
|
Page: nil,
|
||||||
Id: "",
|
Id: "",
|
||||||
Direction: "",
|
Direction: "",
|
||||||
FilterHash: "",
|
FilterHash: "",
|
||||||
@@ -54,13 +62,22 @@ func End() CursorToken {
|
|||||||
func Normal(ts time.Time, id string, dir string, filter string) CursorToken {
|
func Normal(ts time.Time, id string, dir string, filter string) CursorToken {
|
||||||
return CursorToken{
|
return CursorToken{
|
||||||
Mode: CTMNormal,
|
Mode: CTMNormal,
|
||||||
Timestamp: ts.UnixMilli(),
|
Timestamp: langext.Ptr(ts.UnixMilli()),
|
||||||
|
Page: nil,
|
||||||
Id: id,
|
Id: id,
|
||||||
Direction: dir,
|
Direction: dir,
|
||||||
FilterHash: filter,
|
FilterHash: filter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Paginated(p int) CursorToken {
|
||||||
|
return CursorToken{
|
||||||
|
Mode: CTMPaginated,
|
||||||
|
Timestamp: nil,
|
||||||
|
Page: langext.Ptr(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CursorToken) Token() string {
|
func (c *CursorToken) Token() string {
|
||||||
if c.Mode == CTMStart {
|
if c.Mode == CTMStart {
|
||||||
return "@start"
|
return "@start"
|
||||||
@@ -69,6 +86,10 @@ func (c *CursorToken) Token() string {
|
|||||||
return "@end"
|
return "@end"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Page != nil {
|
||||||
|
return fmt.Sprintf("$%d", *c.Page)
|
||||||
|
}
|
||||||
|
|
||||||
// We kinda manually implement omitempty for the CursorToken here
|
// We kinda manually implement omitempty for the CursorToken here
|
||||||
// because omitempty does not work for time.Time and otherwise we would always
|
// because omitempty does not work for time.Time and otherwise we would always
|
||||||
// get weird time values when decoding a token that initially didn't have an Timestamp set
|
// get weird time values when decoding a token that initially didn't have an Timestamp set
|
||||||
@@ -80,8 +101,8 @@ func (c *CursorToken) Token() string {
|
|||||||
sertok.Id = &c.Id
|
sertok.Id = &c.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Timestamp != 0 {
|
if c.Timestamp != nil && *c.Timestamp != 0 {
|
||||||
sertok.Timestamp = &c.Timestamp
|
sertok.Timestamp = langext.Ptr(*c.Timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Direction != "" {
|
if c.Direction != "" {
|
||||||
@@ -111,6 +132,14 @@ func Decode(tok string) (CursorToken, error) {
|
|||||||
return End(), nil
|
return End(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(tok, "$") {
|
||||||
|
p, err := strconv.ParseInt(tok[1:], 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return CursorToken{}, errors.New("could not decode paginated token")
|
||||||
|
}
|
||||||
|
return Paginated(int(p)), nil
|
||||||
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(tok, "tok_") {
|
if !strings.HasPrefix(tok, "tok_") {
|
||||||
return CursorToken{}, errors.New("could not decode token, missing prefix")
|
return CursorToken{}, errors.New("could not decode token, missing prefix")
|
||||||
}
|
}
|
||||||
@@ -129,7 +158,7 @@ func Decode(tok string) (CursorToken, error) {
|
|||||||
token := CursorToken{Mode: CTMNormal}
|
token := CursorToken{Mode: CTMNormal}
|
||||||
|
|
||||||
if tokenDeserialize.Timestamp != nil {
|
if tokenDeserialize.Timestamp != nil {
|
||||||
token.Timestamp = *tokenDeserialize.Timestamp
|
token.Timestamp = langext.Ptr(*tokenDeserialize.Timestamp)
|
||||||
}
|
}
|
||||||
if tokenDeserialize.Id != nil {
|
if tokenDeserialize.Id != nil {
|
||||||
token.Id = *tokenDeserialize.Id
|
token.Id = *tokenDeserialize.Id
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package primary
|
package primary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/db"
|
"blackforestbytes.com/simplecloudnotifier/db"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/sq"
|
"git.blackforestbytes.com/BlackForestBytes/goext/sq"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *Database) GetChannelByName(ctx db.TxContext, userid models.UserID, chanName string) (*models.Channel, error) {
|
func (db *Database) GetChannelByName(ctx db.TxContext, userid models.UserID, chanName string) (*models.Channel, error) {
|
||||||
@@ -16,7 +17,7 @@ func (db *Database) GetChannelByName(ctx db.TxContext, userid models.UserID, cha
|
|||||||
return sq.QuerySingleOpt[models.Channel](ctx, tx, "SELECT * FROM channels WHERE owner_user_id = :uid AND internal_name = :nam AND deleted=0 LIMIT 1", sq.PP{"uid": userid, "nam": chanName}, sq.SModeExtended, sq.Safe)
|
return sq.QuerySingleOpt[models.Channel](ctx, tx, "SELECT * FROM channels WHERE owner_user_id = :uid AND internal_name = :nam AND deleted=0 LIMIT 1", sq.PP{"uid": userid, "nam": chanName}, sq.SModeExtended, sq.Safe)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) GetChannelByID(ctx db.TxContext, chanid models.ChannelID) (*models.Channel, error) {
|
func (db *Database) GetChannelByIDOpt(ctx db.TxContext, chanid models.ChannelID) (*models.Channel, error) {
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -53,6 +53,15 @@ func (db *Database) GetClient(ctx db.TxContext, userid models.UserID, clientid m
|
|||||||
}, sq.SModeExtended, sq.Safe)
|
}, sq.SModeExtended, sq.Safe)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetClientByIDOpt(ctx db.TxContext, clientid models.ClientID) (*models.Client, error) {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sq.QuerySingleOpt[models.Client](ctx, tx, "SELECT * FROM clients WHERE deleted=0 AND client_id = :cid LIMIT 1", sq.PP{"cid": clientid}, sq.SModeExtended, sq.Safe)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *Database) GetClientOpt(ctx db.TxContext, userid models.UserID, clientid models.ClientID) (*models.Client, error) {
|
func (db *Database) GetClientOpt(ctx db.TxContext, userid models.UserID, clientid models.ClientID) (*models.Client, error) {
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package primary
|
package primary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
scn "blackforestbytes.com/simplecloudnotifier"
|
scn "blackforestbytes.com/simplecloudnotifier"
|
||||||
"blackforestbytes.com/simplecloudnotifier/db"
|
"blackforestbytes.com/simplecloudnotifier/db"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/sq"
|
"git.blackforestbytes.com/BlackForestBytes/goext/sq"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *Database) CreateRetryDelivery(ctx db.TxContext, client models.Client, msg models.Message) (models.Delivery, error) {
|
func (db *Database) CreateRetryDelivery(ctx db.TxContext, client models.Client, msg models.Message) (models.Delivery, error) {
|
||||||
@@ -182,3 +183,12 @@ func (db *Database) DeleteDeliveriesOfChannel(ctx db.TxContext, channelid models
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) ListDeliveriesOfMessage(ctx db.TxContext, messageID models.MessageID) ([]models.Delivery, error) {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sq.QueryAll[models.Delivery](ctx, tx, "SELECT * FROM deliveries WHERE message_id = :mid AND deleted=0 ORDER BY timestamp_created ASC", sq.PP{"mid": messageID}, sq.SModeExtended, sq.Safe)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package primary
|
package primary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/db"
|
"blackforestbytes.com/simplecloudnotifier/db"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/sq"
|
"git.blackforestbytes.com/BlackForestBytes/goext/sq"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *Database) CreateKeyToken(ctx db.TxContext, name string, owner models.UserID, allChannels bool, channels []models.ChannelID, permissions models.TokenPermissionList, token string) (models.KeyToken, error) {
|
func (db *Database) CreateKeyToken(ctx db.TxContext, name string, owner models.UserID, allChannels bool, channels []models.ChannelID, permissions models.TokenPermissionList, token string) (models.KeyToken, error) {
|
||||||
@@ -67,7 +68,7 @@ func (db *Database) GetKeyTokenByID(ctx db.TxContext, keyTokenid models.KeyToken
|
|||||||
return sq.QuerySingle[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE keytoken_id = :cid AND deleted=0 LIMIT 1", sq.PP{"cid": keyTokenid}, sq.SModeExtended, sq.Safe)
|
return sq.QuerySingle[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE keytoken_id = :cid AND deleted=0 LIMIT 1", sq.PP{"cid": keyTokenid}, sq.SModeExtended, sq.Safe)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) GetKeyTokenByToken(ctx db.TxContext, key string) (*models.KeyToken, error) {
|
func (db *Database) GetKeyTokenByTokenOpt(ctx db.TxContext, key string) (*models.KeyToken, error) {
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package primary
|
package primary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/db"
|
"blackforestbytes.com/simplecloudnotifier/db"
|
||||||
ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken"
|
ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"errors"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/sq"
|
"git.blackforestbytes.com/BlackForestBytes/goext/sq"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *Database) GetMessageByUserMessageID(ctx db.TxContext, usrMsgId string) (*models.Message, error) {
|
func (db *Database) GetMessageByUserMessageID(ctx db.TxContext, usrMsgId string) (*models.Message, error) {
|
||||||
@@ -87,26 +88,32 @@ func (db *Database) ListMessages(ctx db.TxContext, filter models.MessageFilter,
|
|||||||
return nil, ct.CursorToken{}, 0, err
|
return nil, ct.CursorToken{}, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pageCond := "1=1"
|
|
||||||
if inTok.Mode == ct.CTMNormal {
|
|
||||||
pageCond = "timestamp_real < :tokts OR (timestamp_real = :tokts AND message_id < :tokid )"
|
|
||||||
}
|
|
||||||
|
|
||||||
filterCond, filterJoin, prepParams, err := filter.SQL()
|
filterCond, filterJoin, prepParams, err := filter.SQL()
|
||||||
|
|
||||||
orderClause := ""
|
pageCond := "1=1"
|
||||||
|
|
||||||
|
limitCond := ""
|
||||||
if pageSize != nil {
|
if pageSize != nil {
|
||||||
orderClause = "ORDER BY COALESCE(timestamp_client, timestamp_real) DESC, message_id DESC LIMIT :lim"
|
limitCond = "LIMIT :lim"
|
||||||
prepParams["lim"] = *pageSize + 1
|
prepParams["lim"] = *pageSize + 1
|
||||||
} else {
|
|
||||||
orderClause = "ORDER BY COALESCE(timestamp_client, timestamp_real) DESC, message_id DESC"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlQueryList := "SELECT " + "messages.*" + " FROM messages " + filterJoin + " WHERE ( " + pageCond + " ) AND ( " + filterCond + " ) " + orderClause
|
if inTok.Mode == ct.CTMNormal {
|
||||||
sqlQueryCount := "SELECT " + " COUNT(*) AS count FROM messages " + filterJoin + " WHERE ( " + filterCond + " ) "
|
pageCond = "timestamp_real < :tokts OR (timestamp_real = :tokts AND message_id < :tokid )"
|
||||||
|
prepParams["tokts"] = inTok.Timestamp
|
||||||
|
prepParams["tokid"] = inTok.Id
|
||||||
|
} else if inTok.Mode == ct.CTMPaginated {
|
||||||
|
if pageSize != nil {
|
||||||
|
limitCond = "LIMIT :lim OFFSET :off"
|
||||||
|
prepParams["lim"] = *pageSize + 1
|
||||||
|
prepParams["off"] = (*pageSize) * (*inTok.Page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
prepParams["tokts"] = inTok.Timestamp
|
orderClause := "ORDER BY COALESCE(timestamp_client, timestamp_real) DESC, message_id DESC"
|
||||||
prepParams["tokid"] = inTok.Id
|
|
||||||
|
sqlQueryList := "SELECT " + "messages.*" + " FROM messages " + filterJoin + " WHERE ( " + pageCond + " ) AND ( " + filterCond + " ) " + orderClause + " " + limitCond
|
||||||
|
sqlQueryCount := "SELECT " + " COUNT(*) AS count FROM messages " + filterJoin + " WHERE ( " + filterCond + " ) "
|
||||||
|
|
||||||
if inTok.Mode == ct.CTMEnd {
|
if inTok.Mode == ct.CTMEnd {
|
||||||
|
|
||||||
@@ -132,7 +139,12 @@ func (db *Database) ListMessages(ctx db.TxContext, filter models.MessageFilter,
|
|||||||
return nil, ct.CursorToken{}, 0, err
|
return nil, ct.CursorToken{}, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
outToken := ct.Normal(dataList[*pageSize-1].Timestamp(), dataList[*pageSize-1].MessageID.String(), "DESC", filter.Hash())
|
var outToken ct.CursorToken
|
||||||
|
if inTok.Mode == ct.CTMPaginated {
|
||||||
|
outToken = ct.Paginated(*inTok.Page + 1)
|
||||||
|
} else {
|
||||||
|
outToken = ct.Normal(dataList[*pageSize-1].Timestamp(), dataList[*pageSize-1].MessageID.String(), "DESC", filter.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
return dataList[0:*pageSize], outToken, dataCount.Count, nil
|
return dataList[0:*pageSize], outToken, dataCount.Count, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package primary
|
package primary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
scn "blackforestbytes.com/simplecloudnotifier"
|
scn "blackforestbytes.com/simplecloudnotifier"
|
||||||
"blackforestbytes.com/simplecloudnotifier/db"
|
"blackforestbytes.com/simplecloudnotifier/db"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/sq"
|
"git.blackforestbytes.com/BlackForestBytes/goext/sq"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *Database) CreateUser(ctx db.TxContext, protoken *string, username *string) (models.User, error) {
|
func (db *Database) CreateUser(ctx db.TxContext, protoken *string, username *string) (models.User, error) {
|
||||||
@@ -63,6 +64,15 @@ func (db *Database) GetUser(ctx db.TxContext, userid models.UserID) (models.User
|
|||||||
return sq.QuerySingle[models.User](ctx, tx, "SELECT * FROM users WHERE user_id = :uid AND deleted=0 LIMIT 1", sq.PP{"uid": userid}, sq.SModeExtended, sq.Safe)
|
return sq.QuerySingle[models.User](ctx, tx, "SELECT * FROM users WHERE user_id = :uid AND deleted=0 LIMIT 1", sq.PP{"uid": userid}, sq.SModeExtended, sq.Safe)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetUserByKey(ctx db.TxContext, key string) (models.User, error) {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return models.User{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sq.QuerySingle[models.User](ctx, tx, "SELECT * FROM users WHERE EXISTS(SELECT keytokens.keytoken_id FROM keytokens WHERE keytokens.token = :tok AND users.user_id = keytokens.owner_user_id AND keytokens.deleted=0) AND users.deleted=0 LIMIT 1", sq.PP{"tok": key}, sq.SModeExtended, sq.Safe)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *Database) GetUserOpt(ctx db.TxContext, userid models.UserID) (*models.User, error) {
|
func (db *Database) GetUserOpt(ctx db.TxContext, userid models.UserID) (*models.User, error) {
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -52,14 +52,29 @@ func (db *Database) ListRequestLogs(ctx context.Context, filter models.RequestLo
|
|||||||
return make([]models.RequestLog, 0), ct.End(), nil
|
return make([]models.RequestLog, 0), ct.End(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pageCond := "1=1"
|
|
||||||
if inTok.Mode == ct.CTMNormal {
|
|
||||||
pageCond = "timestamp_created < :tokts OR (timestamp_created = :tokts AND request_id < :tokid )"
|
|
||||||
}
|
|
||||||
|
|
||||||
filterCond, filterJoin, prepParams, err := filter.SQL()
|
filterCond, filterJoin, prepParams, err := filter.SQL()
|
||||||
|
|
||||||
orderClause := ""
|
pageCond := "1=1"
|
||||||
|
|
||||||
|
limitCond := ""
|
||||||
|
if pageSize != nil {
|
||||||
|
limitCond = "LIMIT :lim"
|
||||||
|
prepParams["lim"] = *pageSize + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if inTok.Mode == ct.CTMNormal {
|
||||||
|
pageCond = "timestamp_created < :tokts OR (timestamp_created = :tokts AND request_id < :tokid )"
|
||||||
|
prepParams["tokts"] = inTok.Timestamp
|
||||||
|
prepParams["tokid"] = inTok.Id
|
||||||
|
} else if inTok.Mode == ct.CTMPaginated {
|
||||||
|
if pageSize != nil {
|
||||||
|
limitCond = "LIMIT :lim OFFSET :off"
|
||||||
|
prepParams["lim"] = *pageSize + 1
|
||||||
|
prepParams["off"] = (*pageSize) * (*inTok.Page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
orderClause := "ORDER BY timestamp_created DESC, request_id DESC"
|
||||||
if pageSize != nil {
|
if pageSize != nil {
|
||||||
orderClause = "ORDER BY timestamp_created DESC, request_id DESC LIMIT :lim"
|
orderClause = "ORDER BY timestamp_created DESC, request_id DESC LIMIT :lim"
|
||||||
prepParams["lim"] = *pageSize + 1
|
prepParams["lim"] = *pageSize + 1
|
||||||
@@ -67,7 +82,7 @@ func (db *Database) ListRequestLogs(ctx context.Context, filter models.RequestLo
|
|||||||
orderClause = "ORDER BY timestamp_created DESC, request_id DESC"
|
orderClause = "ORDER BY timestamp_created DESC, request_id DESC"
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlQuery := "SELECT " + "requests.*" + " FROM requests " + filterJoin + " WHERE ( " + pageCond + " ) AND ( " + filterCond + " ) " + orderClause
|
sqlQuery := "SELECT " + "requests.*" + " FROM requests " + filterJoin + " WHERE ( " + pageCond + " ) AND ( " + filterCond + " ) " + orderClause + " " + limitCond
|
||||||
|
|
||||||
prepParams["tokts"] = inTok.Timestamp
|
prepParams["tokts"] = inTok.Timestamp
|
||||||
prepParams["tokid"] = inTok.Id
|
prepParams["tokid"] = inTok.Id
|
||||||
|
|||||||
+25
-22
@@ -1,14 +1,14 @@
|
|||||||
module blackforestbytes.com/simplecloudnotifier
|
module blackforestbytes.com/simplecloudnotifier
|
||||||
|
|
||||||
go 1.23.0
|
go 1.24.0
|
||||||
|
|
||||||
toolchain go1.24.2
|
toolchain go1.24.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.blackforestbytes.com/BlackForestBytes/goext v0.0.575
|
git.blackforestbytes.com/BlackForestBytes/goext v0.0.614
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.11.0
|
||||||
github.com/glebarez/go-sqlite v1.22.0
|
github.com/glebarez/go-sqlite v1.22.0
|
||||||
github.com/go-playground/validator/v10 v10.26.0
|
github.com/go-playground/validator/v10 v10.28.0
|
||||||
github.com/go-sql-driver/mysql v1.8.1
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
github.com/jmoiron/sqlx v1.4.0
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
github.com/rs/zerolog v1.34.0
|
github.com/rs/zerolog v1.34.0
|
||||||
@@ -18,20 +18,22 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/bytedance/sonic v1.13.2 // indirect
|
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
github.com/bytedance/sonic v1.14.2 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
|
||||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
|
github.com/goccy/go-yaml v1.19.0 // indirect
|
||||||
github.com/golang/snappy v1.0.0 // indirect
|
github.com/golang/snappy v1.0.0 // indirect
|
||||||
github.com/google/uuid v1.5.0 // indirect
|
github.com/google/uuid v1.5.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.1 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
@@ -39,24 +41,25 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
|
github.com/quic-go/quic-go v0.57.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rs/xid v1.6.0 // indirect
|
github.com/rs/xid v1.6.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.17.3 // indirect
|
go.mongodb.org/mongo-driver v1.17.6 // indirect
|
||||||
golang.org/x/arch v0.17.0 // indirect
|
golang.org/x/arch v0.23.0 // indirect
|
||||||
golang.org/x/crypto v0.38.0 // indirect
|
golang.org/x/crypto v0.45.0 // indirect
|
||||||
golang.org/x/net v0.40.0 // indirect
|
golang.org/x/net v0.47.0 // indirect
|
||||||
golang.org/x/sync v0.14.0 // indirect
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/term v0.32.0 // indirect
|
golang.org/x/term v0.37.0 // indirect
|
||||||
golang.org/x/text v0.25.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
modernc.org/libc v1.37.6 // indirect
|
modernc.org/libc v1.37.6 // indirect
|
||||||
modernc.org/mathutil v1.6.0 // indirect
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
modernc.org/memory v1.7.2 // indirect
|
modernc.org/memory v1.7.2 // indirect
|
||||||
|
|||||||
+58
-51
@@ -1,27 +1,27 @@
|
|||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
git.blackforestbytes.com/BlackForestBytes/goext v0.0.575 h1:scgvSaNZQ+C0pMbfPbsxF/hE3+rgLotHe+6Lbl5+mJU=
|
git.blackforestbytes.com/BlackForestBytes/goext v0.0.614 h1:LMjy2iHEPHRfLCXhguG9mQ+cgRypy0o2OAKztzZXmqk=
|
||||||
git.blackforestbytes.com/BlackForestBytes/goext v0.0.575/go.mod h1:Rj+bq1jLkgvXYe2sthg5UtXHf22nFvmTLeo+54fbYq8=
|
git.blackforestbytes.com/BlackForestBytes/goext v0.0.614/go.mod h1:LTkvxOvjXqkfxjxMOCD+qUlQ6Cu5uPRXQ0rZodFl7Rw=
|
||||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
|
||||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
|
||||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
|
||||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||||
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
@@ -30,17 +30,19 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
|||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
||||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
||||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
|
github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE=
|
||||||
|
github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||||
@@ -50,12 +52,10 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
|||||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
@@ -81,6 +81,10 @@ github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||||
|
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||||
|
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
|
||||||
|
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||||
@@ -90,48 +94,51 @@ github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
github.com/viney-shih/go-lock v1.1.2 h1:3TdGTiHZCPqBdTvFbQZQN/TRZzKF3KWw2rFEyKz3YqA=
|
github.com/viney-shih/go-lock v1.1.2 h1:3TdGTiHZCPqBdTvFbQZQN/TRZzKF3KWw2rFEyKz3YqA=
|
||||||
github.com/viney-shih/go-lock v1.1.2/go.mod h1:Yijm78Ljteb3kRiJrbLAxVntkUukGu5uzSxq/xV7OO8=
|
github.com/viney-shih/go-lock v1.1.2/go.mod h1:Yijm78Ljteb3kRiJrbLAxVntkUukGu5uzSxq/xV7OO8=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
|
go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
|
||||||
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||||
golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
|
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||||
golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||||
|
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
||||||
|
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -140,25 +147,26 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/loremipsum.v1 v1.1.2 h1:12APklfJKuGszqZsrArW5QoQh03/W+qyCCjvnDuS6Tw=
|
gopkg.in/loremipsum.v1 v1.1.2 h1:12APklfJKuGszqZsrArW5QoQh03/W+qyCCjvnDuS6Tw=
|
||||||
gopkg.in/loremipsum.v1 v1.1.2/go.mod h1:TuRvzFuzuejXj+odBU6Tubp/EPUyGb9wmSvHenyP2Ts=
|
gopkg.in/loremipsum.v1 v1.1.2/go.mod h1:TuRvzFuzuejXj+odBU6Tubp/EPUyGb9wmSvHenyP2Ts=
|
||||||
@@ -174,4 +182,3 @@ modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
|||||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||||
modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
|
modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
|
||||||
modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
|
modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package jobs
|
package jobs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/db/simplectx"
|
"blackforestbytes.com/simplecloudnotifier/db/simplectx"
|
||||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/syncext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/syncext"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeliveryRetryJob struct {
|
type DeliveryRetryJob struct {
|
||||||
@@ -208,7 +209,7 @@ func (j *DeliveryRetryJob) redeliver(ctx *simplectx.SimpleContext, delivery mode
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channel, err := j.app.Database.Primary.GetChannelByID(ctx, msg.ChannelID)
|
channel, err := j.app.Database.Primary.GetChannelByIDOpt(ctx, msg.ChannelID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Str("ChannelID", msg.ChannelID.String()).Msg("Failed to get channel")
|
log.Err(err).Str("ChannelID", msg.ChannelID.String()).Msg("Failed to get channel")
|
||||||
ctx.RollbackTransaction()
|
ctx.RollbackTransaction()
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ func (app *Application) getPermissions(ctx db.TxContext, hdr string) (models.Per
|
|||||||
|
|
||||||
key := strings.TrimSpace(hdr[4:])
|
key := strings.TrimSpace(hdr[4:])
|
||||||
|
|
||||||
tok, err := app.Database.Primary.GetKeyTokenByToken(ctx, key)
|
tok, err := app.Database.Primary.GetKeyTokenByTokenOpt(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.PermissionSet{}, err
|
return models.PermissionSet{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-14
@@ -1,21 +1,22 @@
|
|||||||
package logic
|
package logic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||||
hl "blackforestbytes.com/simplecloudnotifier/api/apihighlight"
|
hl "blackforestbytes.com/simplecloudnotifier/api/apihighlight"
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/ginext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/mathext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/mathext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/timeext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/timeext"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SendMessageResponse struct {
|
type SendMessageResponse struct {
|
||||||
@@ -25,7 +26,7 @@ type SendMessageResponse struct {
|
|||||||
CompatMessageID int64
|
CompatMessageID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *Application) SendMessage(g *gin.Context, ctx *AppContext, UserID *models.UserID, Key *string, Channel *string, Title *string, Content *string, Priority *int, UserMessageID *string, SendTimestamp *float64, SenderName *string) (*SendMessageResponse, *ginext.HTTPResponse) {
|
func (app *Application) SendMessage(g *gin.Context, ctx *AppContext, Key *string, Channel *string, Title *string, Content *string, Priority *int, UserMessageID *string, SendTimestamp *float64, SenderName *string) (*SendMessageResponse, *ginext.HTTPResponse) {
|
||||||
if Title != nil {
|
if Title != nil {
|
||||||
Title = langext.Ptr(strings.TrimSpace(*Title))
|
Title = langext.Ptr(strings.TrimSpace(*Title))
|
||||||
}
|
}
|
||||||
@@ -33,9 +34,6 @@ func (app *Application) SendMessage(g *gin.Context, ctx *AppContext, UserID *mod
|
|||||||
UserMessageID = langext.Ptr(strings.TrimSpace(*UserMessageID))
|
UserMessageID = langext.Ptr(strings.TrimSpace(*UserMessageID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if UserID == nil {
|
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.MISSING_UID, hl.USER_ID, "Missing parameter [[user_id]]", nil))
|
|
||||||
}
|
|
||||||
if Key == nil {
|
if Key == nil {
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.MISSING_TOK, hl.USER_KEY, "Missing parameter [[key]]", nil))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.MISSING_TOK, hl.USER_KEY, "Missing parameter [[key]]", nil))
|
||||||
}
|
}
|
||||||
@@ -49,9 +47,9 @@ func (app *Application) SendMessage(g *gin.Context, ctx *AppContext, UserID *mod
|
|||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.NO_TITLE, hl.TITLE, "No title specified", nil))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.NO_TITLE, hl.TITLE, "No title specified", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := app.Database.Primary.GetUser(ctx, *UserID)
|
user, err := app.Database.Primary.GetUserByKey(ctx, *Key)
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found", err))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 401, apierr.USER_AUTH_FAILED, hl.USER_KEY, "Key not found or not valid", err))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query user", err))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query user", err))
|
||||||
@@ -126,7 +124,7 @@ func (app *Application) SendMessage(g *gin.Context, ctx *AppContext, UserID *mod
|
|||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 403, apierr.QUOTA_REACHED, hl.NONE, fmt.Sprintf("Daily quota reached (%d)", user.QuotaPerDay()), nil))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 403, apierr.QUOTA_REACHED, hl.NONE, fmt.Sprintf("Daily quota reached (%d)", user.QuotaPerDay()), nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
channel, err := app.GetOrCreateChannel(ctx, *UserID, channelDisplayName, channelInternalName)
|
channel, err := app.GetOrCreateChannel(ctx, user.UserID, channelDisplayName, channelInternalName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query/create (owned) channel", err))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query/create (owned) channel", err))
|
||||||
}
|
}
|
||||||
@@ -145,7 +143,7 @@ func (app *Application) SendMessage(g *gin.Context, ctx *AppContext, UserID *mod
|
|||||||
|
|
||||||
clientIP := g.ClientIP()
|
clientIP := g.ClientIP()
|
||||||
|
|
||||||
msg, err := app.Database.Primary.CreateMessage(ctx, *UserID, channel, sendTimestamp, *Title, Content, priority, UserMessageID, clientIP, SenderName, keytok.KeyTokenID)
|
msg, err := app.Database.Primary.CreateMessage(ctx, user.UserID, channel, sendTimestamp, *Title, Content, priority, UserMessageID, clientIP, SenderName, keytok.KeyTokenID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to create message in db", err))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to create message in db", err))
|
||||||
}
|
}
|
||||||
@@ -176,7 +174,7 @@ func (app *Application) SendMessage(g *gin.Context, ctx *AppContext, UserID *mod
|
|||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to inc token msg-counter", err))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to inc token msg-counter", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info().Msg(fmt.Sprintf("Sending new notification %s for user %s (to %d active subscriptions)", msg.MessageID, UserID, len(activeSubscriptions)))
|
log.Info().Msg(fmt.Sprintf("Sending new notification %s for user %s (to %d active subscriptions)", msg.MessageID, user.UserID, len(activeSubscriptions)))
|
||||||
|
|
||||||
for _, sub := range activeSubscriptions {
|
for _, sub := range activeSubscriptions {
|
||||||
clients, err := app.Database.Primary.ListClients(ctx, sub.SubscriberUserID)
|
clients, err := app.Database.Primary.ListClients(ctx, sub.SubscriberUserID)
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func (ac *AppContext) CheckPermissionUserAdmin(userid models.UserID) *ginext.HTT
|
|||||||
|
|
||||||
func (ac *AppContext) CheckPermissionSend(channel models.Channel, key string) (*models.KeyToken, *ginext.HTTPResponse) {
|
func (ac *AppContext) CheckPermissionSend(channel models.Channel, key string) (*models.KeyToken, *ginext.HTTPResponse) {
|
||||||
|
|
||||||
keytok, err := ac.app.Database.Primary.GetKeyTokenByToken(ac, key)
|
keytok, err := ac.app.Database.Primary.GetKeyTokenByTokenOpt(ac, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, langext.Ptr(ginresp.APIError(ac.ginContext, 500, apierr.DATABASE_ERROR, "Failed to query token", err))
|
return nil, langext.Ptr(ginresp.APIError(ac.ginContext, 500, apierr.DATABASE_ERROR, "Failed to query token", err))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,3 +21,25 @@ type Client struct {
|
|||||||
Name *string `db:"name" json:"name"`
|
Name *string `db:"name" json:"name"`
|
||||||
Deleted bool `db:"deleted" json:"-"`
|
Deleted bool `db:"deleted" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ClientPreview struct {
|
||||||
|
ClientID ClientID `json:"client_id"`
|
||||||
|
UserID UserID `json:"user_id"`
|
||||||
|
Type ClientType `json:"type"`
|
||||||
|
TimestampCreated SCNTime `json:"timestamp_created"`
|
||||||
|
AgentModel string `json:"agent_model"`
|
||||||
|
AgentVersion string `json:"agent_version"`
|
||||||
|
Name *string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) Preview() ClientPreview {
|
||||||
|
return ClientPreview{
|
||||||
|
ClientID: c.ClientID,
|
||||||
|
UserID: c.UserID,
|
||||||
|
Type: c.Type,
|
||||||
|
TimestampCreated: c.TimestampCreated,
|
||||||
|
AgentModel: c.AgentModel,
|
||||||
|
AgentVersion: c.AgentVersion,
|
||||||
|
Name: c.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,375 +0,0 @@
|
|||||||
// Code generated by enum-generate.go DO NOT EDIT.
|
|
||||||
|
|
||||||
package models
|
|
||||||
|
|
||||||
import "git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
|
||||||
import "git.blackforestbytes.com/BlackForestBytes/goext/enums"
|
|
||||||
|
|
||||||
const ChecksumEnumGenerator = "1e8100b30bf6c946a1dfdc273b41efcaa91f33eab2bda12ce5dfa853741ac90b" // GoExtVersion: 0.0.575
|
|
||||||
|
|
||||||
// ================================ ClientType ================================
|
|
||||||
//
|
|
||||||
// File: client.go
|
|
||||||
// StringEnum: true
|
|
||||||
// DescrEnum: false
|
|
||||||
// DataEnum: false
|
|
||||||
//
|
|
||||||
|
|
||||||
var __ClientTypeValues = []ClientType{
|
|
||||||
ClientTypeAndroid,
|
|
||||||
ClientTypeIOS,
|
|
||||||
ClientTypeLinux,
|
|
||||||
ClientTypeMacOS,
|
|
||||||
ClientTypeWindows,
|
|
||||||
}
|
|
||||||
|
|
||||||
var __ClientTypeVarnames = map[ClientType]string{
|
|
||||||
ClientTypeAndroid: "ClientTypeAndroid",
|
|
||||||
ClientTypeIOS: "ClientTypeIOS",
|
|
||||||
ClientTypeLinux: "ClientTypeLinux",
|
|
||||||
ClientTypeMacOS: "ClientTypeMacOS",
|
|
||||||
ClientTypeWindows: "ClientTypeWindows",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ClientType) Valid() bool {
|
|
||||||
return langext.InArray(e, __ClientTypeValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ClientType) Values() []ClientType {
|
|
||||||
return __ClientTypeValues
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ClientType) ValuesAny() []any {
|
|
||||||
return langext.ArrCastToAny(__ClientTypeValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ClientType) ValuesMeta() []enums.EnumMetaValue {
|
|
||||||
return ClientTypeValuesMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ClientType) String() string {
|
|
||||||
return string(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ClientType) VarName() string {
|
|
||||||
if d, ok := __ClientTypeVarnames[e]; ok {
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ClientType) TypeName() string {
|
|
||||||
return "ClientType"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ClientType) PackageName() string {
|
|
||||||
return "models"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ClientType) Meta() enums.EnumMetaValue {
|
|
||||||
return enums.EnumMetaValue{VarName: e.VarName(), Value: e, Description: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseClientType(vv string) (ClientType, bool) {
|
|
||||||
for _, ev := range __ClientTypeValues {
|
|
||||||
if string(ev) == vv {
|
|
||||||
return ev, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ClientTypeValues() []ClientType {
|
|
||||||
return __ClientTypeValues
|
|
||||||
}
|
|
||||||
|
|
||||||
func ClientTypeValuesMeta() []enums.EnumMetaValue {
|
|
||||||
return []enums.EnumMetaValue{
|
|
||||||
ClientTypeAndroid.Meta(),
|
|
||||||
ClientTypeIOS.Meta(),
|
|
||||||
ClientTypeLinux.Meta(),
|
|
||||||
ClientTypeMacOS.Meta(),
|
|
||||||
ClientTypeWindows.Meta(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ DeliveryStatus ================================
|
|
||||||
//
|
|
||||||
// File: delivery.go
|
|
||||||
// StringEnum: true
|
|
||||||
// DescrEnum: false
|
|
||||||
// DataEnum: false
|
|
||||||
//
|
|
||||||
|
|
||||||
var __DeliveryStatusValues = []DeliveryStatus{
|
|
||||||
DeliveryStatusRetry,
|
|
||||||
DeliveryStatusSuccess,
|
|
||||||
DeliveryStatusFailed,
|
|
||||||
}
|
|
||||||
|
|
||||||
var __DeliveryStatusVarnames = map[DeliveryStatus]string{
|
|
||||||
DeliveryStatusRetry: "DeliveryStatusRetry",
|
|
||||||
DeliveryStatusSuccess: "DeliveryStatusSuccess",
|
|
||||||
DeliveryStatusFailed: "DeliveryStatusFailed",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DeliveryStatus) Valid() bool {
|
|
||||||
return langext.InArray(e, __DeliveryStatusValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DeliveryStatus) Values() []DeliveryStatus {
|
|
||||||
return __DeliveryStatusValues
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DeliveryStatus) ValuesAny() []any {
|
|
||||||
return langext.ArrCastToAny(__DeliveryStatusValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DeliveryStatus) ValuesMeta() []enums.EnumMetaValue {
|
|
||||||
return DeliveryStatusValuesMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DeliveryStatus) String() string {
|
|
||||||
return string(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DeliveryStatus) VarName() string {
|
|
||||||
if d, ok := __DeliveryStatusVarnames[e]; ok {
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DeliveryStatus) TypeName() string {
|
|
||||||
return "DeliveryStatus"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DeliveryStatus) PackageName() string {
|
|
||||||
return "models"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DeliveryStatus) Meta() enums.EnumMetaValue {
|
|
||||||
return enums.EnumMetaValue{VarName: e.VarName(), Value: e, Description: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseDeliveryStatus(vv string) (DeliveryStatus, bool) {
|
|
||||||
for _, ev := range __DeliveryStatusValues {
|
|
||||||
if string(ev) == vv {
|
|
||||||
return ev, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeliveryStatusValues() []DeliveryStatus {
|
|
||||||
return __DeliveryStatusValues
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeliveryStatusValuesMeta() []enums.EnumMetaValue {
|
|
||||||
return []enums.EnumMetaValue{
|
|
||||||
DeliveryStatusRetry.Meta(),
|
|
||||||
DeliveryStatusSuccess.Meta(),
|
|
||||||
DeliveryStatusFailed.Meta(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ TokenPerm ================================
|
|
||||||
//
|
|
||||||
// File: keytoken.go
|
|
||||||
// StringEnum: true
|
|
||||||
// DescrEnum: true
|
|
||||||
// DataEnum: false
|
|
||||||
//
|
|
||||||
|
|
||||||
var __TokenPermValues = []TokenPerm{
|
|
||||||
PermAdmin,
|
|
||||||
PermChannelRead,
|
|
||||||
PermChannelSend,
|
|
||||||
PermUserRead,
|
|
||||||
}
|
|
||||||
|
|
||||||
var __TokenPermDescriptions = map[TokenPerm]string{
|
|
||||||
PermAdmin: "Edit userdata (+ includes all other permissions)",
|
|
||||||
PermChannelRead: "Read messages",
|
|
||||||
PermChannelSend: "Send messages",
|
|
||||||
PermUserRead: "Read userdata",
|
|
||||||
}
|
|
||||||
|
|
||||||
var __TokenPermVarnames = map[TokenPerm]string{
|
|
||||||
PermAdmin: "PermAdmin",
|
|
||||||
PermChannelRead: "PermChannelRead",
|
|
||||||
PermChannelSend: "PermChannelSend",
|
|
||||||
PermUserRead: "PermUserRead",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) Valid() bool {
|
|
||||||
return langext.InArray(e, __TokenPermValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) Values() []TokenPerm {
|
|
||||||
return __TokenPermValues
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) ValuesAny() []any {
|
|
||||||
return langext.ArrCastToAny(__TokenPermValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) ValuesMeta() []enums.EnumMetaValue {
|
|
||||||
return TokenPermValuesMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) String() string {
|
|
||||||
return string(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) Description() string {
|
|
||||||
if d, ok := __TokenPermDescriptions[e]; ok {
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) VarName() string {
|
|
||||||
if d, ok := __TokenPermVarnames[e]; ok {
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) TypeName() string {
|
|
||||||
return "TokenPerm"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) PackageName() string {
|
|
||||||
return "models"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) Meta() enums.EnumMetaValue {
|
|
||||||
return enums.EnumMetaValue{VarName: e.VarName(), Value: e, Description: langext.Ptr(e.Description())}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TokenPerm) DescriptionMeta() enums.EnumDescriptionMetaValue {
|
|
||||||
return enums.EnumDescriptionMetaValue{VarName: e.VarName(), Value: e, Description: e.Description()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseTokenPerm(vv string) (TokenPerm, bool) {
|
|
||||||
for _, ev := range __TokenPermValues {
|
|
||||||
if string(ev) == vv {
|
|
||||||
return ev, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func TokenPermValues() []TokenPerm {
|
|
||||||
return __TokenPermValues
|
|
||||||
}
|
|
||||||
|
|
||||||
func TokenPermValuesMeta() []enums.EnumMetaValue {
|
|
||||||
return []enums.EnumMetaValue{
|
|
||||||
PermAdmin.Meta(),
|
|
||||||
PermChannelRead.Meta(),
|
|
||||||
PermChannelSend.Meta(),
|
|
||||||
PermUserRead.Meta(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TokenPermValuesDescriptionMeta() []enums.EnumDescriptionMetaValue {
|
|
||||||
return []enums.EnumDescriptionMetaValue{
|
|
||||||
PermAdmin.DescriptionMeta(),
|
|
||||||
PermChannelRead.DescriptionMeta(),
|
|
||||||
PermChannelSend.DescriptionMeta(),
|
|
||||||
PermUserRead.DescriptionMeta(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ TransactionLockMode ================================
|
|
||||||
//
|
|
||||||
// File: lock.go
|
|
||||||
// StringEnum: true
|
|
||||||
// DescrEnum: false
|
|
||||||
// DataEnum: false
|
|
||||||
//
|
|
||||||
|
|
||||||
var __TransactionLockModeValues = []TransactionLockMode{
|
|
||||||
TLockNone,
|
|
||||||
TLockRead,
|
|
||||||
TLockReadWrite,
|
|
||||||
}
|
|
||||||
|
|
||||||
var __TransactionLockModeVarnames = map[TransactionLockMode]string{
|
|
||||||
TLockNone: "TLockNone",
|
|
||||||
TLockRead: "TLockRead",
|
|
||||||
TLockReadWrite: "TLockReadWrite",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TransactionLockMode) Valid() bool {
|
|
||||||
return langext.InArray(e, __TransactionLockModeValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TransactionLockMode) Values() []TransactionLockMode {
|
|
||||||
return __TransactionLockModeValues
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TransactionLockMode) ValuesAny() []any {
|
|
||||||
return langext.ArrCastToAny(__TransactionLockModeValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TransactionLockMode) ValuesMeta() []enums.EnumMetaValue {
|
|
||||||
return TransactionLockModeValuesMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TransactionLockMode) String() string {
|
|
||||||
return string(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TransactionLockMode) VarName() string {
|
|
||||||
if d, ok := __TransactionLockModeVarnames[e]; ok {
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TransactionLockMode) TypeName() string {
|
|
||||||
return "TransactionLockMode"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TransactionLockMode) PackageName() string {
|
|
||||||
return "models"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e TransactionLockMode) Meta() enums.EnumMetaValue {
|
|
||||||
return enums.EnumMetaValue{VarName: e.VarName(), Value: e, Description: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseTransactionLockMode(vv string) (TransactionLockMode, bool) {
|
|
||||||
for _, ev := range __TransactionLockModeValues {
|
|
||||||
if string(ev) == vv {
|
|
||||||
return ev, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func TransactionLockModeValues() []TransactionLockMode {
|
|
||||||
return __TransactionLockModeValues
|
|
||||||
}
|
|
||||||
|
|
||||||
func TransactionLockModeValuesMeta() []enums.EnumMetaValue {
|
|
||||||
return []enums.EnumMetaValue{
|
|
||||||
TLockNone.Meta(),
|
|
||||||
TLockRead.Meta(),
|
|
||||||
TLockReadWrite.Meta(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ ================= ================================
|
|
||||||
|
|
||||||
func AllPackageEnums() []enums.Enum {
|
|
||||||
return []enums.Enum{
|
|
||||||
ClientTypeAndroid, // ClientType
|
|
||||||
DeliveryStatusRetry, // DeliveryStatus
|
|
||||||
PermAdmin, // TokenPerm
|
|
||||||
TLockNone, // TransactionLockMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,410 +0,0 @@
|
|||||||
// Code generated by csid-generate.go DO NOT EDIT.
|
|
||||||
|
|
||||||
package models
|
|
||||||
|
|
||||||
import "crypto/rand"
|
|
||||||
import "crypto/sha256"
|
|
||||||
import "fmt"
|
|
||||||
import "github.com/go-playground/validator/v10"
|
|
||||||
import "github.com/rs/zerolog/log"
|
|
||||||
import "git.blackforestbytes.com/BlackForestBytes/goext/exerr"
|
|
||||||
import "git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
|
||||||
import "git.blackforestbytes.com/BlackForestBytes/goext/rext"
|
|
||||||
import "math/big"
|
|
||||||
import "reflect"
|
|
||||||
import "regexp"
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
const ChecksumCharsetIDGenerator = "1e8100b30bf6c946a1dfdc273b41efcaa91f33eab2bda12ce5dfa853741ac90b" // GoExtVersion: 0.0.575
|
|
||||||
|
|
||||||
const idlen = 24
|
|
||||||
|
|
||||||
const checklen = 1
|
|
||||||
|
|
||||||
const idCharset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
||||||
const idCharsetLen = len(idCharset)
|
|
||||||
|
|
||||||
var charSetReverseMap = generateCharsetMap()
|
|
||||||
|
|
||||||
const (
|
|
||||||
prefixUserID = "USR"
|
|
||||||
prefixChannelID = "CHA"
|
|
||||||
prefixDeliveryID = "DEL"
|
|
||||||
prefixMessageID = "MSG"
|
|
||||||
prefixSubscriptionID = "SUB"
|
|
||||||
prefixClientID = "CLN"
|
|
||||||
prefixRequestID = "REQ"
|
|
||||||
prefixKeyTokenID = "TOK"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
regexUserID = generateRegex(prefixUserID)
|
|
||||||
regexChannelID = generateRegex(prefixChannelID)
|
|
||||||
regexDeliveryID = generateRegex(prefixDeliveryID)
|
|
||||||
regexMessageID = generateRegex(prefixMessageID)
|
|
||||||
regexSubscriptionID = generateRegex(prefixSubscriptionID)
|
|
||||||
regexClientID = generateRegex(prefixClientID)
|
|
||||||
regexRequestID = generateRegex(prefixRequestID)
|
|
||||||
regexKeyTokenID = generateRegex(prefixKeyTokenID)
|
|
||||||
)
|
|
||||||
|
|
||||||
func generateRegex(prefix string) rext.Regex {
|
|
||||||
return rext.W(regexp.MustCompile(fmt.Sprintf("^%s[%s]{%d}[%s]{%d}$", prefix, idCharset, idlen-len(prefix)-checklen, idCharset, checklen)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateCharsetMap() []int {
|
|
||||||
result := make([]int, 128)
|
|
||||||
for i := 0; i < len(result); i++ {
|
|
||||||
result[i] = -1
|
|
||||||
}
|
|
||||||
for idx, chr := range idCharset {
|
|
||||||
result[int(chr)] = idx
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateID(prefix string) string {
|
|
||||||
k := ""
|
|
||||||
csMax := big.NewInt(int64(idCharsetLen))
|
|
||||||
checksum := 0
|
|
||||||
for i := 0; i < idlen-len(prefix)-checklen; i++ {
|
|
||||||
v, err := rand.Int(rand.Reader, csMax)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
v64 := v.Int64()
|
|
||||||
k += string(idCharset[v64])
|
|
||||||
checksum = (checksum + int(v64)) % (idCharsetLen)
|
|
||||||
}
|
|
||||||
checkstr := string(idCharset[checksum%idCharsetLen])
|
|
||||||
return prefix + k + checkstr
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateIDFromSeed(prefix string, seed string) string {
|
|
||||||
h := sha256.New()
|
|
||||||
|
|
||||||
iddata := ""
|
|
||||||
for len(iddata) < idlen-len(prefix)-checklen {
|
|
||||||
h.Write([]byte(seed))
|
|
||||||
bs := h.Sum(nil)
|
|
||||||
iddata += langext.NewAnyBaseConverter(idCharset).Encode(bs)
|
|
||||||
}
|
|
||||||
|
|
||||||
checksum := 0
|
|
||||||
for i := 0; i < idlen-len(prefix)-checklen; i++ {
|
|
||||||
ichr := int(iddata[i])
|
|
||||||
checksum = (checksum + charSetReverseMap[ichr]) % (idCharsetLen)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkstr := string(idCharset[checksum%idCharsetLen])
|
|
||||||
|
|
||||||
return prefix + iddata[:(idlen-len(prefix)-checklen)] + checkstr
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateID(prefix string, value string) error {
|
|
||||||
if len(value) != idlen {
|
|
||||||
return exerr.New(exerr.TypeInvalidCSID, "id has the wrong length").Str("value", value).Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(value, prefix) {
|
|
||||||
return exerr.New(exerr.TypeInvalidCSID, "id is missing the correct prefix").Str("value", value).Str("prefix", prefix).Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
checksum := 0
|
|
||||||
for i := len(prefix); i < len(value)-checklen; i++ {
|
|
||||||
ichr := int(value[i])
|
|
||||||
if ichr < 0 || ichr >= len(charSetReverseMap) || charSetReverseMap[ichr] == -1 {
|
|
||||||
return exerr.New(exerr.TypeInvalidCSID, "id contains invalid characters").Str("value", value).Build()
|
|
||||||
}
|
|
||||||
checksum = (checksum + charSetReverseMap[ichr]) % (idCharsetLen)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkstr := string(idCharset[checksum%idCharsetLen])
|
|
||||||
|
|
||||||
if !strings.HasSuffix(value, checkstr) {
|
|
||||||
return exerr.New(exerr.TypeInvalidCSID, "id checkstring is invalid").Str("value", value).Str("checkstr", checkstr).Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRawData(prefix string, value string) string {
|
|
||||||
if len(value) != idlen {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return value[len(prefix) : idlen-checklen]
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCheckString(prefix string, value string) string {
|
|
||||||
if len(value) != idlen {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return value[idlen-checklen:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateEntityID(vfl validator.FieldLevel) bool {
|
|
||||||
if !vfl.Field().CanInterface() {
|
|
||||||
log.Error().Msgf("Failed to validate EntityID (cannot interface ?!?)")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
ifvalue := vfl.Field().Interface()
|
|
||||||
|
|
||||||
if value1, ok := ifvalue.(EntityID); ok {
|
|
||||||
|
|
||||||
if vfl.Field().Type().Kind() == reflect.Pointer && langext.IsNil(value1) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := value1.Valid(); err != nil {
|
|
||||||
log.Debug().Msgf("Failed to validate EntityID '%s' (%s)", value1.String(), err.Error())
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log.Error().Msgf("Failed to validate EntityID (wrong type: %T)", ifvalue)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ UserID (ids.go) ================================
|
|
||||||
|
|
||||||
func NewUserID() UserID {
|
|
||||||
return UserID(generateID(prefixUserID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id UserID) Valid() error {
|
|
||||||
return validateID(prefixUserID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i UserID) String() string {
|
|
||||||
return string(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i UserID) Prefix() string {
|
|
||||||
return prefixUserID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id UserID) Raw() string {
|
|
||||||
return getRawData(prefixUserID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id UserID) CheckString() string {
|
|
||||||
return getCheckString(prefixUserID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id UserID) Regex() rext.Regex {
|
|
||||||
return regexUserID
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ ChannelID (ids.go) ================================
|
|
||||||
|
|
||||||
func NewChannelID() ChannelID {
|
|
||||||
return ChannelID(generateID(prefixChannelID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id ChannelID) Valid() error {
|
|
||||||
return validateID(prefixChannelID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i ChannelID) String() string {
|
|
||||||
return string(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i ChannelID) Prefix() string {
|
|
||||||
return prefixChannelID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id ChannelID) Raw() string {
|
|
||||||
return getRawData(prefixChannelID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id ChannelID) CheckString() string {
|
|
||||||
return getCheckString(prefixChannelID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id ChannelID) Regex() rext.Regex {
|
|
||||||
return regexChannelID
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ DeliveryID (ids.go) ================================
|
|
||||||
|
|
||||||
func NewDeliveryID() DeliveryID {
|
|
||||||
return DeliveryID(generateID(prefixDeliveryID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id DeliveryID) Valid() error {
|
|
||||||
return validateID(prefixDeliveryID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i DeliveryID) String() string {
|
|
||||||
return string(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i DeliveryID) Prefix() string {
|
|
||||||
return prefixDeliveryID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id DeliveryID) Raw() string {
|
|
||||||
return getRawData(prefixDeliveryID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id DeliveryID) CheckString() string {
|
|
||||||
return getCheckString(prefixDeliveryID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id DeliveryID) Regex() rext.Regex {
|
|
||||||
return regexDeliveryID
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ MessageID (ids.go) ================================
|
|
||||||
|
|
||||||
func NewMessageID() MessageID {
|
|
||||||
return MessageID(generateID(prefixMessageID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id MessageID) Valid() error {
|
|
||||||
return validateID(prefixMessageID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i MessageID) String() string {
|
|
||||||
return string(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i MessageID) Prefix() string {
|
|
||||||
return prefixMessageID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id MessageID) Raw() string {
|
|
||||||
return getRawData(prefixMessageID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id MessageID) CheckString() string {
|
|
||||||
return getCheckString(prefixMessageID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id MessageID) Regex() rext.Regex {
|
|
||||||
return regexMessageID
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ SubscriptionID (ids.go) ================================
|
|
||||||
|
|
||||||
func NewSubscriptionID() SubscriptionID {
|
|
||||||
return SubscriptionID(generateID(prefixSubscriptionID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id SubscriptionID) Valid() error {
|
|
||||||
return validateID(prefixSubscriptionID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i SubscriptionID) String() string {
|
|
||||||
return string(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i SubscriptionID) Prefix() string {
|
|
||||||
return prefixSubscriptionID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id SubscriptionID) Raw() string {
|
|
||||||
return getRawData(prefixSubscriptionID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id SubscriptionID) CheckString() string {
|
|
||||||
return getCheckString(prefixSubscriptionID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id SubscriptionID) Regex() rext.Regex {
|
|
||||||
return regexSubscriptionID
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ ClientID (ids.go) ================================
|
|
||||||
|
|
||||||
func NewClientID() ClientID {
|
|
||||||
return ClientID(generateID(prefixClientID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id ClientID) Valid() error {
|
|
||||||
return validateID(prefixClientID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i ClientID) String() string {
|
|
||||||
return string(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i ClientID) Prefix() string {
|
|
||||||
return prefixClientID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id ClientID) Raw() string {
|
|
||||||
return getRawData(prefixClientID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id ClientID) CheckString() string {
|
|
||||||
return getCheckString(prefixClientID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id ClientID) Regex() rext.Regex {
|
|
||||||
return regexClientID
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ RequestID (ids.go) ================================
|
|
||||||
|
|
||||||
func NewRequestID() RequestID {
|
|
||||||
return RequestID(generateID(prefixRequestID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id RequestID) Valid() error {
|
|
||||||
return validateID(prefixRequestID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i RequestID) String() string {
|
|
||||||
return string(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i RequestID) Prefix() string {
|
|
||||||
return prefixRequestID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id RequestID) Raw() string {
|
|
||||||
return getRawData(prefixRequestID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id RequestID) CheckString() string {
|
|
||||||
return getCheckString(prefixRequestID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id RequestID) Regex() rext.Regex {
|
|
||||||
return regexRequestID
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================ KeyTokenID (ids.go) ================================
|
|
||||||
|
|
||||||
func NewKeyTokenID() KeyTokenID {
|
|
||||||
return KeyTokenID(generateID(prefixKeyTokenID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id KeyTokenID) Valid() error {
|
|
||||||
return validateID(prefixKeyTokenID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i KeyTokenID) String() string {
|
|
||||||
return string(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i KeyTokenID) Prefix() string {
|
|
||||||
return prefixKeyTokenID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id KeyTokenID) Raw() string {
|
|
||||||
return getRawData(prefixKeyTokenID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id KeyTokenID) CheckString() string {
|
|
||||||
return getCheckString(prefixKeyTokenID, string(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id KeyTokenID) Regex() rext.Regex {
|
|
||||||
return regexKeyTokenID
|
|
||||||
}
|
|
||||||
@@ -88,9 +88,9 @@ func (u User) MaxTitleLength() int {
|
|||||||
|
|
||||||
func (u User) QuotaPerDay() int {
|
func (u User) QuotaPerDay() int {
|
||||||
if u.IsPro {
|
if u.IsPro {
|
||||||
return 5000
|
return 15_000
|
||||||
} else {
|
} else {
|
||||||
return 50
|
return 500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ func (u User) MaxTimestampDiffHours() int {
|
|||||||
return 24
|
return 24
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) JSONPreview() UserPreview {
|
func (u User) Preview() UserPreview {
|
||||||
return UserPreview{
|
return UserPreview{
|
||||||
UserID: u.UserID,
|
UserID: u.UserID,
|
||||||
Username: u.Username,
|
Username: u.Username,
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
package push
|
package push
|
||||||
|
|
||||||
import (
|
import (
|
||||||
scn "blackforestbytes.com/simplecloudnotifier"
|
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
scn "blackforestbytes.com/simplecloudnotifier"
|
||||||
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://firebase.google.com/docs/cloud-messaging/send-message#rest
|
// https://firebase.google.com/docs/cloud-messaging/send-message#rest
|
||||||
@@ -66,7 +67,13 @@ func (fb FirebaseConnector) SendNotification(ctx context.Context, user models.Us
|
|||||||
"title": msg.Title,
|
"title": msg.Title,
|
||||||
"body": msg.ShortContent(),
|
"body": msg.ShortContent(),
|
||||||
},
|
},
|
||||||
"apns": gin.H{},
|
"apns": gin.H{
|
||||||
|
"payload": gin.H{
|
||||||
|
"aps": gin.H{
|
||||||
|
"sound": "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else if client.Type == models.ClientTypeAndroid {
|
} else if client.Type == models.ClientTypeAndroid {
|
||||||
jsonBody = gin.H{
|
jsonBody = gin.H{
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user