Compare commits
	
		
			5 Commits
		
	
	
		
			7182336a0d
			...
			b50191970e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b50191970e | |||
| 1b69e6dd42 | |||
| 39fb4d474f | |||
| 392aebcad7 | |||
| e9e3a4c474 | 
@@ -33,22 +33,6 @@
 | 
				
			|||||||
            </intent-filter>
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Sharing Intents -->
 | 
					            <!-- 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>
 | 
					            <intent-filter>
 | 
				
			||||||
                <action android:name="android.intent.action.SEND" />
 | 
					                <action android:name="android.intent.action.SEND" />
 | 
				
			||||||
                <category android:name="android.intent.category.DEFAULT" />
 | 
					                <category android:name="android.intent.category.DEFAULT" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ import androidx.compose.ui.unit.dp
 | 
				
			|||||||
import androidx.compose.ui.unit.sp
 | 
					import androidx.compose.ui.unit.sp
 | 
				
			||||||
import androidx.glance.GlanceId
 | 
					import androidx.glance.GlanceId
 | 
				
			||||||
import androidx.glance.GlanceModifier
 | 
					import androidx.glance.GlanceModifier
 | 
				
			||||||
 | 
					import androidx.glance.GlanceTheme
 | 
				
			||||||
import androidx.glance.action.clickable
 | 
					import androidx.glance.action.clickable
 | 
				
			||||||
import androidx.glance.appwidget.GlanceAppWidget
 | 
					import androidx.glance.appwidget.GlanceAppWidget
 | 
				
			||||||
import androidx.glance.appwidget.provideContent
 | 
					import androidx.glance.appwidget.provideContent
 | 
				
			||||||
@@ -25,8 +26,10 @@ import androidx.glance.text.Text
 | 
				
			|||||||
import androidx.glance.text.TextStyle
 | 
					import androidx.glance.text.TextStyle
 | 
				
			||||||
import com.google.gson.FieldNamingPolicy
 | 
					import com.google.gson.FieldNamingPolicy
 | 
				
			||||||
import com.google.gson.GsonBuilder
 | 
					import com.google.gson.GsonBuilder
 | 
				
			||||||
 | 
					import dev.solsynth.solian.MainActivity
 | 
				
			||||||
import dev.solsynth.solian.data.InstantAdapter
 | 
					import dev.solsynth.solian.data.InstantAdapter
 | 
				
			||||||
import dev.solsynth.solian.data.SolarCheckInRecord
 | 
					import dev.solsynth.solian.data.SolarCheckInRecord
 | 
				
			||||||
 | 
					import es.antonborri.home_widget.actionStartActivity
 | 
				
			||||||
import java.time.Instant
 | 
					import java.time.Instant
 | 
				
			||||||
import java.time.LocalDate
 | 
					import java.time.LocalDate
 | 
				
			||||||
import java.time.OffsetDateTime
 | 
					import java.time.OffsetDateTime
 | 
				
			||||||
