Compare commits
13 Commits
d6d60e60a9
...
2.1.1+36
Author | SHA1 | Date | |
---|---|---|---|
6427ec1f82 | |||
35dc7f4392 | |||
b50191970e | |||
1b69e6dd42 | |||
39fb4d474f | |||
392aebcad7 | |||
e9e3a4c474 | |||
7182336a0d | |||
be98fe133d | |||
e458943f56 | |||
eb125fc436 | |||
dc78f39969 | |||
f5c06bc89c |
@ -33,22 +33,6 @@
|
||||
</intent-filter>
|
||||
|
||||
<!-- Sharing Intents -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data
|
||||
android:scheme="https"
|
||||
android:host="sn.solsynth.dev"
|
||||
android:pathPrefix="/invite"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data
|
||||
android:mimeType="*/*"
|
||||
android:scheme="content" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -1,11 +1,11 @@
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.glance.GlanceId
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.GlanceTheme
|
||||
import androidx.glance.action.clickable
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.provideContent
|
||||
@ -25,8 +25,10 @@ import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextStyle
|
||||
import com.google.gson.FieldNamingPolicy
|
||||
import com.google.gson.GsonBuilder
|
||||
import dev.solsynth.solian.MainActivity
|
||||
import dev.solsynth.solian.data.InstantAdapter
|
||||
import dev.solsynth.solian.data.SolarCheckInRecord
|
||||
import es.antonborri.home_widget.actionStartActivity
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.OffsetDateTime
|
||||
@ -39,7 +41,9 @@ class CheckInWidget : GlanceAppWidget() {
|
||||
|
||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||
provideContent {
|
||||
GlanceContent(context, currentState())
|
||||
GlanceTheme {
|
||||
GlanceContent(context, currentState())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,18 +57,27 @@ class CheckInWidget : GlanceAppWidget() {
|
||||
val resultTierSymbols = listOf("大凶", "凶", "中平", "吉", "大吉")
|
||||
|
||||
val prefs = currentState.preferences
|
||||
val checkInRaw = prefs.getString("pas_check_in_record", null)
|
||||
val checkInRaw: String? = prefs.getString("pas_check_in_record", null)
|
||||
|
||||
val checkIn: SolarCheckInRecord? =
|
||||
checkInRaw?.let { checkInRaw ->
|
||||
gson.fromJson(checkInRaw, SolarCheckInRecord::class.java)
|
||||
} ?: null;
|
||||
|
||||
Column(
|
||||
modifier = GlanceModifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
.background(Color.White)
|
||||
.background(GlanceTheme.colors.widgetBackground)
|
||||
.padding(16.dp)
|
||||
.clickable(
|
||||
onClick = actionStartActivity<MainActivity>(
|
||||
context,
|
||||
Uri.parse("https://sn.solsynth.dev")
|
||||
)
|
||||
)
|
||||
) {
|
||||
if (checkInRaw != null) {
|
||||
val checkIn: SolarCheckInRecord =
|
||||
gson.fromJson(checkInRaw, SolarCheckInRecord::class.java)
|
||||
if (checkIn != null) {
|
||||
val dateFormatter = DateTimeFormatter.ofPattern("EEE, MM/dd")
|
||||
|
||||
val checkDate = checkIn.createdAt.atZone(ZoneId.of("UTC")).toLocalDate()
|
||||
@ -73,11 +86,18 @@ class CheckInWidget : GlanceAppWidget() {
|
||||
Column {
|
||||
Text(
|
||||
text = resultTierSymbols[checkIn.resultTier],
|
||||
style = TextStyle(fontSize = 25.sp, fontFamily = FontFamily.Serif)
|
||||
style = TextStyle(
|
||||
fontSize = 17.sp,
|
||||
color = GlanceTheme.colors.onSurface
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = "+${checkIn.resultExperience} EXP",
|
||||
style = TextStyle(fontSize = 15.sp, fontFamily = FontFamily.Monospace)
|
||||
style = TextStyle(
|
||||
fontSize = 13.sp,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
color = GlanceTheme.colors.onSurface
|
||||
)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = GlanceModifier.height(8.dp))
|
||||
@ -88,18 +108,21 @@ class CheckInWidget : GlanceAppWidget() {
|
||||
ZoneId.systemDefault()
|
||||
)
|
||||
.format(dateFormatter),
|
||||
style = TextStyle(fontSize = 13.sp)
|
||||
style = TextStyle(
|
||||
fontSize = 11.sp,
|
||||
color = GlanceTheme.colors.onSurface
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return@Column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "You haven't checked in today",
|
||||
style = TextStyle(fontSize = 15.sp)
|
||||
)
|
||||
Text(
|
||||
text = "You haven't checked in today",
|
||||
style = TextStyle(fontSize = 15.sp, color = GlanceTheme.colors.onSurface)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import HomeWidgetGlanceState
|
||||
import HomeWidgetGlanceStateDefinition
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@ -9,17 +9,13 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.glance.GlanceId
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.GlanceTheme
|
||||
import androidx.glance.Image
|
||||
import androidx.glance.ImageProvider
|
||||
import androidx.glance.action.clickable
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.cornerRadius
|
||||
import androidx.glance.appwidget.provideContent
|
||||
import androidx.glance.background
|
||||
import androidx.glance.currentState
|
||||
import androidx.glance.layout.Alignment
|
||||
import androidx.glance.layout.Column
|
||||
import androidx.glance.layout.ContentScale
|
||||
import androidx.glance.layout.Row
|
||||
import androidx.glance.layout.Spacer
|
||||
import androidx.glance.layout.fillMaxHeight
|
||||
@ -39,10 +35,6 @@ import dev.solsynth.solian.MainActivity
|
||||
import dev.solsynth.solian.data.InstantAdapter
|
||||
import dev.solsynth.solian.data.SolarPost
|
||||
import es.antonborri.home_widget.actionStartActivity
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okio.IOException
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
@ -52,45 +44,18 @@ class RandomPostWidget : GlanceAppWidget() {
|
||||
override val stateDefinition: GlanceStateDefinition<*>?
|
||||
get() = HomeWidgetGlanceStateDefinition()
|
||||
|
||||
private val defaultUrl = "https://api.sn.solsynth.dev"
|
||||
|
||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||
provideContent {
|
||||
GlanceTheme {
|
||||
GlanceContent(context, currentState(), null)
|
||||
GlanceContent(context, currentState())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val client = OkHttpClient()
|
||||
|
||||
private fun resizeBitmap(bitmap: Bitmap, maxWidth: Int, maxHeight: Int): Bitmap {
|
||||
val aspectRatio = bitmap.width.toFloat() / bitmap.height.toFloat()
|
||||
val newWidth = if (bitmap.width > maxWidth) maxWidth else bitmap.width
|
||||
val newHeight = (newWidth / aspectRatio).toInt()
|
||||
val resizedBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true)
|
||||
return resizedBitmap
|
||||
}
|
||||
|
||||
private fun loadImageFromUrl(url: String): Bitmap? {
|
||||
val request = Request.Builder().url(url).build()
|
||||
|
||||
return try {
|
||||
val response: Response = client.newCall(request).execute()
|
||||
val inputStream = response.body?.byteStream()
|
||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||
resizeBitmap(bitmap, 120, 120)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GlanceContent(
|
||||
context: Context,
|
||||
currentState: HomeWidgetGlanceState,
|
||||
avatar: Bitmap?
|
||||
) {
|
||||
val prefs = currentState.preferences
|
||||
val postRaw = prefs.getString("int_random_post", null)
|
||||
@ -109,7 +74,7 @@ class RandomPostWidget : GlanceAppWidget() {
|
||||
modifier = GlanceModifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
.background(Color.White)
|
||||
.background(GlanceTheme.colors.widgetBackground)
|
||||
.padding(16.dp)
|
||||
.clickable(
|
||||
onClick = actionStartActivity<MainActivity>(
|
||||
@ -120,25 +85,18 @@ class RandomPostWidget : GlanceAppWidget() {
|
||||
) {
|
||||
if (data != null) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (avatar != null) {
|
||||
Image(
|
||||
provider = ImageProvider(bitmap = avatar),
|
||||
contentDescription = null,
|
||||
modifier = GlanceModifier.width(36.dp).height(36.dp)
|
||||
.cornerRadius(18.dp),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Spacer(modifier = GlanceModifier.width(8.dp))
|
||||
}
|
||||
|
||||
Text(
|
||||
text = data.publisher.nick,
|
||||
style = TextStyle(fontSize = 15.sp)
|
||||
style = TextStyle(fontSize = 15.sp, color = GlanceTheme.colors.onSurface)
|
||||
)
|
||||
Spacer(modifier = GlanceModifier.width(8.dp))
|
||||
Text(
|
||||
text = "@${data.publisher.name}",
|
||||
style = TextStyle(fontSize = 13.sp, fontFamily = FontFamily.Monospace)
|
||||
style = TextStyle(
|
||||
fontSize = 13.sp,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
color = GlanceTheme.colors.onSurface
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -147,13 +105,13 @@ class RandomPostWidget : GlanceAppWidget() {
|
||||
if (data.body.title != null) {
|
||||
Text(
|
||||
text = data.body.title,
|
||||
style = TextStyle(fontSize = 25.sp)
|
||||
style = TextStyle(fontSize = 19.sp, color = GlanceTheme.colors.onSurface)
|
||||
)
|
||||
}
|
||||
if (data.body.description != null) {
|
||||
Text(
|
||||
text = data.body.description,
|
||||
style = TextStyle(fontSize = 19.sp)
|
||||
style = TextStyle(fontSize = 17.sp, color = GlanceTheme.colors.onSurface)
|
||||
)
|
||||
}
|
||||
|
||||
@ -163,7 +121,7 @@ class RandomPostWidget : GlanceAppWidget() {
|
||||
|
||||
Text(
|
||||
text = data.body.content ?: "No content",
|
||||
style = TextStyle(fontSize = 15.sp),
|
||||
style = TextStyle(fontSize = 15.sp, color = GlanceTheme.colors.onSurface),
|
||||
)
|
||||
|
||||
Spacer(modifier = GlanceModifier.height(8.dp))
|
||||
@ -172,12 +130,16 @@ class RandomPostWidget : GlanceAppWidget() {
|
||||
Text(
|
||||
LocalDateTime.ofInstant(data.createdAt, ZoneId.systemDefault())
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")),
|
||||
style = TextStyle(fontSize = 13.sp),
|
||||
style = TextStyle(fontSize = 13.sp, color = GlanceTheme.colors.onSurface),
|
||||
)
|
||||
|
||||
Text(
|
||||
"#${data.id}",
|
||||
style = TextStyle(fontSize = 11.sp, fontWeight = FontWeight.Bold),
|
||||
style = TextStyle(
|
||||
fontSize = 11.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = GlanceTheme.colors.onSurface
|
||||
),
|
||||
)
|
||||
|
||||
return@Column;
|
||||
@ -189,12 +151,16 @@ class RandomPostWidget : GlanceAppWidget() {
|
||||
horizontalAlignment = Alignment.Horizontal.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "Unable to fetch post",
|
||||
style = TextStyle(fontSize = 17.sp, fontWeight = FontWeight.Bold)
|
||||
text = "No Recommendations",
|
||||
style = TextStyle(
|
||||
fontSize = 17.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = GlanceTheme.colors.onSurface
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = "Check your internet connection",
|
||||
style = TextStyle(fontSize = 15.sp)
|
||||
text = "Open app to load some posts",
|
||||
style = TextStyle(fontSize = 15.sp, color = GlanceTheme.colors.onSurface)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialLayout="@layout/glance_default_loading_layout"
|
||||
android:minWidth="120dp"
|
||||
android:minWidth="40dp"
|
||||
android:minHeight="40dp"
|
||||
android:resizeMode="horizontal|vertical"
|
||||
android:updatePeriodMillis="10000">
|
||||
|
@ -370,6 +370,8 @@
|
||||
"dailyCheckNegativeHint6": "Going out",
|
||||
"dailyCheckNegativeHint6Description": "Forgot your umbrella and got caught in the rain",
|
||||
"happyBirthday": "Happy birthday, {}!",
|
||||
"celebrateMerryXmas": "Merry christmas, {}!",
|
||||
"celebrateNewYear": "Happy new year, {}!",
|
||||
"friendNew": "Add Friend",
|
||||
"friendRequests": "Friend Requests",
|
||||
"friendRequestsDescription": {
|
||||
@ -455,5 +457,7 @@
|
||||
"poweredBy": "Powered by {}",
|
||||
"shareIntent": "Share",
|
||||
"shareIntentDescription": "What do you want to do with the content you are sharing?",
|
||||
"shareIntentPostStory": "Post a Story"
|
||||
"shareIntentPostStory": "Post a Story",
|
||||
"updateAvailable": "Update Available",
|
||||
"updateOngoing": "正在更新,请稍后..."
|
||||
}
|
||||
|
@ -368,6 +368,8 @@
|
||||
"dailyCheckNegativeHint6": "出门",
|
||||
"dailyCheckNegativeHint6Description": "忘带伞遇上大雨",
|
||||
"happyBirthday": "生日快乐,{}!",
|
||||
"celebrateMerryXmas": "圣诞快乐,{}!",
|
||||
"celebrateNewYear": "新年快乐,{}!",
|
||||
"friendNew": "添加好友",
|
||||
"friendRequests": "好友请求",
|
||||
"friendRequestsDescription": {
|
||||
@ -453,5 +455,7 @@
|
||||
"poweredBy": "由 {} 提供支持",
|
||||
"shareIntent": "分享",
|
||||
"shareIntentDescription": "您想对您分享的内容做些什么?",
|
||||
"shareIntentPostStory": "发布动态"
|
||||
"shareIntentPostStory": "发布动态",
|
||||
"updateAvailable": "检测到更新可用",
|
||||
"updateOngoing": "正在更新,请稍后……"
|
||||
}
|
||||
|
13
ios/Podfile
13
ios/Podfile
@ -36,8 +36,21 @@ target 'Runner' do
|
||||
inherit! :search_paths
|
||||
end
|
||||
|
||||
target 'SolarNotifyService' do
|
||||
inherit! :search_paths
|
||||
pod 'home_widget', :path => '.symlinks/plugins/home_widget/ios'
|
||||
|
||||
pod 'Kingfisher', '~> 8.0'
|
||||
pod 'Alamofire'
|
||||
end
|
||||
|
||||
target 'SolarWidgetExtension' do
|
||||
inherit! :search_paths
|
||||
use_frameworks!
|
||||
use_modular_headers!
|
||||
|
||||
pod 'home_widget', :path => '.symlinks/plugins/home_widget/ios'
|
||||
|
||||
pod 'Kingfisher', '~> 8.0'
|
||||
end
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
PODS:
|
||||
- Alamofire (5.10.2)
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@ -102,6 +103,8 @@ PODS:
|
||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||
- nanopb (~> 3.30910.0)
|
||||
- Flutter (1.0.0)
|
||||
- flutter_app_update (0.0.1):
|
||||
- Flutter
|
||||
- flutter_native_splash (2.4.3):
|
||||
- Flutter
|
||||
- flutter_udid (0.0.1):
|
||||
@ -167,6 +170,8 @@ PODS:
|
||||
- Flutter
|
||||
- image_picker_ios (0.0.1):
|
||||
- Flutter
|
||||
- in_app_review (2.0.0):
|
||||
- Flutter
|
||||
- Kingfisher (8.1.3)
|
||||
- livekit_client (2.3.2):
|
||||
- Flutter
|
||||
@ -221,6 +226,7 @@ PODS:
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- Alamofire
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
|
||||
- croppy (from `.symlinks/plugins/croppy/ios`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
@ -230,12 +236,14 @@ DEPENDENCIES:
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
|
||||
- flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`)
|
||||
- gal (from `.symlinks/plugins/gal/darwin`)
|
||||
- home_widget (from `.symlinks/plugins/home_widget/ios`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
||||
- Kingfisher (~> 8.0)
|
||||
- livekit_client (from `.symlinks/plugins/livekit_client/ios`)
|
||||
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
||||
@ -257,6 +265,7 @@ DEPENDENCIES:
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- Alamofire
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- Firebase
|
||||
@ -295,6 +304,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/firebase_messaging/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_app_update:
|
||||
:path: ".symlinks/plugins/flutter_app_update/ios"
|
||||
flutter_native_splash:
|
||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||
flutter_udid:
|
||||
@ -307,6 +318,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/home_widget/ios"
|
||||
image_picker_ios:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
in_app_review:
|
||||
:path: ".symlinks/plugins/in_app_review/ios"
|
||||
livekit_client:
|
||||
:path: ".symlinks/plugins/livekit_client/ios"
|
||||
media_kit_libs_ios_video:
|
||||
@ -343,6 +356,7 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/workmanager/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
||||
connectivity_plus: 18382e7311ba19efcaee94442b23b32507b20695
|
||||
croppy: b6199bc8d56bd2e03cc11609d1c47ad9875c1321
|
||||
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
|
||||
@ -360,8 +374,9 @@ SPEC CHECKSUMS:
|
||||
FirebaseInstallations: 6ef4a1c7eb2a61ee1f74727d7f6ce2e72acf1414
|
||||
FirebaseMessaging: f8a160d99c2c2e5babbbcc90c4a3e15db036aee2
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_app_update: 65f61da626cb111d1b24674abc4b01728d7723bc
|
||||
flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a
|
||||
flutter_udid: a2482c67a61b9c806ef59dd82ed8d007f1b7ac04
|
||||
flutter_udid: b2417673f287ee62817a1de3d1643f47b9f508ab
|
||||
flutter_webrtc: 1a53bd24f97bcfeff512f13699e721897f261563
|
||||
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
||||
GoogleAppMeasurement: 987769c4ca6b968f2479fbcc9fe3ce34af454b8e
|
||||
@ -369,6 +384,7 @@ SPEC CHECKSUMS:
|
||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57
|
||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||
in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
|
||||
Kingfisher: f2af9028b16baf9dc6c07c570072bc41cbf009ef
|
||||
livekit_client: 6108dad8b77db3142bafd4c630f471d0a54335cd
|
||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
||||
@ -394,6 +410,6 @@ SPEC CHECKSUMS:
|
||||
WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db
|
||||
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
|
||||
|
||||
PODFILE CHECKSUM: f36978bb00ec01cd27f69faaf9a821024de98fcc
|
||||
PODFILE CHECKSUM: 9b244e02f87527430136c8d21cbdcf1cd586b6bc
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
@ -3,18 +3,18 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 77;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0B21A2B78F1AE403D3BE143E /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26CC8DE2338798EAB472B62D /* Pods_RunnerTests.framework */; };
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
2630F2992106E991467A6FC4 /* Pods_SolarWidgetExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F357CFDA89A0D9E5692846D4 /* Pods_SolarWidgetExtension.framework */; };
|
||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
738C1EAC2D0D76A400A215F3 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 731B7B6B2D0D6CE000CEB9B7 /* WidgetKit.framework */; };
|
||||
738C1EAD2D0D76A400A215F3 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 731B7B6D2D0D6CE000CEB9B7 /* SwiftUI.framework */; };
|
||||
738C1EB82D0D76A500A215F3 /* SolarWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 738C1EAB2D0D76A400A215F3 /* SolarWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
7396A3522D16BD890095F4A8 /* NotifyDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7396A3512D16BD890095F4A8 /* NotifyDelegate.swift */; };
|
||||
73B7746E2D0E869200A789CE /* SolarShare.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73B774642D0E869200A789CE /* SolarShare.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
73DA8A012D05C7620024A03E /* SolarNotifyService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73DA89FA2D05C7620024A03E /* SolarNotifyService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
@ -23,6 +23,8 @@
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
CED170BFB6A72CDDAC285637 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDF483E994343CDFBF9BA347 /* Pods_Runner.framework */; };
|
||||
D5125CF12F159F0B8BC7641D /* Pods_SolarNotifyService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02469D286F48D84300484B1E /* Pods_SolarNotifyService.framework */; };
|
||||
D962B51F682FBDEC00AC7281 /* Pods_SolarWidgetExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B1A159F5551E280D0EFC129 /* Pods_SolarWidgetExtension.framework */; };
|
||||
F51C4E3C8FA95426C91FC0A4 /* Pods_SolarShare.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16F41E029731EA30268EDE2A /* Pods_SolarShare.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@ -84,6 +86,8 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
02469D286F48D84300484B1E /* Pods_SolarNotifyService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SolarNotifyService.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1077EFD9ACF793E9DA5D5B63 /* Pods-Runner-SolarNotifyService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-SolarNotifyService.release.xcconfig"; path = "Target Support Files/Pods-Runner-SolarNotifyService/Pods-Runner-SolarNotifyService.release.xcconfig"; sourceTree = "<group>"; };
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
16F41E029731EA30268EDE2A /* Pods_SolarShare.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SolarShare.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -94,8 +98,10 @@
|
||||
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>"; };
|
||||
40B53769EB464E54DACA7CE4 /* 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>"; };
|
||||
430F31F96B82659CBEAD4326 /* Pods-Runner-SolarWidgetExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-SolarWidgetExtension.profile.xcconfig"; path = "Target Support Files/Pods-Runner-SolarWidgetExtension/Pods-Runner-SolarWidgetExtension.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
48AE73F9950AF4FB02B5E9F4 /* 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>"; };
|
||||
4A2F84B6033057E3BD2C7CB8 /* 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>"; };
|
||||
4CBF45ABD292EE527D0A4D1E /* Pods-SolarNotifyService.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolarNotifyService.profile.xcconfig"; path = "Target Support Files/Pods-SolarNotifyService/Pods-SolarNotifyService.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
5922A50B1231B06B92E31F20 /* Pods-SolarShare.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolarShare.debug.xcconfig"; path = "Target Support Files/Pods-SolarShare/Pods-SolarShare.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
64FBE78F9C282712818D6D95 /* 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>"; };
|
||||
6618E2E3015264643175B43D /* Pods-SolarWidgetExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolarWidgetExtension.release.xcconfig"; path = "Target Support Files/Pods-SolarWidgetExtension/Pods-SolarWidgetExtension.release.xcconfig"; sourceTree = "<group>"; };
|
||||
@ -105,11 +111,15 @@
|
||||
731B7B6D2D0D6CE000CEB9B7 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
|
||||
738C1EAB2D0D76A400A215F3 /* SolarWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolarWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
738C1F132D0D7DDC00A215F3 /* SolarWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SolarWidgetExtension.entitlements; sourceTree = "<group>"; };
|
||||
7396A3512D16BD890095F4A8 /* NotifyDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotifyDelegate.swift; sourceTree = "<group>"; };
|
||||
73B774642D0E869200A789CE /* SolarShare.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolarShare.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
73DA89FA2D05C7620024A03E /* SolarNotifyService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolarNotifyService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
7B1A159F5551E280D0EFC129 /* Pods_SolarWidgetExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SolarWidgetExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8E44A071621D5CAF864FB2F1 /* Pods-Runner-SolarNotifyService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-SolarNotifyService.debug.xcconfig"; path = "Target Support Files/Pods-Runner-SolarNotifyService/Pods-Runner-SolarNotifyService.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
931FBE9EDB99B3AD8B1FFB00 /* Pods-Runner-SolarWidgetExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-SolarWidgetExtension.release.xcconfig"; path = "Target Support Files/Pods-Runner-SolarWidgetExtension/Pods-Runner-SolarWidgetExtension.release.xcconfig"; sourceTree = "<group>"; };
|
||||
96081771773FA019A97CCC3F /* 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; 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>"; };
|
||||
@ -120,9 +130,12 @@
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
A2C24C5238FAC44EA2CCF738 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
B1763F1D7318A2745CA7EDFE /* Pods-SolarShare.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolarShare.release.xcconfig"; path = "Target Support Files/Pods-SolarShare/Pods-SolarShare.release.xcconfig"; sourceTree = "<group>"; };
|
||||
B4550C68292419CDC580808B /* Pods-Runner-SolarNotifyService.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-SolarNotifyService.profile.xcconfig"; path = "Target Support Files/Pods-Runner-SolarNotifyService/Pods-Runner-SolarNotifyService.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
BCE0C4086B776A27B202B373 /* Pods-SolarWidgetExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolarWidgetExtension.profile.xcconfig"; path = "Target Support Files/Pods-SolarWidgetExtension/Pods-SolarWidgetExtension.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
BFF3B436D74FA8CBFFE34A27 /* Pods-Runner-SolarWidgetExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner-SolarWidgetExtension.debug.xcconfig"; path = "Target Support Files/Pods-Runner-SolarWidgetExtension/Pods-Runner-SolarWidgetExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
D7E1FA77FDA53439DB2C0E75 /* Pods-SolarNotifyService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolarNotifyService.release.xcconfig"; path = "Target Support Files/Pods-SolarNotifyService/Pods-SolarNotifyService.release.xcconfig"; sourceTree = "<group>"; };
|
||||
D96D1DB4ED46A2640C1B9D34 /* Pods-SolarNotifyService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolarNotifyService.debug.xcconfig"; path = "Target Support Files/Pods-SolarNotifyService/Pods-SolarNotifyService.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
EDF483E994343CDFBF9BA347 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F357CFDA89A0D9E5692846D4 /* Pods_SolarWidgetExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SolarWidgetExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
@ -222,7 +235,7 @@
|
||||
files = (
|
||||
738C1EAD2D0D76A400A215F3 /* SwiftUI.framework in Frameworks */,
|
||||
738C1EAC2D0D76A400A215F3 /* WidgetKit.framework in Frameworks */,
|
||||
2630F2992106E991467A6FC4 /* Pods_SolarWidgetExtension.framework in Frameworks */,
|
||||
D962B51F682FBDEC00AC7281 /* Pods_SolarWidgetExtension.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -238,6 +251,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D5125CF12F159F0B8BC7641D /* Pods_SolarNotifyService.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -268,7 +282,8 @@
|
||||
731B7B6B2D0D6CE000CEB9B7 /* WidgetKit.framework */,
|
||||
731B7B6D2D0D6CE000CEB9B7 /* SwiftUI.framework */,
|
||||
16F41E029731EA30268EDE2A /* Pods_SolarShare.framework */,
|
||||
F357CFDA89A0D9E5692846D4 /* Pods_SolarWidgetExtension.framework */,
|
||||
02469D286F48D84300484B1E /* Pods_SolarNotifyService.framework */,
|
||||
7B1A159F5551E280D0EFC129 /* Pods_SolarWidgetExtension.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@ -335,6 +350,7 @@
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
7396A3512D16BD890095F4A8 /* NotifyDelegate.swift */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
@ -354,6 +370,15 @@
|
||||
2134F3903A0E8EB8CC2670BE /* Pods-SolarWidgetExtension.debug.xcconfig */,
|
||||
6618E2E3015264643175B43D /* Pods-SolarWidgetExtension.release.xcconfig */,
|
||||
BCE0C4086B776A27B202B373 /* Pods-SolarWidgetExtension.profile.xcconfig */,
|
||||
D96D1DB4ED46A2640C1B9D34 /* Pods-SolarNotifyService.debug.xcconfig */,
|
||||
D7E1FA77FDA53439DB2C0E75 /* Pods-SolarNotifyService.release.xcconfig */,
|
||||
4CBF45ABD292EE527D0A4D1E /* Pods-SolarNotifyService.profile.xcconfig */,
|
||||
8E44A071621D5CAF864FB2F1 /* Pods-Runner-SolarNotifyService.debug.xcconfig */,
|
||||
1077EFD9ACF793E9DA5D5B63 /* Pods-Runner-SolarNotifyService.release.xcconfig */,
|
||||
B4550C68292419CDC580808B /* Pods-Runner-SolarNotifyService.profile.xcconfig */,
|
||||
BFF3B436D74FA8CBFFE34A27 /* Pods-Runner-SolarWidgetExtension.debug.xcconfig */,
|
||||
931FBE9EDB99B3AD8B1FFB00 /* Pods-Runner-SolarWidgetExtension.release.xcconfig */,
|
||||
430F31F96B82659CBEAD4326 /* Pods-Runner-SolarWidgetExtension.profile.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
@ -427,6 +452,7 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 73DA8A072D05C7620024A03E /* Build configuration list for PBXNativeTarget "SolarNotifyService" */;
|
||||
buildPhases = (
|
||||
50F5704AB2E7309C916CA2E7 /* [CP] Check Pods Manifest.lock */,
|
||||
73DA89F62D05C7620024A03E /* Sources */,
|
||||
73DA89F72D05C7620024A03E /* Frameworks */,
|
||||
73DA89F82D05C7620024A03E /* Resources */,
|
||||
@ -622,6 +648,28 @@
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
50F5704AB2E7309C916CA2E7 /* [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-SolarNotifyService-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;
|
||||
};
|
||||
738C1EBE2D0D76C500A215F3 /* Copy Bundle Version */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -798,6 +846,7 @@
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
7396A3522D16BD890095F4A8 /* NotifyDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1229,6 +1278,7 @@
|
||||
};
|
||||
73DA8A032D05C7620024A03E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D96D1DB4ED46A2640C1B9D34 /* Pods-SolarNotifyService.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
@ -1270,6 +1320,7 @@
|
||||
};
|
||||
73DA8A042D05C7620024A03E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D7E1FA77FDA53439DB2C0E75 /* Pods-SolarNotifyService.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
@ -1308,6 +1359,7 @@
|
||||
};
|
||||
73DA8A052D05C7620024A03E /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 4CBF45ABD292EE527D0A4D1E /* Pods-SolarNotifyService.profile.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
|
@ -5,18 +5,22 @@ import workmanager
|
||||
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
|
||||
WorkmanagerPlugin.setPluginRegistrantCallback { registry in
|
||||
GeneratedPluginRegistrant.register(with: registry)
|
||||
}
|
||||
let notifyDelegate = NotifyDelegate()
|
||||
|
||||
UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*5))
|
||||
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
WorkmanagerPlugin.setPluginRegistrantCallback { registry in
|
||||
GeneratedPluginRegistrant.register(with: registry)
|
||||
}
|
||||
|
||||
UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*5))
|
||||
|
||||
UNUserNotificationCenter.current().delegate = notifyDelegate
|
||||
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
}
|
||||
|
39
ios/Runner/AppIntent.swift
Normal file
39
ios/Runner/AppIntent.swift
Normal file
@ -0,0 +1,39 @@
|
||||
//
|
||||
// AppIntent.swift
|
||||
// Runner
|
||||
//
|
||||
// Created by LittleSheep on 2024/12/21.
|
||||
//
|
||||
|
||||
import AppIntents
|
||||
import Flutter
|
||||
import Foundation
|
||||
import home_widget
|
||||
|
||||
@available(iOS 17, *)
|
||||
public struct AppBackgroundIntent: AppIntent {
|
||||
static public var title: LocalizedStringResource = "Solar Network Background Intent"
|
||||
|
||||
@Parameter(title: "Widget URI")
|
||||
var url: URL?
|
||||
|
||||
@Parameter(title: "AppGroup")
|
||||
var appGroup: String?
|
||||
|
||||
public init() {}
|
||||
|
||||
public init(url: URL?, appGroup: String?) {
|
||||
self.url = url
|
||||
self.appGroup = appGroup
|
||||
}
|
||||
|
||||
public func perform() async throws -> some IntentResult {
|
||||
await HomeWidgetBackgroundWorker.run(url: url, appGroup: appGroup!)
|
||||
|
||||
return .result()
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 17, *)
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
extension AppBackgroundIntent: ForegroundContinuableIntent {}
|
55
ios/Runner/NotifyDelegate.swift
Normal file
55
ios/Runner/NotifyDelegate.swift
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// NotifyDelegate.swift
|
||||
// Runner
|
||||
//
|
||||
// Created by LittleSheep on 2024/12/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import home_widget
|
||||
import Alamofire
|
||||
|
||||
class NotifyDelegate: UIResponder, UNUserNotificationCenterDelegate {
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||
if let textResponse = response as? UNTextInputNotificationResponse {
|
||||
let content = response.notification.request.content
|
||||
guard let metadata = content.userInfo["metadata"] as? [AnyHashable: Any] else {
|
||||
return
|
||||
}
|
||||
|
||||
let channelId = metadata["channel_id"] as? Int
|
||||
let eventId = metadata["event_id"] as? Int
|
||||
|
||||
let replyToken = metadata["reply_token"] as? String
|
||||
|
||||
if (channelId == nil || eventId == nil || replyToken == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
let serverUrl = "https://api.sn.solsynth.dev"
|
||||
let url = "\(serverUrl)/cgi/im/quick/\(channelId!)/reply/\(eventId!)?replyToken=\(replyToken!)"
|
||||
|
||||
let parameters: [String: Any] = [
|
||||
"type": "messages.new",
|
||||
"body": [
|
||||
"text": textResponse.userText,
|
||||
"algorithm": "plain"
|
||||
]
|
||||
]
|
||||
|
||||
AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
|
||||
.validate()
|
||||
.responseString { response in
|
||||
switch response.result {
|
||||
case .success(_):
|
||||
break
|
||||
case .failure(let error):
|
||||
print("Failed to send chat reply message: \(error)")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler()
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@
|
||||
|
||||
import UserNotifications
|
||||
import Intents
|
||||
import Kingfisher
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
enum ParseNotificationPayloadError: Error {
|
||||
case missingMetadata(String)
|
||||
@ -18,58 +20,6 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
private var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
private var bestAttemptContent: UNMutableNotificationContent?
|
||||
|
||||
private func fetchAvatarImage(from url: String, completion: @escaping (INImage?) -> Void) {
|
||||
guard let imageURL = URL(string: url) else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Define a cache location based on the URL hash
|
||||
let cacheFileName = imageURL.lastPathComponent
|
||||
let tempDirectory = FileManager.default.temporaryDirectory
|
||||
let cachedFileUrl = tempDirectory.appendingPathComponent(cacheFileName)
|
||||
|
||||
// Check if the image is already cached
|
||||
if FileManager.default.fileExists(atPath: cachedFileUrl.path) {
|
||||
do {
|
||||
let data = try Data(contentsOf: cachedFileUrl)
|
||||
let cachedImage = INImage(imageData: data) // No optional binding here
|
||||
completion(cachedImage)
|
||||
return
|
||||
} catch {
|
||||
print("Failed to load cached avatar image: \(error.localizedDescription)")
|
||||
try? FileManager.default.removeItem(at: cachedFileUrl) // Clear corrupted cache
|
||||
}
|
||||
}
|
||||
|
||||
// Download the image if not cached
|
||||
let session = URLSession(configuration: .default)
|
||||
session.downloadTask(with: imageURL) { localUrl, response, error in
|
||||
if let error = error {
|
||||
print("Failed to fetch avatar image: \(error.localizedDescription)")
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
|
||||
guard let localUrl = localUrl, let data = try? Data(contentsOf: localUrl) else {
|
||||
print("Failed to fetch data for avatar image.")
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
// Cache the downloaded file
|
||||
try FileManager.default.moveItem(at: localUrl, to: cachedFileUrl)
|
||||
} catch {
|
||||
print("Failed to cache avatar image: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
// Create INImage from the downloaded data
|
||||
let inImage = INImage(imageData: data) // Create directly
|
||||
completion(inImage)
|
||||
}.resume()
|
||||
}
|
||||
|
||||
override func didReceive(
|
||||
_ request: UNNotificationRequest,
|
||||
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
|
||||
@ -112,16 +62,39 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
throw ParseNotificationPayloadError.missingAvatarUrl("The notification has no avatar.")
|
||||
}
|
||||
|
||||
let replyableMessageCategory = UNNotificationCategory(
|
||||
identifier: content.categoryIdentifier,
|
||||
actions: [
|
||||
UNTextInputNotificationAction(
|
||||
identifier: "reply_action",
|
||||
title: "Reply",
|
||||
options: []
|
||||
),
|
||||
],
|
||||
intentIdentifiers: [],
|
||||
options: []
|
||||
)
|
||||
|
||||
UNUserNotificationCenter.current().setNotificationCategories([replyableMessageCategory])
|
||||
content.categoryIdentifier = replyableMessageCategory.identifier
|
||||
|
||||
let metadataCopy = metadata as? [String: String] ?? [:]
|
||||
let avatarUrl = getAttachmentUrl(for: avatarIdentifier)
|
||||
fetchAvatarImage(from: avatarUrl) { [weak self] inImage in
|
||||
guard let self = self else { return }
|
||||
KingfisherManager.shared.retrieveImage(with: URL(string: avatarUrl)!, completionHandler: { result in
|
||||
var image: Data?
|
||||
switch result {
|
||||
case .success(let value):
|
||||
image = value.image.pngData()
|
||||
case .failure(let error):
|
||||
print("Unable to get avatar url: \(error)")
|
||||
}
|
||||
|
||||
let handle = INPersonHandle(value: "\(metadata["user_id"] ?? "")", type: .unknown)
|
||||
let handle = INPersonHandle(value: "\(metadataCopy["user_id"] ?? "")", type: .unknown)
|
||||
let sender = INPerson(
|
||||
personHandle: handle,
|
||||
nameComponents: nil,
|
||||
displayName: content.title,
|
||||
image: inImage,
|
||||
image: image == nil ? nil : INImage(imageData: image!),
|
||||
contactIdentifier: nil,
|
||||
customIdentifier: nil
|
||||
)
|
||||
@ -132,12 +105,12 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
let updatedContent = try? request.content.updating(from: intent)
|
||||
self.contentHandler?(updatedContent ?? content)
|
||||
} else {
|
||||
let intent = self.createMessageIntent(with: sender, metadata: metadata, body: content.body)
|
||||
let intent = self.createMessageIntent(with: sender, metadata: metadataCopy, body: content.body)
|
||||
self.donateInteraction(for: intent)
|
||||
let updatedContent = try? request.content.updating(from: intent)
|
||||
self.contentHandler?(updatedContent ?? content)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func handleDefaultNotification(content: UNMutableNotificationContent) throws {
|
||||
@ -146,15 +119,15 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
|
||||
if let imageIdentifier = metadata["image"] as? String {
|
||||
attachMedia(to: content, withIdentifier: imageIdentifier)
|
||||
attachMedia(to: content, withIdentifier: imageIdentifier, fileType: UTType.jpeg, doScaleDown: true)
|
||||
} else if let avatarIdentifier = metadata["avatar"] as? String {
|
||||
attachMedia(to: content, withIdentifier: avatarIdentifier)
|
||||
attachMedia(to: content, withIdentifier: avatarIdentifier, fileType: UTType.jpeg, doScaleDown: true)
|
||||
} else {
|
||||
contentHandler?(content)
|
||||
}
|
||||
|
||||
contentHandler?(content)
|
||||
}
|
||||
|
||||
private func attachMedia(to content: UNMutableNotificationContent, withIdentifier identifier: String) {
|
||||
private func attachMedia(to content: UNMutableNotificationContent, withIdentifier identifier: String, fileType type: UTType?, doScaleDown scaleDown: Bool = false) {
|
||||
let attachmentUrl = getAttachmentUrl(for: identifier)
|
||||
|
||||
guard let remoteUrl = URL(string: attachmentUrl) else {
|
||||
@ -162,49 +135,62 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
return
|
||||
}
|
||||
|
||||
// Define a cache location based on the identifier
|
||||
let tempDirectory = FileManager.default.temporaryDirectory
|
||||
let cachedFileUrl = tempDirectory.appendingPathComponent(identifier)
|
||||
let targetSize = 800
|
||||
let scaleProcessor = ResizingImageProcessor(referenceSize: CGSize(width: targetSize, height: targetSize), mode: .aspectFit)
|
||||
|
||||
if FileManager.default.fileExists(atPath: cachedFileUrl.path) {
|
||||
// Use cached file
|
||||
attachLocalMedia(to: content, from: cachedFileUrl, withIdentifier: identifier)
|
||||
} else {
|
||||
// Download and cache the file
|
||||
let session = URLSession(configuration: .default)
|
||||
session.downloadTask(with: remoteUrl) { [weak content] localUrl, response, error in
|
||||
guard let content = content else { return }
|
||||
|
||||
if let error = error {
|
||||
print("Failed to download media: \(error.localizedDescription)")
|
||||
self.contentHandler?(content)
|
||||
return
|
||||
}
|
||||
|
||||
guard let localUrl = localUrl else {
|
||||
print("No local file URL after download")
|
||||
self.contentHandler?(content)
|
||||
return
|
||||
}
|
||||
KingfisherManager.shared.retrieveImage(with: remoteUrl, options: scaleDown ? [
|
||||
.processor(scaleProcessor)
|
||||
] : nil) { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
|
||||
switch result {
|
||||
case .success(let retrievalResult):
|
||||
// The image is either retrieved from cache or downloaded
|
||||
let tempDirectory = FileManager.default.temporaryDirectory
|
||||
let cachedFileUrl = tempDirectory.appendingPathComponent(identifier)
|
||||
|
||||
do {
|
||||
// Move the downloaded file to the cache
|
||||
try FileManager.default.moveItem(at: localUrl, to: cachedFileUrl)
|
||||
self.attachLocalMedia(to: content, from: cachedFileUrl, withIdentifier: identifier)
|
||||
// Write the image data to a temporary file for UNNotificationAttachment
|
||||
try retrievalResult.image.pngData()?.write(to: cachedFileUrl)
|
||||
self.attachLocalMedia(to: content, fileType: type?.identifier, from: cachedFileUrl, withIdentifier: identifier)
|
||||
} catch {
|
||||
print("Failed to cache media file: \(error.localizedDescription)")
|
||||
print("Failed to write media to temporary file: \(error.localizedDescription)")
|
||||
self.contentHandler?(content)
|
||||
}
|
||||
}.resume()
|
||||
|
||||
case .failure(let error):
|
||||
print("Failed to retrieve image: \(error.localizedDescription)")
|
||||
self.contentHandler?(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func attachLocalMedia(to content: UNMutableNotificationContent, from localUrl: URL, withIdentifier identifier: String) {
|
||||
if let attachment = try? UNNotificationAttachment(identifier: identifier, url: localUrl) {
|
||||
|
||||
private func attachLocalMedia(to content: UNMutableNotificationContent, fileType type: String?, from localUrl: URL, withIdentifier identifier: String) {
|
||||
do {
|
||||
let attachment = try UNNotificationAttachment(identifier: identifier, url: localUrl, options: [
|
||||
UNNotificationAttachmentOptionsTypeHintKey: type as Any,
|
||||
UNNotificationAttachmentOptionsThumbnailHiddenKey: 0,
|
||||
])
|
||||
content.attachments = [attachment]
|
||||
} else {
|
||||
print("Failed to create attachment from cached file: \(localUrl.path)")
|
||||
} catch let error as NSError {
|
||||
// Log detailed error information
|
||||
print("Failed to create attachment from file at \(localUrl.path)")
|
||||
print("Error: \(error.localizedDescription)")
|
||||
|
||||
// Check specific error codes if needed
|
||||
if error.domain == NSCocoaErrorDomain {
|
||||
switch error.code {
|
||||
case NSFileReadNoSuchFileError:
|
||||
print("File does not exist at \(localUrl.path)")
|
||||
case NSFileReadNoPermissionError:
|
||||
print("No permission to read file at \(localUrl.path)")
|
||||
default:
|
||||
print("Unhandled file error: \(error.code)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call content handler regardless of success or failure
|
||||
self.contentHandler?(content)
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import SwiftUI
|
||||
|
||||
struct CheckInProvider: TimelineProvider {
|
||||
func placeholder(in context: Context) -> CheckInEntry {
|
||||
CheckInEntry(date: Date(), user: nil, checkIn: nil)
|
||||
CheckInEntry(date: Date(), checkIn: nil)
|
||||
}
|
||||
|
||||
func getSnapshot(in context: Context, completion: @escaping (CheckInEntry) -> ()) {
|
||||
@ -23,24 +23,17 @@ struct CheckInProvider: TimelineProvider {
|
||||
jsonDecoder.dateDecodingStrategy = .formatted(dateFormatter)
|
||||
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||
|
||||
let userRaw = prefs?.string(forKey: "user")
|
||||
var user: SolarUser?
|
||||
if let userRaw = userRaw {
|
||||
user = try! jsonDecoder.decode(SolarUser.self, from: userRaw.data(using: .utf8)!)
|
||||
}
|
||||
|
||||
let checkInRaw = prefs?.string(forKey: "pas_check_in_record")
|
||||
var checkIn: SolarCheckInRecord?
|
||||
if let checkInRaw = checkInRaw {
|
||||
checkIn = try! jsonDecoder.decode(SolarCheckInRecord.self, from: checkInRaw.data(using: .utf8)!)
|
||||
if checkIn != nil && Calendar.current.isDate(checkIn!.createdAt, inSameDayAs: Date()) {
|
||||
if checkIn != nil && !Calendar.current.isDate(checkIn!.createdAt, inSameDayAs: Date()) {
|
||||
checkIn = nil
|
||||
}
|
||||
}
|
||||
|
||||
let entry = CheckInEntry(
|
||||
date: Date(),
|
||||
user: user,
|
||||
checkIn: checkIn
|
||||
)
|
||||
completion(entry)
|
||||
@ -56,7 +49,6 @@ struct CheckInProvider: TimelineProvider {
|
||||
|
||||
struct CheckInEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let user: SolarUser?
|
||||
let checkIn: SolarCheckInRecord?
|
||||
}
|
||||
|
||||
@ -135,10 +127,9 @@ struct CheckInWidget: Widget {
|
||||
#Preview(as: .systemSmall) {
|
||||
CheckInWidget()
|
||||
} timeline: {
|
||||
CheckInEntry(date: .now, user: nil, checkIn: nil)
|
||||
CheckInEntry(date: .now, checkIn: nil)
|
||||
CheckInEntry(
|
||||
date: .now,
|
||||
user: SolarUser(id: 1, name: "demo", nick: "Deemo"),
|
||||
checkIn: SolarCheckInRecord(id: 1, resultTier: 1, resultExperience: 100, createdAt: Date.now)
|
||||
)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ struct RandomPostProvider: TimelineProvider {
|
||||
func placeholder(in context: Context) -> RandomPostEntry {
|
||||
RandomPostEntry(date: Date(), user: nil, randomPost: nil, family: .systemMedium)
|
||||
}
|
||||
|
||||
|
||||
func getSnapshot(in context: Context, completion: @escaping (RandomPostEntry) -> ()) {
|
||||
let prefs = UserDefaults(suiteName: "group.solsynth.solian")
|
||||
|
||||
@ -45,7 +45,7 @@ struct RandomPostProvider: TimelineProvider {
|
||||
)
|
||||
completion(entry)
|
||||
}
|
||||
|
||||
|
||||
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
|
||||
getSnapshot(in: context) { (entry) in
|
||||
let timeline = Timeline(entries: [entry], policy: .atEnd)
|
||||
@ -64,7 +64,7 @@ struct RandomPostEntry: TimelineEntry {
|
||||
|
||||
struct RandomPostWidgetEntryView : View {
|
||||
var entry: RandomPostProvider.Entry
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
if let randomPost = entry.randomPost {
|
||||
@ -73,16 +73,27 @@ struct RandomPostWidgetEntryView : View {
|
||||
if let avatar = randomPost.publisher.avatar {
|
||||
let avatarUrl = getAttachmentUrl(for: avatar)
|
||||
let size: CGFloat = 28
|
||||
let scaleProcessor = ResizingImageProcessor(referenceSize: CGSize(width: size, height: size), mode: .aspectFill)
|
||||
|
||||
KFImage.url(URL(string: avatarUrl))
|
||||
.resizable()
|
||||
.setProcessor(ResizingImageProcessor(referenceSize: CGSize(width: size, height: size), mode: .aspectFit))
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.setProcessor(scaleProcessor)
|
||||
.fade(duration: 0.25)
|
||||
.placeholder{
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
}
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: size, height: size)
|
||||
.cornerRadius(size / 2)
|
||||
|
||||
.frame(width: size, height: size, alignment: .center)
|
||||
}
|
||||
|
||||
Text(randomPost.publisher.nick)
|
||||
.font(.system(size: 15))
|
||||
.opacity(0.9)
|
||||
|
||||
Text("@\(randomPost.publisher.name)")
|
||||
.font(.system(size: 13, design: .monospaced))
|
||||
.opacity(0.9)
|
||||
@ -152,7 +163,7 @@ struct RandomPostWidgetEntryView : View {
|
||||
|
||||
struct RandomPostWidget: Widget {
|
||||
let kind: String = "SolarRandomPostWidget"
|
||||
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: RandomPostProvider()) { entry in
|
||||
if #available(iOS 17.0, *) {
|
||||
|
@ -1,8 +1,10 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:croppy/croppy.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:easy_localization_loader/easy_localization_loader.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
@ -11,9 +13,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:relative_time/relative_time.dart';
|
||||
import 'package:responsive_framework/responsive_framework.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:surface/firebase_options.dart';
|
||||
import 'package:surface/providers/channel.dart';
|
||||
@ -37,12 +41,14 @@ import 'package:surface/types/realm.dart';
|
||||
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
|
||||
import 'package:surface/widgets/dialog.dart';
|
||||
import 'package:surface/widgets/version_label.dart';
|
||||
import 'package:version/version.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
import 'package:in_app_review/in_app_review.dart';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void appBackgroundDispatcher() {
|
||||
Workmanager().executeTask((task, inputData) async {
|
||||
print("Native called background task: $task");
|
||||
log("[WorkManager] Native called background task: $task");
|
||||
switch (task) {
|
||||
case Workmanager.iOSBackgroundTask:
|
||||
await Future.wait([widgetUpdateRandomPost()]);
|
||||
@ -87,13 +93,15 @@ void main() async {
|
||||
appBackgroundDispatcher,
|
||||
isInDebugMode: kDebugMode,
|
||||
);
|
||||
Workmanager().registerPeriodicTask(
|
||||
"widget-update-random-post",
|
||||
"WidgetUpdateRandomPost",
|
||||
frequency: Duration(minutes: 1),
|
||||
constraints: Constraints(networkType: NetworkType.connected),
|
||||
tag: "widget-update",
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
Workmanager().registerPeriodicTask(
|
||||
"widget-update-random-post",
|
||||
"WidgetUpdateRandomPost",
|
||||
frequency: Duration(minutes: 1),
|
||||
constraints: Constraints(networkType: NetworkType.connected),
|
||||
tag: "widget-update",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
runApp(const SolianApp());
|
||||
@ -122,7 +130,7 @@ class SolianApp extends StatelessWidget {
|
||||
Provider(create: (ctx) => HomeWidgetProvider(ctx)),
|
||||
|
||||
// Preferences layer
|
||||
Provider(create: (ctx) => ConfigProvider(ctx)),
|
||||
ChangeNotifierProvider(create: (ctx) => ConfigProvider(ctx)),
|
||||
|
||||
// Display layer
|
||||
ChangeNotifierProvider(create: (_) => ThemeProvider()),
|
||||
@ -198,6 +206,55 @@ class _AppSplashScreen extends StatefulWidget {
|
||||
class _AppSplashScreenState extends State<_AppSplashScreen> {
|
||||
bool _isReady = false;
|
||||
|
||||
void _tryRequestRating() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (prefs.containsKey('first_boot_time')) {
|
||||
final rawTime = prefs.getString('first_boot_time');
|
||||
final time = DateTime.tryParse(rawTime ?? '');
|
||||
if (time != null && time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) {
|
||||
final inAppReview = InAppReview.instance;
|
||||
if (prefs.getBool('rating_requested') == true) return;
|
||||
if (await inAppReview.isAvailable()) {
|
||||
await inAppReview.requestReview();
|
||||
prefs.setBool('rating_requested', true);
|
||||
} else {
|
||||
log('Unable request app review, unavailable');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
prefs.setString('first_boot_time', DateTime.now().toIso8601String());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _checkForUpdate() async {
|
||||
if (kIsWeb) return;
|
||||
try {
|
||||
final info = await PackageInfo.fromPlatform();
|
||||
final localVersionString = '${info.version}+${info.buildNumber}';
|
||||
final resp = await Dio(
|
||||
BaseOptions(
|
||||
sendTimeout: const Duration(seconds: 60),
|
||||
receiveTimeout: const Duration(seconds: 60),
|
||||
),
|
||||
).get(
|
||||
'https://git.solsynth.dev/api/v1/repos/HyperNet/Surface/tags?page=1&limit=1',
|
||||
);
|
||||
final remoteVersionString = (resp.data as List).firstOrNull?['name'] ?? '0.0.0+0';
|
||||
final remoteVersion = Version.parse(remoteVersionString.split('+').first);
|
||||
final localVersion = Version.parse(localVersionString.split('+').first);
|
||||
final remoteBuildNumber = int.tryParse(remoteVersionString.split('+').last) ?? 0;
|
||||
final localBuildNumber = int.tryParse(localVersionString.split('+').last) ?? 0;
|
||||
log("[Update] Local: $localVersionString, Remote: $remoteVersionString");
|
||||
if ((remoteVersion > localVersion || remoteBuildNumber > localBuildNumber) && mounted) {
|
||||
final config = context.read<ConfigProvider>();
|
||||
config.setUpdate(remoteVersionString);
|
||||
log("[Update] Update available: $remoteVersionString");
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) context.showErrorDialog('Unable to check update: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initialize() async {
|
||||
try {
|
||||
final home = context.read<HomeWidgetProvider>();
|
||||
@ -232,7 +289,11 @@ class _AppSplashScreenState extends State<_AppSplashScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initialize().then((_) => _postInitialization());
|
||||
_initialize().then((_) {
|
||||
_postInitialization();
|
||||
_tryRequestRating();
|
||||
_checkForUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -125,10 +125,8 @@ class ChatChannelProvider extends ChangeNotifier {
|
||||
final channelBox = await Hive.openBox<SnChatMessage>(
|
||||
'${ChatMessageController.kChatMessageBoxPrefix}${channel.id}',
|
||||
);
|
||||
final lastMessage = channelBox.isNotEmpty
|
||||
? channelBox.values
|
||||
.reduce((a, b) => a.createdAt.isAfter(b.createdAt) ? a : b)
|
||||
: null;
|
||||
final lastMessage =
|
||||
channelBox.isNotEmpty ? channelBox.values.reduce((a, b) => a.createdAt.isAfter(b.createdAt) ? a : b) : null;
|
||||
if (lastMessage != null) result.add(lastMessage);
|
||||
channelBox.close();
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ const Map<String, FilterQuality> kImageQualityLevel = {
|
||||
'settingsImageQualityHigh': FilterQuality.high,
|
||||
};
|
||||
|
||||
class ConfigProvider {
|
||||
class ConfigProvider extends ChangeNotifier {
|
||||
late final SharedPreferences prefs;
|
||||
|
||||
late final HomeWidgetProvider _home;
|
||||
@ -36,8 +36,16 @@ class ConfigProvider {
|
||||
String get serverUrl {
|
||||
return prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
|
||||
}
|
||||
|
||||
set serverUrl(String url) {
|
||||
prefs.setString(kNetworkServerStoreKey, url);
|
||||
_home.saveWidgetData("nex_server_url", url);
|
||||
}
|
||||
|
||||
String? updatableVersion;
|
||||
|
||||
void setUpdate(String newVersion) {
|
||||
updatableVersion = newVersion;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ const kNetworkServerDirectory = [
|
||||
('Local', 'http://localhost:8001'),
|
||||
];
|
||||
|
||||
Completer<String?>? _refreshCompleter;
|
||||
|
||||
class SnNetworkProvider {
|
||||
late final Dio client;
|
||||
|
||||
@ -90,6 +92,13 @@ class SnNetworkProvider {
|
||||
RequestOptions options,
|
||||
RequestInterceptorHandler handler,
|
||||
) async {
|
||||
final atk = await _getFreshAtk(client, prefs.getString(kAtkStoreKey), prefs.getString(kRtkStoreKey), (atk, rtk) {
|
||||
prefs.setString(kAtkStoreKey, atk);
|
||||
prefs.setString(kRtkStoreKey, rtk);
|
||||
});
|
||||
if (atk != null) {
|
||||
options.headers['Authorization'] = 'Bearer $atk';
|
||||
}
|
||||
options.headers['User-Agent'] = ua;
|
||||
return handler.next(options);
|
||||
},
|
||||
@ -135,9 +144,13 @@ class SnNetworkProvider {
|
||||
|
||||
final tkLock = Lock();
|
||||
|
||||
Completer<String?>? _refreshCompleter;
|
||||
|
||||
Future<String?> getFreshAtk() async {
|
||||
return await _getFreshAtk(client, _prefs.getString(kAtkStoreKey), _prefs.getString(kRtkStoreKey), (atk, rtk) {
|
||||
setTokenPair(atk, rtk);
|
||||
});
|
||||
}
|
||||
|
||||
static Future<String?> _getFreshAtk(Dio client, String? atk, String? rtk, Function(String atk, String rtk)? onRefresh) async {
|
||||
if (_refreshCompleter != null) {
|
||||
return await _refreshCompleter!.future;
|
||||
} else {
|
||||
@ -145,7 +158,6 @@ class SnNetworkProvider {
|
||||
}
|
||||
|
||||
try {
|
||||
var atk = _prefs.getString(kAtkStoreKey);
|
||||
if (atk != null) {
|
||||
final atkParts = atk.split('.');
|
||||
if (atkParts.length != 3) {
|
||||
@ -171,7 +183,13 @@ class SnNetworkProvider {
|
||||
final exp = jsonDecode(payload)['exp'];
|
||||
if (exp <= DateTime.now().millisecondsSinceEpoch ~/ 1000) {
|
||||
log('Access token need refresh, doing it at ${DateTime.now()}');
|
||||
atk = await refreshToken();
|
||||
final result = await _refreshToken(client.options.baseUrl, rtk);
|
||||
if (result == null) {
|
||||
atk = null;
|
||||
} else {
|
||||
atk = result.$1;
|
||||
onRefresh?.call(atk, result.$2);
|
||||
}
|
||||
}
|
||||
|
||||
if (atk != null) {
|
||||
@ -209,21 +227,28 @@ class SnNetworkProvider {
|
||||
|
||||
Future<String?> refreshToken() async {
|
||||
final rtk = _prefs.getString(kRtkStoreKey);
|
||||
final result = await _refreshToken(client.options.baseUrl, rtk);
|
||||
if (result == null) return null;
|
||||
_prefs.setString(kAtkStoreKey, result.$1);
|
||||
_prefs.setString(kRtkStoreKey, result.$2);
|
||||
return result.$1;
|
||||
}
|
||||
|
||||
static Future<(String, String)?> _refreshToken(String baseUrl, String? rtk) async {
|
||||
if (rtk == null) return null;
|
||||
|
||||
final dio = Dio();
|
||||
dio.options.baseUrl = client.options.baseUrl;
|
||||
dio.options.baseUrl = baseUrl;
|
||||
|
||||
final resp = await dio.post('/cgi/id/auth/token', data: {
|
||||
'grant_type': 'refresh_token',
|
||||
'refresh_token': rtk,
|
||||
});
|
||||
|
||||
final atk = resp.data['access_token'];
|
||||
final nRtk = resp.data['refresh_token'];
|
||||
setTokenPair(atk, nRtk);
|
||||
final String atk = resp.data['access_token'];
|
||||
final String nRtk = resp.data['refresh_token'];
|
||||
|
||||
return atk;
|
||||
return (atk, nRtk);
|
||||
}
|
||||
|
||||
void setBaseUrl(String url) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_app_update/flutter_app_update.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
@ -10,6 +13,7 @@ import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:surface/providers/config.dart';
|
||||
import 'package:surface/providers/post.dart';
|
||||
import 'package:surface/providers/sn_network.dart';
|
||||
import 'package:surface/providers/userinfo.dart';
|
||||
@ -69,18 +73,15 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
body: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return Align(
|
||||
alignment: constraints.maxWidth > 640
|
||||
? Alignment.center
|
||||
: Alignment.topCenter,
|
||||
alignment: constraints.maxWidth > 640 ? Alignment.center : Alignment.topCenter,
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 640),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: constraints.maxWidth > 640
|
||||
? MainAxisAlignment.center
|
||||
: MainAxisAlignment.start,
|
||||
mainAxisAlignment: constraints.maxWidth > 640 ? MainAxisAlignment.center : MainAxisAlignment.start,
|
||||
children: [
|
||||
_HomeDashSpecialDayWidget().padding(top: 8, horizontal: 8),
|
||||
_HomeDashSpecialDayWidget().padding(bottom: 8, horizontal: 8),
|
||||
_HomeDashUpdateWidget(padding: const EdgeInsets.only(bottom: 8, left: 8, right: 8)),
|
||||
StaggeredGrid.extent(
|
||||
maxCrossAxisExtent: 280,
|
||||
mainAxisSpacing: 8,
|
||||
@ -104,6 +105,52 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
class _HomeDashUpdateWidget extends StatelessWidget {
|
||||
final EdgeInsets? padding;
|
||||
|
||||
const _HomeDashUpdateWidget({super.key, this.padding});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final config = context.watch<ConfigProvider>();
|
||||
|
||||
return ListenableBuilder(
|
||||
listenable: config,
|
||||
builder: (context, _) {
|
||||
if (config.updatableVersion != null) {
|
||||
return Container(
|
||||
padding: padding,
|
||||
child: Card(
|
||||
child: ListTile(
|
||||
leading: Icon(Symbols.update),
|
||||
title: Text('updateAvailable').tr(),
|
||||
subtitle: Text(config.updatableVersion!),
|
||||
trailing: (kIsWeb || Platform.isWindows || Platform.isLinux)
|
||||
? null
|
||||
: IconButton(
|
||||
icon: const Icon(Symbols.arrow_right_alt),
|
||||
onPressed: () {
|
||||
final model = UpdateModel(
|
||||
'https://files.solsynth.dev/d/production01/solian/app-arm64-v8a-release.apk',
|
||||
'solian-app-release-${config.updatableVersion!}.apk',
|
||||
'ic_notification',
|
||||
'https://apps.apple.com/us/app/solian/id6499032345',
|
||||
);
|
||||
AzhonAppUpdate.update(model);
|
||||
context.showSnackbar('updateOngoing'.tr());
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return SizedBox.shrink();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HomeDashSpecialDayWidget extends StatelessWidget {
|
||||
const _HomeDashSpecialDayWidget({super.key});
|
||||
|
||||
@ -112,10 +159,10 @@ class _HomeDashSpecialDayWidget extends StatelessWidget {
|
||||
final ua = context.watch<UserProvider>();
|
||||
final today = DateTime.now();
|
||||
final birthday = ua.user?.profile?.birthday?.toLocal();
|
||||
final isBirthday = birthday != null &&
|
||||
birthday.day == today.day &&
|
||||
birthday.month == today.month;
|
||||
final isBirthday = birthday != null && birthday.day == today.day && birthday.month == today.month;
|
||||
|
||||
return Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
if (isBirthday)
|
||||
Card(
|
||||
@ -124,6 +171,20 @@ class _HomeDashSpecialDayWidget extends StatelessWidget {
|
||||
title: Text('happyBirthday').tr(args: [ua.user?.nick ?? 'user']),
|
||||
),
|
||||
).padding(bottom: 8),
|
||||
if (today.month == 12 && today.day == 25)
|
||||
Card(
|
||||
child: ListTile(
|
||||
leading: Text('🎄').fontSize(24),
|
||||
title: Text('celebrateMerryXmas').tr(args: [ua.user?.nick ?? 'user']),
|
||||
),
|
||||
),
|
||||
if (today.month == 1 && today.day == 1)
|
||||
Card(
|
||||
child: ListTile(
|
||||
leading: Text('🎉').fontSize(24),
|
||||
title: Text('celebrateNewYear').tr(args: [ua.user?.nick ?? 'user']),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -174,20 +235,15 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
|
||||
}
|
||||
|
||||
Widget _buildDetailChunk(int index, bool positive) {
|
||||
final prefix =
|
||||
positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint';
|
||||
final mod =
|
||||
positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount;
|
||||
final prefix = positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint';
|
||||
final mod = positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount;
|
||||
final pos = math.max(1, _todayRecord!.resultModifiers[index] % mod);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
prefix.tr(args: ['$prefix$pos'.tr()]),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.copyWith(fontWeight: FontWeight.bold),
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
'$prefix${pos}Description',
|
||||
@ -222,10 +278,7 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
|
||||
else
|
||||
Text(
|
||||
'dailyCheckEverythingIsNegative',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.copyWith(fontWeight: FontWeight.bold),
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
const Gap(8),
|
||||
if (_todayRecord?.resultTier != 4)
|
||||
@ -241,10 +294,7 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
|
||||
else
|
||||
Text(
|
||||
'dailyCheckEverythingIsPositive',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.copyWith(fontWeight: FontWeight.bold),
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
],
|
||||
),
|
||||
@ -362,12 +412,10 @@ class _HomeDashNotificationWidget extends StatefulWidget {
|
||||
const _HomeDashNotificationWidget({super.key});
|
||||
|
||||
@override
|
||||
State<_HomeDashNotificationWidget> createState() =>
|
||||
_HomeDashNotificationWidgetState();
|
||||
State<_HomeDashNotificationWidget> createState() => _HomeDashNotificationWidgetState();
|
||||
}
|
||||
|
||||
class _HomeDashNotificationWidgetState
|
||||
extends State<_HomeDashNotificationWidget> {
|
||||
class _HomeDashNotificationWidgetState extends State<_HomeDashNotificationWidget> {
|
||||
int? _count;
|
||||
|
||||
Future<void> _fetchNotificationCount() async {
|
||||
@ -404,9 +452,7 @@ class _HomeDashNotificationWidgetState
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
).tr(),
|
||||
Text(
|
||||
_count == null
|
||||
? 'loading'.tr()
|
||||
: 'notificationUnreadCount'.plural(_count ?? 0),
|
||||
_count == null ? 'loading'.tr() : 'notificationUnreadCount'.plural(_count ?? 0),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
],
|
||||
@ -437,12 +483,10 @@ class _HomeDashRecommendationPostWidget extends StatefulWidget {
|
||||
const _HomeDashRecommendationPostWidget({super.key});
|
||||
|
||||
@override
|
||||
State<_HomeDashRecommendationPostWidget> createState() =>
|
||||
_HomeDashRecommendationPostWidgetState();
|
||||
State<_HomeDashRecommendationPostWidget> createState() => _HomeDashRecommendationPostWidgetState();
|
||||
}
|
||||
|
||||
class _HomeDashRecommendationPostWidgetState
|
||||
extends State<_HomeDashRecommendationPostWidget> {
|
||||
class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendationPostWidget> {
|
||||
bool _isBusy = false;
|
||||
List<SnPost>? _posts;
|
||||
|
||||
@ -491,8 +535,7 @@ class _HomeDashRecommendationPostWidgetState
|
||||
).padding(horizontal: 18, top: 12, bottom: 8),
|
||||
Expanded(
|
||||
child: PageView.builder(
|
||||
scrollBehavior:
|
||||
ScrollConfiguration.of(context).copyWith(dragDevices: {
|
||||
scrollBehavior: ScrollConfiguration.of(context).copyWith(dragDevices: {
|
||||
PointerDeviceKind.mouse,
|
||||
PointerDeviceKind.touch,
|
||||
}),
|
||||
@ -505,8 +548,7 @@ class _HomeDashRecommendationPostWidgetState
|
||||
showMenu: false,
|
||||
).padding(bottom: 8),
|
||||
onTap: () {
|
||||
GoRouter.of(context)
|
||||
.pushNamed('postDetail', pathParameters: {
|
||||
GoRouter.of(context).pushNamed('postDetail', pathParameters: {
|
||||
'slug': _posts![index].id.toString(),
|
||||
});
|
||||
},
|
||||
|
@ -16,6 +16,7 @@ import firebase_messaging
|
||||
import flutter_udid
|
||||
import flutter_webrtc
|
||||
import gal
|
||||
import in_app_review
|
||||
import livekit_client
|
||||
import media_kit_libs_macos_video
|
||||
import media_kit_video
|
||||
@ -41,6 +42,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FlutterUdidPlugin.register(with: registry.registrar(forPlugin: "FlutterUdidPlugin"))
|
||||
FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
|
||||
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
|
||||
InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
|
||||
LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
|
||||
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
|
||||
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
|
||||
|
@ -26,7 +26,7 @@ PODS:
|
||||
- Firebase/Analytics (= 11.4.0)
|
||||
- firebase_core
|
||||
- FlutterMacOS
|
||||
- firebase_core (3.8.1):
|
||||
- firebase_core (3.9.0):
|
||||
- Firebase/CoreOnly (~> 11.4.0)
|
||||
- FlutterMacOS
|
||||
- firebase_messaging (15.1.6):
|
||||
@ -132,6 +132,8 @@ PODS:
|
||||
- GoogleUtilities/UserDefaults (8.0.2):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Privacy
|
||||
- in_app_review (2.0.0):
|
||||
- FlutterMacOS
|
||||
- livekit_client (2.3.2):
|
||||
- flutter_webrtc
|
||||
- FlutterMacOS
|
||||
@ -186,6 +188,7 @@ DEPENDENCIES:
|
||||
- flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- gal (from `Flutter/ephemeral/.symlinks/plugins/gal/darwin`)
|
||||
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
|
||||
- livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`)
|
||||
- media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`)
|
||||
- media_kit_native_event_loop (from `Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos`)
|
||||
@ -243,6 +246,8 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral
|
||||
gal:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/gal/darwin
|
||||
in_app_review:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/in_app_review/macos
|
||||
livekit_client:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos
|
||||
media_kit_libs_macos_video:
|
||||
@ -279,20 +284,21 @@ SPEC CHECKSUMS:
|
||||
file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d
|
||||
Firebase: cf1b19f21410b029b6786a54e9764a0cacad3c99
|
||||
firebase_analytics: a80b3d6645f2f12d626fde928b61dae12e5ea2ef
|
||||
firebase_core: e4a35c426636a2cce00a5163df7ba69bfd0cca57
|
||||
firebase_core: 1dfe1f4d02ad78be0277e320aa3d8384cf46231f
|
||||
firebase_messaging: 61f678060b69a7ae1013e3a939ec8e1c56ef6fcf
|
||||
FirebaseAnalytics: 3feef9ae8733c567866342a1000691baaa7cad49
|
||||
FirebaseCore: e0510f1523bc0eb21653cac00792e1e2bd6f1771
|
||||
FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
|
||||
FirebaseInstallations: 6ef4a1c7eb2a61ee1f74727d7f6ce2e72acf1414
|
||||
FirebaseMessaging: f8a160d99c2c2e5babbbcc90c4a3e15db036aee2
|
||||
flutter_udid: 6b2b89780c3dfeecf0047bdf93f622d6416b1c07
|
||||
flutter_udid: 2e7b3da4b5fdfba86a396b97898f5fe8f4ec1a52
|
||||
flutter_webrtc: 53c9e1285ab32dfb58afb1e1471416a877e23d7a
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
||||
GoogleAppMeasurement: 987769c4ca6b968f2479fbcc9fe3ce34af454b8e
|
||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||
in_app_review: a6a031b9acd03c7d103e341aa334adf2c493fb93
|
||||
livekit_client: 9fdcb22df3de55e6d4b24bdc3b5eb1c0269d774a
|
||||
media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82
|
||||
media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5
|
||||
|
40
pubspec.lock
40
pubspec.lock
@ -627,6 +627,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.2"
|
||||
flutter_app_update:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_app_update
|
||||
sha256: "09290240949c4651581cd6fc535e52d019e189e694d6019c56b5a56c2e69ba65"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
flutter_cache_manager:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -729,10 +737,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_udid
|
||||
sha256: "63384bd96203aaefccfd7137fab642edda18afede12b0e9e1a2c96fe2589fd07"
|
||||
sha256: be464dc5b1fb7ee894f6a32d65c086ca5e177fdcf9375ac08d77495b98150f84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "3.0.1"
|
||||
flutter_web_plugins:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@ -962,6 +970,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+1"
|
||||
in_app_review:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: in_app_review
|
||||
sha256: "36a06771b88fb0e79985b15e7f2ac0f1142e903fe72517f3c055d78bc3bc1819"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.10"
|
||||
in_app_review_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: in_app_review_platform_interface
|
||||
sha256: fed2c755f2125caa9ae10495a3c163aa7fab5af3585a9c62ef4a6920c5b45f10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1975,6 +1999,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: version
|
||||
sha256: "3d4140128e6ea10d83da32fef2fa4003fccbf6852217bb854845802f04191f94"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
very_good_infinite_list:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -2003,10 +2035,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: wakelock_plus
|
||||
sha256: "1aeab49f24aec1e5ab417d7cdfc47c7bbcb815353f1840667ffe68c89a0cd2e6"
|
||||
sha256: "36c88af0b930121941345306d259ec4cc4ecca3b151c02e3a9e71aede83c615e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.9"
|
||||
version: "1.2.10"
|
||||
wakelock_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 2.1.1+34
|
||||
version: 2.1.1+36
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.4
|
||||
@ -108,6 +108,9 @@ dependencies:
|
||||
home_widget: ^0.7.0
|
||||
receive_sharing_intent: ^1.8.1
|
||||
workmanager: ^0.5.2
|
||||
flutter_app_update: ^3.2.2
|
||||
in_app_review: ^2.0.10
|
||||
version: ^3.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
25
web/.well-known/apple-app-site-association
Normal file
25
web/.well-known/apple-app-site-association
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"applinks": {
|
||||
"apps": [],
|
||||
"details": [
|
||||
{
|
||||
"appIDs": [
|
||||
"W7HPZ53V6B.dev.solsynth.solian"
|
||||
],
|
||||
"paths": [
|
||||
"*"
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"/": "/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"webcredentials": {
|
||||
"apps": [
|
||||
"W7HPZ53V6B.dev.solsynth.solian"
|
||||
]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user