@@ -39,9 +42,11 @@ class CheckInWidget : GlanceAppWidget() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    override suspend fun provideGlance(context: Context, id: GlanceId) {
 | 
					    override suspend fun provideGlance(context: Context, id: GlanceId) {
 | 
				
			||||||
        provideContent {
 | 
					        provideContent {
 | 
				
			||||||
 | 
					            GlanceTheme {
 | 
				
			||||||
                GlanceContent(context, currentState())
 | 
					                GlanceContent(context, currentState())
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Composable
 | 
					    @Composable
 | 
				
			||||||
    private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) {
 | 
					    private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) {
 | 
				
			||||||
@@ -53,18 +58,27 @@ class CheckInWidget : GlanceAppWidget() {
 | 
				
			|||||||
        val resultTierSymbols = listOf("大凶", "凶", "中平", "吉", "大吉")
 | 
					        val resultTierSymbols = listOf("大凶", "凶", "中平", "吉", "大吉")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        val prefs = currentState.preferences
 | 
					        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(
 | 
					        Column(
 | 
				
			||||||
            modifier = GlanceModifier
 | 
					            modifier = GlanceModifier
 | 
				
			||||||
                .fillMaxWidth()
 | 
					                .fillMaxWidth()
 | 
				
			||||||
                .fillMaxHeight()
 | 
					                .fillMaxHeight()
 | 
				
			||||||
                .background(Color.White)
 | 
					                .background(GlanceTheme.colors.widgetBackground)
 | 
				
			||||||
                .padding(16.dp)
 | 
					                .padding(16.dp)
 | 
				
			||||||
 | 
					                .clickable(
 | 
				
			||||||
 | 
					                    onClick = actionStartActivity<MainActivity>(
 | 
				
			||||||
 | 
					                        context,
 | 
				
			||||||
 | 
					                        Uri.parse("https://sn.solsynth.dev")
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            if (checkInRaw != null) {
 | 
					            if (checkIn != null) {
 | 
				
			||||||
                val checkIn: SolarCheckInRecord =
 | 
					 | 
				
			||||||
                    gson.fromJson(checkInRaw, SolarCheckInRecord::class.java)
 | 
					 | 
				
			||||||
                val dateFormatter = DateTimeFormatter.ofPattern("EEE, MM/dd")
 | 
					                val dateFormatter = DateTimeFormatter.ofPattern("EEE, MM/dd")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                val checkDate = checkIn.createdAt.atZone(ZoneId.of("UTC")).toLocalDate()
 | 
					                val checkDate = checkIn.createdAt.atZone(ZoneId.of("UTC")).toLocalDate()
 | 
				
			||||||
@@ -73,11 +87,11 @@ class CheckInWidget : GlanceAppWidget() {
 | 
				
			|||||||
                    Column {
 | 
					                    Column {
 | 
				
			||||||
                        Text(
 | 
					                        Text(
 | 
				
			||||||
                            text = resultTierSymbols[checkIn.resultTier],
 | 
					                            text = resultTierSymbols[checkIn.resultTier],
 | 
				
			||||||
                            style = TextStyle(fontSize = 25.sp, fontFamily = FontFamily.Serif)
 | 
					                            style = TextStyle(fontSize = 17.sp)
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                        Text(
 | 
					                        Text(
 | 
				
			||||||
                            text = "+${checkIn.resultExperience} EXP",
 | 
					                            text = "+${checkIn.resultExperience} EXP",
 | 
				
			||||||
                            style = TextStyle(fontSize = 15.sp, fontFamily = FontFamily.Monospace)
 | 
					                            style = TextStyle(fontSize = 13.sp, fontFamily = FontFamily.Monospace)
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Spacer(modifier = GlanceModifier.height(8.dp))
 | 
					                    Spacer(modifier = GlanceModifier.height(8.dp))
 | 
				
			||||||
@@ -88,18 +102,18 @@ class CheckInWidget : GlanceAppWidget() {
 | 
				
			|||||||
                                ZoneId.systemDefault()
 | 
					                                ZoneId.systemDefault()
 | 
				
			||||||
                            )
 | 
					                            )
 | 
				
			||||||
                                .format(dateFormatter),
 | 
					                                .format(dateFormatter),
 | 
				
			||||||
                            style = TextStyle(fontSize = 13.sp)
 | 
					                            style = TextStyle(fontSize = 11.sp)
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return@Column;
 | 
					                    return@Column;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Text(
 | 
					            Text(
 | 
				
			||||||
                text = "You haven't checked in today",
 | 
					                text = "You haven't checked in today",
 | 
				
			||||||
                style = TextStyle(fontSize = 15.sp)
 | 
					                style = TextStyle(fontSize = 15.sp)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
 | 
					import HomeWidgetGlanceState
 | 
				
			||||||
 | 
					import HomeWidgetGlanceStateDefinition
 | 
				
			||||||
import android.content.Context
 | 
					import android.content.Context
 | 
				
			||||||
import android.graphics.Bitmap
 | 
					 | 
				
			||||||
import android.graphics.BitmapFactory
 | 
					 | 
				
			||||||
import android.net.Uri
 | 
					import android.net.Uri
 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
import androidx.compose.ui.graphics.Color
 | 
					import androidx.compose.ui.graphics.Color
 | 
				
			||||||
@@ -9,17 +9,13 @@ import androidx.compose.ui.unit.sp
 | 
				
			|||||||
import androidx.glance.GlanceId
 | 
					import androidx.glance.GlanceId
 | 
				
			||||||
import androidx.glance.GlanceModifier
 | 
					import androidx.glance.GlanceModifier
 | 
				
			||||||
import androidx.glance.GlanceTheme
 | 
					import androidx.glance.GlanceTheme
 | 
				
			||||||
import androidx.glance.Image
 | 
					 | 
				
			||||||
import androidx.glance.ImageProvider
 | 
					 | 
				
			||||||
import androidx.glance.action.clickable
 | 
					import androidx.glance.action.clickable
 | 
				
			||||||
import androidx.glance.appwidget.GlanceAppWidget
 | 
					import androidx.glance.appwidget.GlanceAppWidget
 | 
				
			||||||
import androidx.glance.appwidget.cornerRadius
 | 
					 | 
				
			||||||
import androidx.glance.appwidget.provideContent
 | 
					import androidx.glance.appwidget.provideContent
 | 
				
			||||||
import androidx.glance.background
 | 
					import androidx.glance.background
 | 
				
			||||||
import androidx.glance.currentState
 | 
					import androidx.glance.currentState
 | 
				
			||||||
import androidx.glance.layout.Alignment
 | 
					import androidx.glance.layout.Alignment
 | 
				
			||||||
import androidx.glance.layout.Column
 | 
					import androidx.glance.layout.Column
 | 
				
			||||||
import androidx.glance.layout.ContentScale
 | 
					 | 
				
			||||||
import androidx.glance.layout.Row
 | 
					import androidx.glance.layout.Row
 | 
				
			||||||
import androidx.glance.layout.Spacer
 | 
					import androidx.glance.layout.Spacer
 | 
				
			||||||
import androidx.glance.layout.fillMaxHeight
 | 
					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.InstantAdapter
 | 
				
			||||||
import dev.solsynth.solian.data.SolarPost
 | 
					import dev.solsynth.solian.data.SolarPost
 | 
				
			||||||
import es.antonborri.home_widget.actionStartActivity
 | 
					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.Instant
 | 
				
			||||||
import java.time.LocalDateTime
 | 
					import java.time.LocalDateTime
 | 
				
			||||||
import java.time.ZoneId
 | 
					import java.time.ZoneId
 | 
				
			||||||
@@ -52,45 +44,18 @@ class RandomPostWidget : GlanceAppWidget() {
 | 
				
			|||||||
    override val stateDefinition: GlanceStateDefinition<*>?
 | 
					    override val stateDefinition: GlanceStateDefinition<*>?
 | 
				
			||||||
        get() = HomeWidgetGlanceStateDefinition()
 | 
					        get() = HomeWidgetGlanceStateDefinition()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val defaultUrl = "https://api.sn.solsynth.dev"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override suspend fun provideGlance(context: Context, id: GlanceId) {
 | 
					    override suspend fun provideGlance(context: Context, id: GlanceId) {
 | 
				
			||||||
        provideContent {
 | 
					        provideContent {
 | 
				
			||||||
            GlanceTheme {
 | 
					            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
 | 
					    @Composable
 | 
				
			||||||
    private fun GlanceContent(
 | 
					    private fun GlanceContent(
 | 
				
			||||||
        context: Context,
 | 
					        context: Context,
 | 
				
			||||||
        currentState: HomeWidgetGlanceState,
 | 
					        currentState: HomeWidgetGlanceState,
 | 
				
			||||||
        avatar: Bitmap?
 | 
					 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        val prefs = currentState.preferences
 | 
					        val prefs = currentState.preferences
 | 
				
			||||||
        val postRaw = prefs.getString("int_random_post", null)
 | 
					        val postRaw = prefs.getString("int_random_post", null)
 | 
				
			||||||
@@ -109,7 +74,7 @@ class RandomPostWidget : GlanceAppWidget() {
 | 
				
			|||||||
            modifier = GlanceModifier
 | 
					            modifier = GlanceModifier
 | 
				
			||||||
                .fillMaxWidth()
 | 
					                .fillMaxWidth()
 | 
				
			||||||
                .fillMaxHeight()
 | 
					                .fillMaxHeight()
 | 
				
			||||||
                .background(Color.White)
 | 
					                .background(GlanceTheme.colors.widgetBackground)
 | 
				
			||||||
                .padding(16.dp)
 | 
					                .padding(16.dp)
 | 
				
			||||||
                .clickable(
 | 
					                .clickable(
 | 
				
			||||||
                    onClick = actionStartActivity<MainActivity>(
 | 
					                    onClick = actionStartActivity<MainActivity>(
 | 
				
			||||||
@@ -120,17 +85,6 @@ class RandomPostWidget : GlanceAppWidget() {
 | 
				
			|||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            if (data != null) {
 | 
					            if (data != null) {
 | 
				
			||||||
                Row(verticalAlignment = Alignment.CenterVertically) {
 | 
					                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(
 | 
				
			||||||
                        text = data.publisher.nick,
 | 
					                        text = data.publisher.nick,
 | 
				
			||||||
                        style = TextStyle(fontSize = 15.sp)
 | 
					                        style = TextStyle(fontSize = 15.sp)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
    android:initialLayout="@layout/glance_default_loading_layout"
 | 
					    android:initialLayout="@layout/glance_default_loading_layout"
 | 
				
			||||||
    android:minWidth="120dp"
 | 
					    android:minWidth="40dp"
 | 
				
			||||||
    android:minHeight="40dp"
 | 
					    android:minHeight="40dp"
 | 
				
			||||||
    android:resizeMode="horizontal|vertical"
 | 
					    android:resizeMode="horizontal|vertical"
 | 
				
			||||||
    android:updatePeriodMillis="10000">
 | 
					    android:updatePeriodMillis="10000">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -370,6 +370,8 @@
 | 
				
			|||||||
  "dailyCheckNegativeHint6": "Going out",
 | 
					  "dailyCheckNegativeHint6": "Going out",
 | 
				
			||||||
  "dailyCheckNegativeHint6Description": "Forgot your umbrella and got caught in the rain",
 | 
					  "dailyCheckNegativeHint6Description": "Forgot your umbrella and got caught in the rain",
 | 
				
			||||||
  "happyBirthday": "Happy birthday, {}!",
 | 
					  "happyBirthday": "Happy birthday, {}!",
 | 
				
			||||||
 | 
					  "celebrateMerryXmas": "Merry christmas, {}!",
 | 
				
			||||||
 | 
					  "celebrateNewYear": "Happy new year, {}!",
 | 
				
			||||||
  "friendNew": "Add Friend",
 | 
					  "friendNew": "Add Friend",
 | 
				
			||||||
  "friendRequests": "Friend Requests",
 | 
					  "friendRequests": "Friend Requests",
 | 
				
			||||||
  "friendRequestsDescription": {
 | 
					  "friendRequestsDescription": {
 | 
				
			||||||
@@ -455,5 +457,7 @@
 | 
				
			|||||||
  "poweredBy": "Powered by {}",
 | 
					  "poweredBy": "Powered by {}",
 | 
				
			||||||
  "shareIntent": "Share",
 | 
					  "shareIntent": "Share",
 | 
				
			||||||
  "shareIntentDescription":  "What do you want to do with the content you are sharing?",
 | 
					  "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": "出门",
 | 
					  "dailyCheckNegativeHint6": "出门",
 | 
				
			||||||
  "dailyCheckNegativeHint6Description": "忘带伞遇上大雨",
 | 
					  "dailyCheckNegativeHint6Description": "忘带伞遇上大雨",
 | 
				
			||||||
  "happyBirthday": "生日快乐,{}!",
 | 
					  "happyBirthday": "生日快乐,{}!",
 | 
				
			||||||
 | 
					  "celebrateMerryXmas": "圣诞快乐,{}!",
 | 
				
			||||||
 | 
					  "celebrateNewYear": "新年快乐,{}!",
 | 
				
			||||||
  "friendNew": "添加好友",
 | 
					  "friendNew": "添加好友",
 | 
				
			||||||
  "friendRequests": "好友请求",
 | 
					  "friendRequests": "好友请求",
 | 
				
			||||||
  "friendRequestsDescription": {
 | 
					  "friendRequestsDescription": {
 | 
				
			||||||
@@ -453,5 +455,7 @@
 | 
				
			|||||||
  "poweredBy": "由 {} 提供支持",
 | 
					  "poweredBy": "由 {} 提供支持",
 | 
				
			||||||
  "shareIntent": "分享",
 | 
					  "shareIntent": "分享",
 | 
				
			||||||
  "shareIntentDescription": "您想对您分享的内容做些什么?",
 | 
					  "shareIntentDescription": "您想对您分享的内容做些什么?",
 | 
				
			||||||
  "shareIntentPostStory": "发布动态"
 | 
					  "shareIntentPostStory": "发布动态",
 | 
				
			||||||
 | 
					  "updateAvailable": "检测到更新可用",
 | 
				
			||||||
 | 
					  "updateOngoing": "正在更新,请稍后……"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ import SwiftUI
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct CheckInProvider: TimelineProvider {
 | 
					struct CheckInProvider: TimelineProvider {
 | 
				
			||||||
    func placeholder(in context: Context) -> CheckInEntry {
 | 
					    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) -> ()) {
 | 
					    func getSnapshot(in context: Context, completion: @escaping (CheckInEntry) -> ()) {
 | 
				
			||||||
@@ -23,24 +23,17 @@ struct CheckInProvider: TimelineProvider {
 | 
				
			|||||||
        jsonDecoder.dateDecodingStrategy = .formatted(dateFormatter)
 | 
					        jsonDecoder.dateDecodingStrategy = .formatted(dateFormatter)
 | 
				
			||||||
        jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
 | 
					        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")
 | 
					        let checkInRaw = prefs?.string(forKey: "pas_check_in_record")
 | 
				
			||||||
        var checkIn: SolarCheckInRecord?
 | 
					        var checkIn: SolarCheckInRecord?
 | 
				
			||||||
        if let checkInRaw = checkInRaw {
 | 
					        if let checkInRaw = checkInRaw {
 | 
				
			||||||
            checkIn = try! jsonDecoder.decode(SolarCheckInRecord.self, from: checkInRaw.data(using: .utf8)!)
 | 
					            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
 | 
					                checkIn = nil
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        let entry = CheckInEntry(
 | 
					        let entry = CheckInEntry(
 | 
				
			||||||
            date: Date(),
 | 
					            date: Date(),
 | 
				
			||||||
            user: user,
 | 
					 | 
				
			||||||
            checkIn: checkIn
 | 
					            checkIn: checkIn
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        completion(entry)
 | 
					        completion(entry)
 | 
				
			||||||
@@ -56,7 +49,6 @@ struct CheckInProvider: TimelineProvider {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct CheckInEntry: TimelineEntry {
 | 
					struct CheckInEntry: TimelineEntry {
 | 
				
			||||||
    let date: Date
 | 
					    let date: Date
 | 
				
			||||||
    let user: SolarUser?
 | 
					 | 
				
			||||||
    let checkIn: SolarCheckInRecord?
 | 
					    let checkIn: SolarCheckInRecord?
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -135,10 +127,9 @@ struct CheckInWidget: Widget {
 | 
				
			|||||||
#Preview(as: .systemSmall) {
 | 
					#Preview(as: .systemSmall) {
 | 
				
			||||||
    CheckInWidget()
 | 
					    CheckInWidget()
 | 
				
			||||||
} timeline: {
 | 
					} timeline: {
 | 
				
			||||||
    CheckInEntry(date: .now, user: nil, checkIn: nil)
 | 
					    CheckInEntry(date: .now, checkIn: nil)
 | 
				
			||||||
    CheckInEntry(
 | 
					    CheckInEntry(
 | 
				
			||||||
        date: .now,
 | 
					        date: .now,
 | 
				
			||||||
        user: SolarUser(id: 1, name: "demo", nick: "Deemo"),
 | 
					 | 
				
			||||||
        checkIn: SolarCheckInRecord(id: 1, resultTier: 1, resultExperience: 100, createdAt: Date.now)
 | 
					        checkIn: SolarCheckInRecord(id: 1, resultTier: 1, resultExperience: 100, createdAt: Date.now)
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,15 +73,20 @@ struct RandomPostWidgetEntryView : View {
 | 
				
			|||||||
                        if let avatar = randomPost.publisher.avatar {
 | 
					                        if let avatar = randomPost.publisher.avatar {
 | 
				
			||||||
                            let avatarUrl = getAttachmentUrl(for: avatar)
 | 
					                            let avatarUrl = getAttachmentUrl(for: avatar)
 | 
				
			||||||
                            let size: CGFloat = 28
 | 
					                            let size: CGFloat = 28
 | 
				
			||||||
                            let scaleProcessor = ResizingImageProcessor(referenceSize: CGSize(width: size, height: size), mode: .aspectFit)
 | 
					                            let scaleProcessor = ResizingImageProcessor(referenceSize: CGSize(width: size, height: size), mode: .aspectFill)
 | 
				
			||||||
                            
 | 
					                            
 | 
				
			||||||
                            KFImage.url(URL(string: avatarUrl))
 | 
					                            KFImage.url(URL(string: avatarUrl))
 | 
				
			||||||
                                .resizable()
 | 
					                                .resizable()
 | 
				
			||||||
                                .setProcessor(scaleProcessor)
 | 
					                                .setProcessor(scaleProcessor)
 | 
				
			||||||
                                .fade(duration: 0.25)
 | 
					                                .fade(duration: 0.25)
 | 
				
			||||||
                                .aspectRatio(contentMode: .fit)
 | 
					                                .placeholder{
 | 
				
			||||||
 | 
					                                    ProgressView()
 | 
				
			||||||
 | 
					                                        .progressViewStyle(CircularProgressViewStyle())
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                .aspectRatio(contentMode: .fill)
 | 
				
			||||||
                                .frame(width: size, height: size)
 | 
					                                .frame(width: size, height: size)
 | 
				
			||||||
                                .cornerRadius(size / 2)
 | 
					                                .cornerRadius(size / 2)
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
                                .frame(width: size, height: size, alignment: .center)
 | 
					                                .frame(width: size, height: size, alignment: .center)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        
 | 
					                        
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import 'dart:io';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
 | 
					import 'package:bitsdojo_window/bitsdojo_window.dart';
 | 
				
			||||||
import 'package:croppy/croppy.dart';
 | 
					import 'package:croppy/croppy.dart';
 | 
				
			||||||
 | 
					import 'package:dio/dio.dart';
 | 
				
			||||||
import 'package:easy_localization/easy_localization.dart';
 | 
					import 'package:easy_localization/easy_localization.dart';
 | 
				
			||||||
import 'package:easy_localization_loader/easy_localization_loader.dart';
 | 
					import 'package:easy_localization_loader/easy_localization_loader.dart';
 | 
				
			||||||
import 'package:firebase_core/firebase_core.dart';
 | 
					import 'package:firebase_core/firebase_core.dart';
 | 
				
			||||||
@@ -12,9 +13,11 @@ import 'package:flutter/material.dart';
 | 
				
			|||||||
import 'package:gap/gap.dart';
 | 
					import 'package:gap/gap.dart';
 | 
				
			||||||
import 'package:go_router/go_router.dart';
 | 
					import 'package:go_router/go_router.dart';
 | 
				
			||||||
import 'package:hive_flutter/hive_flutter.dart';
 | 
					import 'package:hive_flutter/hive_flutter.dart';
 | 
				
			||||||
 | 
					import 'package:package_info_plus/package_info_plus.dart';
 | 
				
			||||||
import 'package:provider/provider.dart';
 | 
					import 'package:provider/provider.dart';
 | 
				
			||||||
import 'package:relative_time/relative_time.dart';
 | 
					import 'package:relative_time/relative_time.dart';
 | 
				
			||||||
import 'package:responsive_framework/responsive_framework.dart';
 | 
					import 'package:responsive_framework/responsive_framework.dart';
 | 
				
			||||||
 | 
					import 'package:shared_preferences/shared_preferences.dart';
 | 
				
			||||||
import 'package:styled_widget/styled_widget.dart';
 | 
					import 'package:styled_widget/styled_widget.dart';
 | 
				
			||||||
import 'package:surface/firebase_options.dart';
 | 
					import 'package:surface/firebase_options.dart';
 | 
				
			||||||
import 'package:surface/providers/channel.dart';
 | 
					import 'package:surface/providers/channel.dart';
 | 
				
			||||||
@@ -38,7 +41,9 @@ import 'package:surface/types/realm.dart';
 | 
				
			|||||||
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
 | 
					import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
 | 
				
			||||||
import 'package:surface/widgets/dialog.dart';
 | 
					import 'package:surface/widgets/dialog.dart';
 | 
				
			||||||
import 'package:surface/widgets/version_label.dart';
 | 
					import 'package:surface/widgets/version_label.dart';
 | 
				
			||||||
 | 
					import 'package:version/version.dart';
 | 
				
			||||||
import 'package:workmanager/workmanager.dart';
 | 
					import 'package:workmanager/workmanager.dart';
 | 
				
			||||||
 | 
					import 'package:in_app_review/in_app_review.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pragma('vm:entry-point')
 | 
					@pragma('vm:entry-point')
 | 
				
			||||||
void appBackgroundDispatcher() {
 | 
					void appBackgroundDispatcher() {
 | 
				
			||||||
@@ -125,7 +130,7 @@ class SolianApp extends StatelessWidget {
 | 
				
			|||||||
            Provider(create: (ctx) => HomeWidgetProvider(ctx)),
 | 
					            Provider(create: (ctx) => HomeWidgetProvider(ctx)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Preferences layer
 | 
					            // Preferences layer
 | 
				
			||||||
            Provider(create: (ctx) => ConfigProvider(ctx)),
 | 
					            ChangeNotifierProvider(create: (ctx) => ConfigProvider(ctx)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Display layer
 | 
					            // Display layer
 | 
				
			||||||
            ChangeNotifierProvider(create: (_) => ThemeProvider()),
 | 
					            ChangeNotifierProvider(create: (_) => ThemeProvider()),
 | 
				
			||||||
@@ -201,6 +206,55 @@ class _AppSplashScreen extends StatefulWidget {
 | 
				
			|||||||
class _AppSplashScreenState extends State<_AppSplashScreen> {
 | 
					class _AppSplashScreenState extends State<_AppSplashScreen> {
 | 
				
			||||||
  bool _isReady = false;
 | 
					  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 {
 | 
					  Future<void> _initialize() async {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final home = context.read<HomeWidgetProvider>();
 | 
					      final home = context.read<HomeWidgetProvider>();
 | 
				
			||||||
@@ -235,7 +289,11 @@ class _AppSplashScreenState extends State<_AppSplashScreen> {
 | 
				
			|||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
    super.initState();
 | 
					    super.initState();
 | 
				
			||||||
    _initialize().then((_) => _postInitialization());
 | 
					    _initialize().then((_) {
 | 
				
			||||||
 | 
					      _postInitialization();
 | 
				
			||||||
 | 
					      _tryRequestRating();
 | 
				
			||||||
 | 
					      _checkForUpdate();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ const Map<String, FilterQuality> kImageQualityLevel = {
 | 
				
			|||||||
  'settingsImageQualityHigh': FilterQuality.high,
 | 
					  'settingsImageQualityHigh': FilterQuality.high,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConfigProvider {
 | 
					class ConfigProvider extends ChangeNotifier {
 | 
				
			||||||
  late final SharedPreferences prefs;
 | 
					  late final SharedPreferences prefs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  late final HomeWidgetProvider _home;
 | 
					  late final HomeWidgetProvider _home;
 | 
				
			||||||
@@ -36,8 +36,16 @@ class ConfigProvider {
 | 
				
			|||||||
  String get serverUrl {
 | 
					  String get serverUrl {
 | 
				
			||||||
    return prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
 | 
					    return prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  set serverUrl(String url) {
 | 
					  set serverUrl(String url) {
 | 
				
			||||||
    prefs.setString(kNetworkServerStoreKey, url);
 | 
					    prefs.setString(kNetworkServerStoreKey, url);
 | 
				
			||||||
    _home.saveWidgetData("nex_server_url", url);
 | 
					    _home.saveWidgetData("nex_server_url", url);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String? updatableVersion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void setUpdate(String newVersion) {
 | 
				
			||||||
 | 
					    updatableVersion = newVersion;
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,10 @@
 | 
				
			|||||||
 | 
					import 'dart:io';
 | 
				
			||||||
import 'dart:math' as math;
 | 
					import 'dart:math' as math;
 | 
				
			||||||
import 'dart:ui';
 | 
					import 'dart:ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:easy_localization/easy_localization.dart';
 | 
					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:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
 | 
				
			||||||
import 'package:gap/gap.dart';
 | 
					import 'package:gap/gap.dart';
 | 
				
			||||||
import 'package:go_router/go_router.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:provider/provider.dart';
 | 
				
			||||||
import 'package:styled_widget/styled_widget.dart';
 | 
					import 'package:styled_widget/styled_widget.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:surface/providers/config.dart';
 | 
				
			||||||
import 'package:surface/providers/post.dart';
 | 
					import 'package:surface/providers/post.dart';
 | 
				
			||||||
import 'package:surface/providers/sn_network.dart';
 | 
					import 'package:surface/providers/sn_network.dart';
 | 
				
			||||||
import 'package:surface/providers/userinfo.dart';
 | 
					import 'package:surface/providers/userinfo.dart';
 | 
				
			||||||
@@ -69,18 +73,15 @@ class _HomeScreenState extends State<HomeScreen> {
 | 
				
			|||||||
      body: LayoutBuilder(
 | 
					      body: LayoutBuilder(
 | 
				
			||||||
        builder: (context, constraints) {
 | 
					        builder: (context, constraints) {
 | 
				
			||||||
          return Align(
 | 
					          return Align(
 | 
				
			||||||
            alignment: constraints.maxWidth > 640
 | 
					            alignment: constraints.maxWidth > 640 ? Alignment.center : Alignment.topCenter,
 | 
				
			||||||
                ? Alignment.center
 | 
					 | 
				
			||||||
                : Alignment.topCenter,
 | 
					 | 
				
			||||||
            child: Container(
 | 
					            child: Container(
 | 
				
			||||||
              constraints: const BoxConstraints(maxWidth: 640),
 | 
					              constraints: const BoxConstraints(maxWidth: 640),
 | 
				
			||||||
              child: SingleChildScrollView(
 | 
					              child: SingleChildScrollView(
 | 
				
			||||||
                child: Column(
 | 
					                child: Column(
 | 
				
			||||||
                  mainAxisAlignment: constraints.maxWidth > 640
 | 
					                  mainAxisAlignment: constraints.maxWidth > 640 ? MainAxisAlignment.center : MainAxisAlignment.start,
 | 
				
			||||||
                      ? MainAxisAlignment.center
 | 
					 | 
				
			||||||
                      : MainAxisAlignment.start,
 | 
					 | 
				
			||||||
                  children: [
 | 
					                  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(
 | 
					                    StaggeredGrid.extent(
 | 
				
			||||||
                      maxCrossAxisExtent: 280,
 | 
					                      maxCrossAxisExtent: 280,
 | 
				
			||||||
                      mainAxisSpacing: 8,
 | 
					                      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 {
 | 
					class _HomeDashSpecialDayWidget extends StatelessWidget {
 | 
				
			||||||
  const _HomeDashSpecialDayWidget({super.key});
 | 
					  const _HomeDashSpecialDayWidget({super.key});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -112,10 +159,10 @@ class _HomeDashSpecialDayWidget extends StatelessWidget {
 | 
				
			|||||||
    final ua = context.watch<UserProvider>();
 | 
					    final ua = context.watch<UserProvider>();
 | 
				
			||||||
    final today = DateTime.now();
 | 
					    final today = DateTime.now();
 | 
				
			||||||
    final birthday = ua.user?.profile?.birthday?.toLocal();
 | 
					    final birthday = ua.user?.profile?.birthday?.toLocal();
 | 
				
			||||||
    final isBirthday = birthday != null &&
 | 
					    final isBirthday = birthday != null && birthday.day == today.day && birthday.month == today.month;
 | 
				
			||||||
        birthday.day == today.day &&
 | 
					
 | 
				
			||||||
        birthday.month == today.month;
 | 
					 | 
				
			||||||
    return Column(
 | 
					    return Column(
 | 
				
			||||||
 | 
					      spacing: 8,
 | 
				
			||||||
      children: [
 | 
					      children: [
 | 
				
			||||||
        if (isBirthday)
 | 
					        if (isBirthday)
 | 
				
			||||||
          Card(
 | 
					          Card(
 | 
				
			||||||
@@ -124,6 +171,20 @@ class _HomeDashSpecialDayWidget extends StatelessWidget {
 | 
				
			|||||||
              title: Text('happyBirthday').tr(args: [ua.user?.nick ?? 'user']),
 | 
					              title: Text('happyBirthday').tr(args: [ua.user?.nick ?? 'user']),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
          ).padding(bottom: 8),
 | 
					          ).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) {
 | 
					  Widget _buildDetailChunk(int index, bool positive) {
 | 
				
			||||||
    final prefix =
 | 
					    final prefix = positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint';
 | 
				
			||||||
        positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint';
 | 
					    final mod = positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount;
 | 
				
			||||||
    final mod =
 | 
					 | 
				
			||||||
        positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount;
 | 
					 | 
				
			||||||
    final pos = math.max(1, _todayRecord!.resultModifiers[index] % mod);
 | 
					    final pos = math.max(1, _todayRecord!.resultModifiers[index] % mod);
 | 
				
			||||||
    return Column(
 | 
					    return Column(
 | 
				
			||||||
      crossAxisAlignment: CrossAxisAlignment.start,
 | 
					      crossAxisAlignment: CrossAxisAlignment.start,
 | 
				
			||||||
      children: [
 | 
					      children: [
 | 
				
			||||||
        Text(
 | 
					        Text(
 | 
				
			||||||
          prefix.tr(args: ['$prefix$pos'.tr()]),
 | 
					          prefix.tr(args: ['$prefix$pos'.tr()]),
 | 
				
			||||||
          style: Theme.of(context)
 | 
					          style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold),
 | 
				
			||||||
              .textTheme
 | 
					 | 
				
			||||||
              .titleMedium!
 | 
					 | 
				
			||||||
              .copyWith(fontWeight: FontWeight.bold),
 | 
					 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        Text(
 | 
					        Text(
 | 
				
			||||||
          '$prefix${pos}Description',
 | 
					          '$prefix${pos}Description',
 | 
				
			||||||
@@ -222,10 +278,7 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
 | 
				
			|||||||
              else
 | 
					              else
 | 
				
			||||||
                Text(
 | 
					                Text(
 | 
				
			||||||
                  'dailyCheckEverythingIsNegative',
 | 
					                  'dailyCheckEverythingIsNegative',
 | 
				
			||||||
                  style: Theme.of(context)
 | 
					                  style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold),
 | 
				
			||||||
                      .textTheme
 | 
					 | 
				
			||||||
                      .titleMedium!
 | 
					 | 
				
			||||||
                      .copyWith(fontWeight: FontWeight.bold),
 | 
					 | 
				
			||||||
                ).tr(),
 | 
					                ).tr(),
 | 
				
			||||||
              const Gap(8),
 | 
					              const Gap(8),
 | 
				
			||||||
              if (_todayRecord?.resultTier != 4)
 | 
					              if (_todayRecord?.resultTier != 4)
 | 
				
			||||||
@@ -241,10 +294,7 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
 | 
				
			|||||||
              else
 | 
					              else
 | 
				
			||||||
                Text(
 | 
					                Text(
 | 
				
			||||||
                  'dailyCheckEverythingIsPositive',
 | 
					                  'dailyCheckEverythingIsPositive',
 | 
				
			||||||
                  style: Theme.of(context)
 | 
					                  style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold),
 | 
				
			||||||
                      .textTheme
 | 
					 | 
				
			||||||
                      .titleMedium!
 | 
					 | 
				
			||||||
                      .copyWith(fontWeight: FontWeight.bold),
 | 
					 | 
				
			||||||
                ).tr(),
 | 
					                ).tr(),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
@@ -362,12 +412,10 @@ class _HomeDashNotificationWidget extends StatefulWidget {
 | 
				
			|||||||
  const _HomeDashNotificationWidget({super.key});
 | 
					  const _HomeDashNotificationWidget({super.key});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  State<_HomeDashNotificationWidget> createState() =>
 | 
					  State<_HomeDashNotificationWidget> createState() => _HomeDashNotificationWidgetState();
 | 
				
			||||||
      _HomeDashNotificationWidgetState();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _HomeDashNotificationWidgetState
 | 
					class _HomeDashNotificationWidgetState extends State<_HomeDashNotificationWidget> {
 | 
				
			||||||
    extends State<_HomeDashNotificationWidget> {
 | 
					 | 
				
			||||||
  int? _count;
 | 
					  int? _count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<void> _fetchNotificationCount() async {
 | 
					  Future<void> _fetchNotificationCount() async {
 | 
				
			||||||
@@ -404,9 +452,7 @@ class _HomeDashNotificationWidgetState
 | 
				
			|||||||
                  style: Theme.of(context).textTheme.titleLarge,
 | 
					                  style: Theme.of(context).textTheme.titleLarge,
 | 
				
			||||||
                ).tr(),
 | 
					                ).tr(),
 | 
				
			||||||
                Text(
 | 
					                Text(
 | 
				
			||||||
                  _count == null
 | 
					                  _count == null ? 'loading'.tr() : 'notificationUnreadCount'.plural(_count ?? 0),
 | 
				
			||||||
                      ? 'loading'.tr()
 | 
					 | 
				
			||||||
                      : 'notificationUnreadCount'.plural(_count ?? 0),
 | 
					 | 
				
			||||||
                  style: Theme.of(context).textTheme.bodyLarge,
 | 
					                  style: Theme.of(context).textTheme.bodyLarge,
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
              ],
 | 
					              ],
 | 
				
			||||||
@@ -437,12 +483,10 @@ class _HomeDashRecommendationPostWidget extends StatefulWidget {
 | 
				
			|||||||
  const _HomeDashRecommendationPostWidget({super.key});
 | 
					  const _HomeDashRecommendationPostWidget({super.key});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  State<_HomeDashRecommendationPostWidget> createState() =>
 | 
					  State<_HomeDashRecommendationPostWidget> createState() => _HomeDashRecommendationPostWidgetState();
 | 
				
			||||||
      _HomeDashRecommendationPostWidgetState();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _HomeDashRecommendationPostWidgetState
 | 
					class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendationPostWidget> {
 | 
				
			||||||
    extends State<_HomeDashRecommendationPostWidget> {
 | 
					 | 
				
			||||||
  bool _isBusy = false;
 | 
					  bool _isBusy = false;
 | 
				
			||||||
  List<SnPost>? _posts;
 | 
					  List<SnPost>? _posts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -491,8 +535,7 @@ class _HomeDashRecommendationPostWidgetState
 | 
				
			|||||||
          ).padding(horizontal: 18, top: 12, bottom: 8),
 | 
					          ).padding(horizontal: 18, top: 12, bottom: 8),
 | 
				
			||||||
          Expanded(
 | 
					          Expanded(
 | 
				
			||||||
            child: PageView.builder(
 | 
					            child: PageView.builder(
 | 
				
			||||||
              scrollBehavior:
 | 
					              scrollBehavior: ScrollConfiguration.of(context).copyWith(dragDevices: {
 | 
				
			||||||
                  ScrollConfiguration.of(context).copyWith(dragDevices: {
 | 
					 | 
				
			||||||
                PointerDeviceKind.mouse,
 | 
					                PointerDeviceKind.mouse,
 | 
				
			||||||
                PointerDeviceKind.touch,
 | 
					                PointerDeviceKind.touch,
 | 
				
			||||||
              }),
 | 
					              }),
 | 
				
			||||||
@@ -505,8 +548,7 @@ class _HomeDashRecommendationPostWidgetState
 | 
				
			|||||||
                      showMenu: false,
 | 
					                      showMenu: false,
 | 
				
			||||||
                    ).padding(bottom: 8),
 | 
					                    ).padding(bottom: 8),
 | 
				
			||||||
                    onTap: () {
 | 
					                    onTap: () {
 | 
				
			||||||
                      GoRouter.of(context)
 | 
					                      GoRouter.of(context).pushNamed('postDetail', pathParameters: {
 | 
				
			||||||
                          .pushNamed('postDetail', pathParameters: {
 | 
					 | 
				
			||||||
                        'slug': _posts![index].id.toString(),
 | 
					                        'slug': _posts![index].id.toString(),
 | 
				
			||||||
                      });
 | 
					                      });
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@ import firebase_messaging
 | 
				
			|||||||
import flutter_udid
 | 
					import flutter_udid
 | 
				
			||||||
import flutter_webrtc
 | 
					import flutter_webrtc
 | 
				
			||||||
import gal
 | 
					import gal
 | 
				
			||||||
 | 
					import in_app_review
 | 
				
			||||||
import livekit_client
 | 
					import livekit_client
 | 
				
			||||||
import media_kit_libs_macos_video
 | 
					import media_kit_libs_macos_video
 | 
				
			||||||
import media_kit_video
 | 
					import media_kit_video
 | 
				
			||||||
@@ -41,6 +42,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 | 
				
			|||||||
  FlutterUdidPlugin.register(with: registry.registrar(forPlugin: "FlutterUdidPlugin"))
 | 
					  FlutterUdidPlugin.register(with: registry.registrar(forPlugin: "FlutterUdidPlugin"))
 | 
				
			||||||
  FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
 | 
					  FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
 | 
				
			||||||
  GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
 | 
					  GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
 | 
				
			||||||
 | 
					  InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
 | 
				
			||||||
  LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
 | 
					  LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
 | 
				
			||||||
  MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
 | 
					  MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
 | 
				
			||||||
  MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
 | 
					  MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										36
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								pubspec.lock
									
									
									
									
									
								
							@@ -627,6 +627,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "4.5.2"
 | 
					    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:
 | 
					  flutter_cache_manager:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -729,10 +737,10 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: flutter_udid
 | 
					      name: flutter_udid
 | 
				
			||||||
      sha256: "63384bd96203aaefccfd7137fab642edda18afede12b0e9e1a2c96fe2589fd07"
 | 
					      sha256: be464dc5b1fb7ee894f6a32d65c086ca5e177fdcf9375ac08d77495b98150f84
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.0.0"
 | 
					    version: "3.0.1"
 | 
				
			||||||
  flutter_web_plugins:
 | 
					  flutter_web_plugins:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
@@ -962,6 +970,22 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "0.2.1+1"
 | 
					    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:
 | 
					  intl:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -1975,6 +1999,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.1.4"
 | 
					    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:
 | 
					  very_good_infinite_list:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    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
 | 
					# 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
 | 
					# 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.
 | 
					# of the product and file versions while build-number is used as the build suffix.
 | 
				
			||||||
version: 2.1.1+36
 | 
					version: 2.1.1+35
 | 
				
			||||||
 | 
					
 | 
				
			||||||
environment:
 | 
					environment:
 | 
				
			||||||
  sdk: ^3.5.4
 | 
					  sdk: ^3.5.4
 | 
				
			||||||
@@ -108,6 +108,9 @@ dependencies:
 | 
				
			|||||||
  home_widget: ^0.7.0
 | 
					  home_widget: ^0.7.0
 | 
				
			||||||
  receive_sharing_intent: ^1.8.1
 | 
					  receive_sharing_intent: ^1.8.1
 | 
				
			||||||
  workmanager: ^0.5.2
 | 
					  workmanager: ^0.5.2
 | 
				
			||||||
 | 
					  flutter_app_update: ^3.2.2
 | 
				
			||||||
 | 
					  in_app_review: ^2.0.10
 | 
				
			||||||
 | 
					  version: ^3.0.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
  flutter_test:
 | 
					  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