Compare commits
14 Commits
7182336a0d
...
2.1.1+37
Author | SHA1 | Date | |
---|---|---|---|
|
9ac4a940dd | ||
|
ec050ab712 | ||
|
77e3ce8bcc | ||
|
f5dcf71e10 | ||
|
7fc18b40db | ||
|
8c8ab24c9e | ||
|
a319bd7f8c | ||
|
6427ec1f82 | ||
|
35dc7f4392 | ||
|
b50191970e | ||
|
1b69e6dd42 | ||
|
39fb4d474f | ||
|
392aebcad7 | ||
|
e9e3a4c474 |
@@ -10,8 +10,9 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "androidx.glance:glance:1.1.1"
|
implementation 'com.google.android.material:material:1.12.0'
|
||||||
implementation "androidx.glance:glance-appwidget:1.1.1"
|
implementation 'androidx.glance:glance:1.1.1'
|
||||||
|
implementation 'androidx.glance:glance-appwidget:1.1.1'
|
||||||
implementation 'androidx.compose.foundation:foundation-layout-android:1.7.6'
|
implementation 'androidx.compose.foundation:foundation-layout-android:1.7.6'
|
||||||
implementation 'com.google.code.gson:gson:2.10.1'
|
implementation 'com.google.code.gson:gson:2.10.1'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||||
@@ -19,6 +20,12 @@ dependencies {
|
|||||||
implementation 'io.coil-kt.coil3:coil-network-okhttp:3.0.4'
|
implementation 'io.coil-kt.coil3:coil-network-okhttp:3.0.4'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose true
|
compose true
|
||||||
@@ -49,6 +56,15 @@ android {
|
|||||||
versionName = flutter.versionName
|
versionName = flutter.versionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
keyAlias = keystoreProperties['keyAlias']
|
||||||
|
keyPassword = keystoreProperties['keyPassword']
|
||||||
|
storeFile = keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||||
|
storePassword = keystoreProperties['storePassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
debuggable true
|
debuggable true
|
||||||
@@ -56,9 +72,7 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
release {
|
release {
|
||||||
// TODO: Add your own signing config for the release build.
|
signingConfig = signingConfigs.release
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
|
||||||
signingConfig = signingConfigs.debug
|
|
||||||
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
|
@@ -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" />
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
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.unit.dp
|
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 +25,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,7 +41,9 @@ class CheckInWidget : GlanceAppWidget() {
|
|||||||
|
|
||||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||||
provideContent {
|
provideContent {
|
||||||
GlanceContent(context, currentState())
|
GlanceTheme {
|
||||||
|
GlanceContent(context, currentState())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,18 +57,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 +86,18 @@ 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,
|
||||||
|
color = GlanceTheme.colors.onSurface
|
||||||
|
)
|
||||||
)
|
)
|
||||||
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,
|
||||||
|
color = GlanceTheme.colors.onSurface
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = GlanceModifier.height(8.dp))
|
Spacer(modifier = GlanceModifier.height(8.dp))
|
||||||
@@ -88,18 +108,21 @@ class CheckInWidget : GlanceAppWidget() {
|
|||||||
ZoneId.systemDefault()
|
ZoneId.systemDefault()
|
||||||
)
|
)
|
||||||
.format(dateFormatter),
|
.format(dateFormatter),
|
||||||
style = TextStyle(fontSize = 13.sp)
|
style = TextStyle(
|
||||||
|
fontSize = 11.sp,
|
||||||
|
color = GlanceTheme.colors.onSurface
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
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, color = GlanceTheme.colors.onSurface)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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,25 +85,18 @@ 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, color = GlanceTheme.colors.onSurface)
|
||||||
)
|
)
|
||||||
Spacer(modifier = GlanceModifier.width(8.dp))
|
Spacer(modifier = GlanceModifier.width(8.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "@${data.publisher.name}",
|
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) {
|
if (data.body.title != null) {
|
||||||
Text(
|
Text(
|
||||||
text = data.body.title,
|
text = data.body.title,
|
||||||
style = TextStyle(fontSize = 25.sp)
|
style = TextStyle(fontSize = 19.sp, color = GlanceTheme.colors.onSurface)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (data.body.description != null) {
|
if (data.body.description != null) {
|
||||||
Text(
|
Text(
|
||||||
text = data.body.description,
|
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(
|
||||||
text = data.body.content ?: "No content",
|
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))
|
Spacer(modifier = GlanceModifier.height(8.dp))
|
||||||
@@ -172,12 +130,16 @@ class RandomPostWidget : GlanceAppWidget() {
|
|||||||
Text(
|
Text(
|
||||||
LocalDateTime.ofInstant(data.createdAt, ZoneId.systemDefault())
|
LocalDateTime.ofInstant(data.createdAt, ZoneId.systemDefault())
|
||||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")),
|
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")),
|
||||||
style = TextStyle(fontSize = 13.sp),
|
style = TextStyle(fontSize = 13.sp, color = GlanceTheme.colors.onSurface),
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
"#${data.id}",
|
"#${data.id}",
|
||||||
style = TextStyle(fontSize = 11.sp, fontWeight = FontWeight.Bold),
|
style = TextStyle(
|
||||||
|
fontSize = 11.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = GlanceTheme.colors.onSurface
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
return@Column;
|
return@Column;
|
||||||
@@ -189,12 +151,16 @@ class RandomPostWidget : GlanceAppWidget() {
|
|||||||
horizontalAlignment = Alignment.Horizontal.CenterHorizontally
|
horizontalAlignment = Alignment.Horizontal.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Unable to fetch post",
|
text = "No Recommendations",
|
||||||
style = TextStyle(fontSize = 17.sp, fontWeight = FontWeight.Bold)
|
style = TextStyle(
|
||||||
|
fontSize = 17.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = GlanceTheme.colors.onSurface
|
||||||
|
)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Check your internet connection",
|
text = "Open app to load some posts",
|
||||||
style = TextStyle(fontSize = 15.sp)
|
style = TextStyle(fontSize = 15.sp, color = GlanceTheme.colors.onSurface)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 537 B |
Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 736 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#FFFFFFFF</color>
|
<color name="ic_launcher_background">#FFFFFFFF</color>
|
||||||
|
<color name="ic_notification_background">#00000000</color>
|
||||||
</resources>
|
</resources>
|
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
<style name="LaunchTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
|
||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
the Flutter engine draws its first frame -->
|
the Flutter engine draws its first frame -->
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
running.
|
running.
|
||||||
|
|
||||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
<style name="NormalTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
<item name="android:windowBackground">?android:colorBackground</item>
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -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">
|
||||||
|
@@ -139,6 +139,8 @@
|
|||||||
"fieldPostTitle": "Title",
|
"fieldPostTitle": "Title",
|
||||||
"fieldPostDescription": "Description",
|
"fieldPostDescription": "Description",
|
||||||
"fieldPostTags": "Tags",
|
"fieldPostTags": "Tags",
|
||||||
|
"fieldPostAlias": "Alias",
|
||||||
|
"fieldPostAliasHint": "Optional, used to represent the post in URL, should follow URL-Safe.",
|
||||||
"postPublish": "Publish",
|
"postPublish": "Publish",
|
||||||
"postPosted": "Post has been posted.",
|
"postPosted": "Post has been posted.",
|
||||||
"postPublishedAt": "Published At",
|
"postPublishedAt": "Published At",
|
||||||
@@ -370,6 +372,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 +459,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": "正在更新,请稍后..."
|
||||||
}
|
}
|
||||||
|
@@ -123,6 +123,8 @@
|
|||||||
"fieldPostTitle": "标题",
|
"fieldPostTitle": "标题",
|
||||||
"fieldPostDescription": "描述",
|
"fieldPostDescription": "描述",
|
||||||
"fieldPostTags": "标签",
|
"fieldPostTags": "标签",
|
||||||
|
"fieldPostAlias": "别名",
|
||||||
|
"fieldPostAliasHint": "可选项,用于在 URL 中表示该帖子,应遵循 URL-Safe 的原则。",
|
||||||
"postPublish": "发布",
|
"postPublish": "发布",
|
||||||
"postPublishedAt": "发布于",
|
"postPublishedAt": "发布于",
|
||||||
"postPublishedUntil": "取消发布于",
|
"postPublishedUntil": "取消发布于",
|
||||||
@@ -368,6 +370,8 @@
|
|||||||
"dailyCheckNegativeHint6": "出门",
|
"dailyCheckNegativeHint6": "出门",
|
||||||
"dailyCheckNegativeHint6Description": "忘带伞遇上大雨",
|
"dailyCheckNegativeHint6Description": "忘带伞遇上大雨",
|
||||||
"happyBirthday": "生日快乐,{}!",
|
"happyBirthday": "生日快乐,{}!",
|
||||||
|
"celebrateMerryXmas": "圣诞快乐,{}!",
|
||||||
|
"celebrateNewYear": "新年快乐,{}!",
|
||||||
"friendNew": "添加好友",
|
"friendNew": "添加好友",
|
||||||
"friendRequests": "好友请求",
|
"friendRequests": "好友请求",
|
||||||
"friendRequestsDescription": {
|
"friendRequestsDescription": {
|
||||||
@@ -453,5 +457,7 @@
|
|||||||
"poweredBy": "由 {} 提供支持",
|
"poweredBy": "由 {} 提供支持",
|
||||||
"shareIntent": "分享",
|
"shareIntent": "分享",
|
||||||
"shareIntentDescription": "您想对您分享的内容做些什么?",
|
"shareIntentDescription": "您想对您分享的内容做些什么?",
|
||||||
"shareIntentPostStory": "发布动态"
|
"shareIntentPostStory": "发布动态",
|
||||||
|
"updateAvailable": "检测到更新可用",
|
||||||
|
"updateOngoing": "正在更新,请稍后……"
|
||||||
}
|
}
|
||||||
|
@@ -103,6 +103,8 @@ PODS:
|
|||||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- flutter_app_update (0.0.1):
|
||||||
|
- Flutter
|
||||||
- flutter_native_splash (2.4.3):
|
- flutter_native_splash (2.4.3):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_udid (0.0.1):
|
- flutter_udid (0.0.1):
|
||||||
@@ -168,6 +170,8 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- image_picker_ios (0.0.1):
|
- image_picker_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- in_app_review (2.0.0):
|
||||||
|
- Flutter
|
||||||
- Kingfisher (8.1.3)
|
- Kingfisher (8.1.3)
|
||||||
- livekit_client (2.3.2):
|
- livekit_client (2.3.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
@@ -232,12 +236,14 @@ DEPENDENCIES:
|
|||||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
|
||||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||||
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
|
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
|
||||||
- flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`)
|
- flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`)
|
||||||
- gal (from `.symlinks/plugins/gal/darwin`)
|
- gal (from `.symlinks/plugins/gal/darwin`)
|
||||||
- home_widget (from `.symlinks/plugins/home_widget/ios`)
|
- home_widget (from `.symlinks/plugins/home_widget/ios`)
|
||||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||||
|
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
||||||
- Kingfisher (~> 8.0)
|
- Kingfisher (~> 8.0)
|
||||||
- livekit_client (from `.symlinks/plugins/livekit_client/ios`)
|
- livekit_client (from `.symlinks/plugins/livekit_client/ios`)
|
||||||
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
||||||
@@ -298,6 +304,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/firebase_messaging/ios"
|
:path: ".symlinks/plugins/firebase_messaging/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
flutter_app_update:
|
||||||
|
:path: ".symlinks/plugins/flutter_app_update/ios"
|
||||||
flutter_native_splash:
|
flutter_native_splash:
|
||||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||||
flutter_udid:
|
flutter_udid:
|
||||||
@@ -310,6 +318,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/home_widget/ios"
|
:path: ".symlinks/plugins/home_widget/ios"
|
||||||
image_picker_ios:
|
image_picker_ios:
|
||||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||||
|
in_app_review:
|
||||||
|
:path: ".symlinks/plugins/in_app_review/ios"
|
||||||
livekit_client:
|
livekit_client:
|
||||||
:path: ".symlinks/plugins/livekit_client/ios"
|
:path: ".symlinks/plugins/livekit_client/ios"
|
||||||
media_kit_libs_ios_video:
|
media_kit_libs_ios_video:
|
||||||
@@ -364,8 +374,9 @@ SPEC CHECKSUMS:
|
|||||||
FirebaseInstallations: 6ef4a1c7eb2a61ee1f74727d7f6ce2e72acf1414
|
FirebaseInstallations: 6ef4a1c7eb2a61ee1f74727d7f6ce2e72acf1414
|
||||||
FirebaseMessaging: f8a160d99c2c2e5babbbcc90c4a3e15db036aee2
|
FirebaseMessaging: f8a160d99c2c2e5babbbcc90c4a3e15db036aee2
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
|
flutter_app_update: 65f61da626cb111d1b24674abc4b01728d7723bc
|
||||||
flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a
|
flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a
|
||||||
flutter_udid: a2482c67a61b9c806ef59dd82ed8d007f1b7ac04
|
flutter_udid: b2417673f287ee62817a1de3d1643f47b9f508ab
|
||||||
flutter_webrtc: 1a53bd24f97bcfeff512f13699e721897f261563
|
flutter_webrtc: 1a53bd24f97bcfeff512f13699e721897f261563
|
||||||
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
||||||
GoogleAppMeasurement: 987769c4ca6b968f2479fbcc9fe3ce34af454b8e
|
GoogleAppMeasurement: 987769c4ca6b968f2479fbcc9fe3ce34af454b8e
|
||||||
@@ -373,6 +384,7 @@ SPEC CHECKSUMS:
|
|||||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||||
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57
|
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57
|
||||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||||
|
in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
|
||||||
Kingfisher: f2af9028b16baf9dc6c07c570072bc41cbf009ef
|
Kingfisher: f2af9028b16baf9dc6c07c570072bc41cbf009ef
|
||||||
livekit_client: 6108dad8b77db3142bafd4c630f471d0a54335cd
|
livekit_client: 6108dad8b77db3142bafd4c630f471d0a54335cd
|
||||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
||||||
|
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@ struct RandomPostProvider: TimelineProvider {
|
|||||||
func placeholder(in context: Context) -> RandomPostEntry {
|
func placeholder(in context: Context) -> RandomPostEntry {
|
||||||
RandomPostEntry(date: Date(), user: nil, randomPost: nil, family: .systemMedium)
|
RandomPostEntry(date: Date(), user: nil, randomPost: nil, family: .systemMedium)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSnapshot(in context: Context, completion: @escaping (RandomPostEntry) -> ()) {
|
func getSnapshot(in context: Context, completion: @escaping (RandomPostEntry) -> ()) {
|
||||||
let prefs = UserDefaults(suiteName: "group.solsynth.solian")
|
let prefs = UserDefaults(suiteName: "group.solsynth.solian")
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ struct RandomPostProvider: TimelineProvider {
|
|||||||
)
|
)
|
||||||
completion(entry)
|
completion(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
|
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
|
||||||
getSnapshot(in: context) { (entry) in
|
getSnapshot(in: context) { (entry) in
|
||||||
let timeline = Timeline(entries: [entry], policy: .atEnd)
|
let timeline = Timeline(entries: [entry], policy: .atEnd)
|
||||||
@@ -64,7 +64,7 @@ struct RandomPostEntry: TimelineEntry {
|
|||||||
|
|
||||||
struct RandomPostWidgetEntryView : View {
|
struct RandomPostWidgetEntryView : View {
|
||||||
var entry: RandomPostProvider.Entry
|
var entry: RandomPostProvider.Entry
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
if let randomPost = entry.randomPost {
|
if let randomPost = entry.randomPost {
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +163,7 @@ struct RandomPostWidgetEntryView : View {
|
|||||||
|
|
||||||
struct RandomPostWidget: Widget {
|
struct RandomPostWidget: Widget {
|
||||||
let kind: String = "SolarRandomPostWidget"
|
let kind: String = "SolarRandomPostWidget"
|
||||||
|
|
||||||
var body: some WidgetConfiguration {
|
var body: some WidgetConfiguration {
|
||||||
StaticConfiguration(kind: kind, provider: RandomPostProvider()) { entry in
|
StaticConfiguration(kind: kind, provider: RandomPostProvider()) { entry in
|
||||||
if #available(iOS 17.0, *) {
|
if #available(iOS 17.0, *) {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@@ -152,6 +153,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
final TextEditingController contentController = TextEditingController();
|
final TextEditingController contentController = TextEditingController();
|
||||||
final TextEditingController titleController = TextEditingController();
|
final TextEditingController titleController = TextEditingController();
|
||||||
final TextEditingController descriptionController = TextEditingController();
|
final TextEditingController descriptionController = TextEditingController();
|
||||||
|
final TextEditingController aliasController = TextEditingController();
|
||||||
|
|
||||||
PostWriteController() {
|
PostWriteController() {
|
||||||
titleController.addListener(() => notifyListeners());
|
titleController.addListener(() => notifyListeners());
|
||||||
@@ -198,6 +200,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
titleController.text = post.body['title'] ?? '';
|
titleController.text = post.body['title'] ?? '';
|
||||||
descriptionController.text = post.body['description'] ?? '';
|
descriptionController.text = post.body['description'] ?? '';
|
||||||
contentController.text = post.body['content'] ?? '';
|
contentController.text = post.body['content'] ?? '';
|
||||||
|
aliasController.text = post.alias ?? '';
|
||||||
publishedAt = post.publishedAt;
|
publishedAt = post.publishedAt;
|
||||||
publishedUntil = post.publishedUntil;
|
publishedUntil = post.publishedUntil;
|
||||||
visibleUsers = List.from(post.visibleUsersList ?? []);
|
visibleUsers = List.from(post.visibleUsersList ?? []);
|
||||||
@@ -269,7 +272,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> post(BuildContext context) async {
|
Future<void> sendPost(BuildContext context) async {
|
||||||
if (isBusy || publisher == null) return;
|
if (isBusy || publisher == null) return;
|
||||||
|
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
@@ -305,12 +308,14 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
place.$2,
|
place.$2,
|
||||||
onProgress: (progress) {
|
onProgress: (progress) {
|
||||||
// Calculate overall progress for attachments
|
// Calculate overall progress for attachments
|
||||||
progress = ((i + progress) / attachments.length) * kAttachmentProgressWeight;
|
progress = math.max(((i + progress) / attachments.length) * kAttachmentProgressWeight, progress);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
progress = (i + 1) / attachments.length * kAttachmentProgressWeight;
|
||||||
attachments[i] = PostWriteMedia(item);
|
attachments[i] = PostWriteMedia(item);
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
isBusy = false;
|
isBusy = false;
|
||||||
@@ -334,6 +339,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
data: {
|
data: {
|
||||||
'publisher': publisher!.id,
|
'publisher': publisher!.id,
|
||||||
'content': contentController.text,
|
'content': contentController.text,
|
||||||
|
if (aliasController.text.isNotEmpty) 'alias': aliasController.text,
|
||||||
if (titleController.text.isNotEmpty) 'title': titleController.text,
|
if (titleController.text.isNotEmpty) 'title': titleController.text,
|
||||||
if (descriptionController.text.isNotEmpty) 'description': descriptionController.text,
|
if (descriptionController.text.isNotEmpty) 'description': descriptionController.text,
|
||||||
if (thumbnail != null && thumbnail!.attachment != null) 'thumbnail': thumbnail!.attachment!.rid,
|
if (thumbnail != null && thumbnail!.attachment != null) 'thumbnail': thumbnail!.attachment!.rid,
|
||||||
|
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -41,8 +41,7 @@ class SnAttachmentProvider {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SnAttachment?>> getMultiple(List<String> rids,
|
Future<List<SnAttachment?>> getMultiple(List<String> rids, {noCache = false}) async {
|
||||||
{noCache = false}) async {
|
|
||||||
final result = List<SnAttachment?>.filled(rids.length, null);
|
final result = List<SnAttachment?>.filled(rids.length, null);
|
||||||
final Map<String, int> randomMapping = {};
|
final Map<String, int> randomMapping = {};
|
||||||
for (int i = 0; i < rids.length; i++) {
|
for (int i = 0; i < rids.length; i++) {
|
||||||
@@ -63,9 +62,7 @@ class SnAttachmentProvider {
|
|||||||
'id': pendingFetch.join(','),
|
'id': pendingFetch.join(','),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final out = resp.data['data']
|
final out = resp.data['data'].map((e) => e['id'] == 0 ? null : SnAttachment.fromJson(e)).toList();
|
||||||
.map((e) => e['id'] == 0 ? null : SnAttachment.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
for (final item in out) {
|
for (final item in out) {
|
||||||
if (item == null) continue;
|
if (item == null) continue;
|
||||||
@@ -79,10 +76,7 @@ class SnAttachmentProvider {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<String, String> mimetypeOverrides = {
|
static Map<String, String> mimetypeOverrides = {'mov': 'video/quicktime', 'mp4': 'video/mp4'};
|
||||||
'mov': 'video/quicktime',
|
|
||||||
'mp4': 'video/mp4'
|
|
||||||
};
|
|
||||||
|
|
||||||
Future<SnAttachment> directUploadOne(
|
Future<SnAttachment> directUploadOne(
|
||||||
Uint8List data,
|
Uint8List data,
|
||||||
@@ -93,11 +87,8 @@ class SnAttachmentProvider {
|
|||||||
Function(double progress)? onProgress,
|
Function(double progress)? onProgress,
|
||||||
}) async {
|
}) async {
|
||||||
final filePayload = MultipartFile.fromBytes(data, filename: filename);
|
final filePayload = MultipartFile.fromBytes(data, filename: filename);
|
||||||
final fileAlt = filename.contains('.')
|
final fileAlt = filename.contains('.') ? filename.substring(0, filename.lastIndexOf('.')) : filename;
|
||||||
? filename.substring(0, filename.lastIndexOf('.'))
|
final fileExt = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
||||||
: filename;
|
|
||||||
final fileExt =
|
|
||||||
filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
|
||||||
|
|
||||||
String? mimetypeOverride;
|
String? mimetypeOverride;
|
||||||
if (mimetype != null) {
|
if (mimetype != null) {
|
||||||
@@ -133,11 +124,8 @@ class SnAttachmentProvider {
|
|||||||
Map<String, dynamic>? metadata, {
|
Map<String, dynamic>? metadata, {
|
||||||
String? mimetype,
|
String? mimetype,
|
||||||
}) async {
|
}) async {
|
||||||
final fileAlt = filename.contains('.')
|
final fileAlt = filename.contains('.') ? filename.substring(0, filename.lastIndexOf('.')) : filename;
|
||||||
? filename.substring(0, filename.lastIndexOf('.'))
|
final fileExt = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
||||||
: filename;
|
|
||||||
final fileExt =
|
|
||||||
filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
|
||||||
|
|
||||||
String? mimetypeOverride;
|
String? mimetypeOverride;
|
||||||
if (mimetype == null && mimetypeOverrides.keys.contains(fileExt)) {
|
if (mimetype == null && mimetypeOverrides.keys.contains(fileExt)) {
|
||||||
@@ -155,10 +143,7 @@ class SnAttachmentProvider {
|
|||||||
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
|
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (SnAttachment.fromJson(resp.data['meta']), resp.data['chunk_size'] as int);
|
||||||
SnAttachment.fromJson(resp.data['meta']),
|
|
||||||
resp.data['chunk_size'] as int
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SnAttachment> _chunkedUploadOnePart(
|
Future<SnAttachment> _chunkedUploadOnePart(
|
||||||
@@ -200,24 +185,17 @@ class SnAttachmentProvider {
|
|||||||
(entry.value + 1) * chunkSize,
|
(entry.value + 1) * chunkSize,
|
||||||
await file.length(),
|
await file.length(),
|
||||||
);
|
);
|
||||||
final data = Uint8List.fromList(await file
|
final data = Uint8List.fromList(await file.openRead(beginCursor, endCursor).expand((chunk) => chunk).toList());
|
||||||
.openRead(beginCursor, endCursor)
|
|
||||||
.expand((chunk) => chunk)
|
|
||||||
.toList());
|
|
||||||
|
|
||||||
place = await _chunkedUploadOnePart(
|
place = await _chunkedUploadOnePart(
|
||||||
data,
|
data,
|
||||||
place.rid,
|
place.rid,
|
||||||
entry.key,
|
entry.key,
|
||||||
onProgress: (chunkProgress) {
|
|
||||||
final overallProgress =
|
|
||||||
(currentTask + chunkProgress) / chunks.length;
|
|
||||||
if (onProgress != null) {
|
|
||||||
onProgress(overallProgress);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final overallProgress = currentTask / chunks.length;
|
||||||
|
onProgress?.call(overallProgress);
|
||||||
|
|
||||||
currentTask++;
|
currentTask++;
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
|
@@ -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_launcher',
|
||||||
|
'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(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@@ -13,6 +13,7 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
import 'package:pasteboard/pasteboard.dart';
|
import 'package:pasteboard/pasteboard.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/controllers/post_write_controller.dart';
|
import 'package:surface/controllers/post_write_controller.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
@@ -71,11 +72,14 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final config = context.read<ConfigProvider>();
|
||||||
final resp = await sn.client.get('/cgi/co/publishers/me');
|
final resp = await sn.client.get('/cgi/co/publishers/me');
|
||||||
_publishers = List<SnPublisher>.from(
|
_publishers = List<SnPublisher>.from(
|
||||||
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
|
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
|
||||||
);
|
);
|
||||||
_writeController.setPublisher(_publishers?.firstOrNull);
|
final beforeId = config.prefs.getInt('int_last_publisher_id');
|
||||||
|
_writeController
|
||||||
|
.setPublisher(_publishers?.where((ele) => ele.id == beforeId).firstOrNull ?? _publishers?.firstOrNull);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@@ -265,6 +269,8 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
_writeController.setPublisher(value);
|
_writeController.setPublisher(value);
|
||||||
|
final config = context.read<ConfigProvider>();
|
||||||
|
config.prefs.setInt('int_last_publisher_id', value.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
buttonStyleData: const ButtonStyleData(
|
buttonStyleData: const ButtonStyleData(
|
||||||
@@ -496,7 +502,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
onPressed: (_writeController.isBusy || _writeController.publisher == null)
|
onPressed: (_writeController.isBusy || _writeController.publisher == null)
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
_writeController.post(context).then((_) {
|
_writeController.sendPost(context).then((_) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
});
|
});
|
||||||
|
@@ -17,6 +17,8 @@ import 'package:responsive_framework/responsive_framework.dart';
|
|||||||
import 'package:screenshot/screenshot.dart';
|
import 'package:screenshot/screenshot.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
|
import 'package:surface/providers/link_preview.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';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
@@ -83,6 +85,8 @@ class PostItem extends StatelessWidget {
|
|||||||
child: MultiProvider(
|
child: MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
Provider<SnNetworkProvider>(create: (_) => context.read()),
|
Provider<SnNetworkProvider>(create: (_) => context.read()),
|
||||||
|
Provider<SnLinkPreviewProvider>(create: (_) => context.read()),
|
||||||
|
ChangeNotifierProvider<ConfigProvider>(create: (_) => context.read()),
|
||||||
],
|
],
|
||||||
child: ResponsiveBreakpoints.builder(
|
child: ResponsiveBreakpoints.builder(
|
||||||
breakpoints: ResponsiveBreakpoints.of(context).breakpoints,
|
breakpoints: ResponsiveBreakpoints.of(context).breakpoints,
|
||||||
@@ -410,7 +414,7 @@ class PostShareImageWidget extends StatelessWidget {
|
|||||||
size: Size(28, 28),
|
size: Size(28, 28),
|
||||||
),
|
),
|
||||||
eyeStyle: QrEyeStyle(
|
eyeStyle: QrEyeStyle(
|
||||||
eyeShape: QrEyeShape.square,
|
eyeShape: QrEyeShape.circle,
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
dataModuleStyle: QrDataModuleStyle(
|
dataModuleStyle: QrDataModuleStyle(
|
||||||
|
@@ -189,16 +189,19 @@ class PostMediaPendingList extends StatelessWidget {
|
|||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: switch (thumbnail!.type) {
|
child: switch (thumbnail!.type) {
|
||||||
PostWriteMediaType.image => LayoutBuilder(builder: (context, constraints) {
|
PostWriteMediaType.image => Container(
|
||||||
return Image(
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
image: thumbnail!.getImageProvider(
|
child: LayoutBuilder(builder: (context, constraints) {
|
||||||
context,
|
return Image(
|
||||||
width: (constraints.maxWidth * devicePixelRatio).round(),
|
image: thumbnail!.getImageProvider(
|
||||||
height: (constraints.maxHeight * devicePixelRatio).round(),
|
context,
|
||||||
)!,
|
width: (constraints.maxWidth * devicePixelRatio).round(),
|
||||||
fit: BoxFit.cover,
|
height: (constraints.maxHeight * devicePixelRatio).round(),
|
||||||
);
|
)!,
|
||||||
}),
|
fit: BoxFit.contain,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
_ => Container(
|
_ => Container(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
child: const Icon(Symbols.docs).center(),
|
child: const Icon(Symbols.docs).center(),
|
||||||
@@ -236,18 +239,21 @@ class PostMediaPendingList extends StatelessWidget {
|
|||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: switch (media.type) {
|
child: switch (media.type) {
|
||||||
PostWriteMediaType.image => LayoutBuilder(builder: (context, constraints) {
|
PostWriteMediaType.image => Container(
|
||||||
return Image(
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
image: media.getImageProvider(
|
child: LayoutBuilder(builder: (context, constraints) {
|
||||||
context,
|
return Image(
|
||||||
width: (constraints.maxWidth * devicePixelRatio).round(),
|
image: media.getImageProvider(
|
||||||
height: (constraints.maxHeight * devicePixelRatio).round(),
|
context,
|
||||||
)!,
|
width: (constraints.maxWidth * devicePixelRatio).round(),
|
||||||
fit: BoxFit.cover,
|
height: (constraints.maxHeight * devicePixelRatio).round(),
|
||||||
);
|
)!,
|
||||||
}),
|
fit: BoxFit.contain,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
_ => Container(
|
_ => Container(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
child: const Icon(Symbols.docs).center(),
|
child: const Icon(Symbols.docs).center(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@@ -114,6 +114,18 @@ class PostMetaEditor extends StatelessWidget {
|
|||||||
controller.setTags(value);
|
controller.setTags(value);
|
||||||
},
|
},
|
||||||
).padding(horizontal: 24),
|
).padding(horizontal: 24),
|
||||||
|
const Gap(4),
|
||||||
|
TextField(
|
||||||
|
controller: controller.aliasController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'fieldPostAlias'.tr(),
|
||||||
|
helperText: 'fieldPostAliasHint'.tr(),
|
||||||
|
helperMaxLines: 2,
|
||||||
|
border: UnderlineInputBorder(),
|
||||||
|
),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
).padding(horizontal: 24),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
@@ -7,6 +7,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:surface/controllers/post_write_controller.dart';
|
import 'package:surface/controllers/post_write_controller.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
@@ -16,6 +17,7 @@ import 'package:surface/widgets/loading_indicator.dart';
|
|||||||
class PostMiniEditor extends StatefulWidget {
|
class PostMiniEditor extends StatefulWidget {
|
||||||
final int? postReplyId;
|
final int? postReplyId;
|
||||||
final Function? onPost;
|
final Function? onPost;
|
||||||
|
|
||||||
const PostMiniEditor({super.key, this.postReplyId, this.onPost});
|
const PostMiniEditor({super.key, this.postReplyId, this.onPost});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -26,6 +28,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
final PostWriteController _writeController = PostWriteController();
|
final PostWriteController _writeController = PostWriteController();
|
||||||
|
|
||||||
bool _isFetching = false;
|
bool _isFetching = false;
|
||||||
|
|
||||||
bool get _isLoading => _isFetching || _writeController.isLoading;
|
bool get _isLoading => _isFetching || _writeController.isLoading;
|
||||||
|
|
||||||
List<SnPublisher>? _publishers;
|
List<SnPublisher>? _publishers;
|
||||||
@@ -35,11 +38,14 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final config = context.read<ConfigProvider>();
|
||||||
final resp = await sn.client.get('/cgi/co/publishers/me');
|
final resp = await sn.client.get('/cgi/co/publishers/me');
|
||||||
_publishers = List<SnPublisher>.from(
|
_publishers = List<SnPublisher>.from(
|
||||||
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
|
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
|
||||||
);
|
);
|
||||||
_writeController.setPublisher(_publishers?.firstOrNull);
|
final beforeId = config.prefs.getInt('int_last_publisher_id');
|
||||||
|
_writeController
|
||||||
|
.setPublisher(_publishers?.where((ele) => ele.id == beforeId).firstOrNull ?? _publishers?.firstOrNull);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@@ -93,17 +99,11 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment:
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
Text(item.nick).textStyle(
|
Text(item.nick).textStyle(Theme.of(context).textTheme.bodyMedium!),
|
||||||
Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium!),
|
|
||||||
Text('@${item.name}')
|
Text('@${item.name}')
|
||||||
.textStyle(Theme.of(context)
|
.textStyle(Theme.of(context).textTheme.bodySmall!)
|
||||||
.textTheme
|
|
||||||
.bodySmall!)
|
|
||||||
.fontSize(12),
|
.fontSize(12),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -120,8 +120,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 16,
|
radius: 16,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor:
|
foregroundColor: Theme.of(context).colorScheme.onSurface,
|
||||||
Theme.of(context).colorScheme.onSurface,
|
|
||||||
child: const Icon(Symbols.add),
|
child: const Icon(Symbols.add),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
@@ -130,8 +129,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('publishersNew').tr().textStyle(
|
Text('publishersNew').tr().textStyle(Theme.of(context).textTheme.bodyMedium!),
|
||||||
Theme.of(context).textTheme.bodyMedium!),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -142,9 +140,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
value: _writeController.publisher,
|
value: _writeController.publisher,
|
||||||
onChanged: (SnPublisher? value) {
|
onChanged: (SnPublisher? value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
GoRouter.of(context)
|
GoRouter.of(context).pushNamed('accountPublisherNew').then((value) {
|
||||||
.pushNamed('accountPublisherNew')
|
|
||||||
.then((value) {
|
|
||||||
if (value == true) {
|
if (value == true) {
|
||||||
_publishers = null;
|
_publishers = null;
|
||||||
_fetchPublishers();
|
_fetchPublishers();
|
||||||
@@ -152,6 +148,8 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
_writeController.setPublisher(value);
|
_writeController.setPublisher(value);
|
||||||
|
final config = context.read<ConfigProvider>();
|
||||||
|
config.prefs.setInt('int_last_publisher_id', value.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
buttonStyleData: const ButtonStyleData(
|
buttonStyleData: const ButtonStyleData(
|
||||||
@@ -178,8 +176,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
),
|
),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
onTapOutside: (_) =>
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
@@ -188,8 +185,7 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
TweenAnimationBuilder<double>(
|
TweenAnimationBuilder<double>(
|
||||||
tween: Tween(begin: 0, end: _writeController.progress),
|
tween: Tween(begin: 0, end: _writeController.progress),
|
||||||
duration: Duration(milliseconds: 300),
|
duration: Duration(milliseconds: 300),
|
||||||
builder: (context, value, _) =>
|
builder: (context, value, _) => LinearProgressIndicator(value: value, minHeight: 2),
|
||||||
LinearProgressIndicator(value: value, minHeight: 2),
|
|
||||||
)
|
)
|
||||||
else if (_writeController.isBusy)
|
else if (_writeController.isBusy)
|
||||||
const LinearProgressIndicator(value: null, minHeight: 2),
|
const LinearProgressIndicator(value: null, minHeight: 2),
|
||||||
@@ -206,18 +202,16 @@ class _PostMiniEditorState extends State<PostMiniEditor> {
|
|||||||
'postEditor',
|
'postEditor',
|
||||||
pathParameters: {'mode': 'stories'},
|
pathParameters: {'mode': 'stories'},
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
if (widget.postReplyId != null)
|
if (widget.postReplyId != null) 'replying': widget.postReplyId.toString(),
|
||||||
'replying': widget.postReplyId.toString(),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
onPressed: (_writeController.isBusy ||
|
onPressed: (_writeController.isBusy || _writeController.publisher == null)
|
||||||
_writeController.publisher == null)
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
_writeController.post(context).then((_) {
|
_writeController.sendPost(context).then((_) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
if (widget.onPost != null) widget.onPost!();
|
if (widget.onPost != null) widget.onPost!();
|
||||||
context.showSnackbar('postPosted'.tr());
|
context.showSnackbar('postPosted'.tr());
|
||||||
|
@@ -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"))
|
||||||
|
@@ -132,6 +132,8 @@ PODS:
|
|||||||
- GoogleUtilities/UserDefaults (8.0.2):
|
- GoogleUtilities/UserDefaults (8.0.2):
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
|
- in_app_review (2.0.0):
|
||||||
|
- FlutterMacOS
|
||||||
- livekit_client (2.3.2):
|
- livekit_client (2.3.2):
|
||||||
- flutter_webrtc
|
- flutter_webrtc
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@@ -186,6 +188,7 @@ DEPENDENCIES:
|
|||||||
- flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`)
|
- flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- gal (from `Flutter/ephemeral/.symlinks/plugins/gal/darwin`)
|
- 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`)
|
- 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_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`)
|
- 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
|
:path: Flutter/ephemeral
|
||||||
gal:
|
gal:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/gal/darwin
|
:path: Flutter/ephemeral/.symlinks/plugins/gal/darwin
|
||||||
|
in_app_review:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/in_app_review/macos
|
||||||
livekit_client:
|
livekit_client:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos
|
||||||
media_kit_libs_macos_video:
|
media_kit_libs_macos_video:
|
||||||
@@ -286,13 +291,14 @@ SPEC CHECKSUMS:
|
|||||||
FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
|
FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
|
||||||
FirebaseInstallations: 6ef4a1c7eb2a61ee1f74727d7f6ce2e72acf1414
|
FirebaseInstallations: 6ef4a1c7eb2a61ee1f74727d7f6ce2e72acf1414
|
||||||
FirebaseMessaging: f8a160d99c2c2e5babbbcc90c4a3e15db036aee2
|
FirebaseMessaging: f8a160d99c2c2e5babbbcc90c4a3e15db036aee2
|
||||||
flutter_udid: 6b2b89780c3dfeecf0047bdf93f622d6416b1c07
|
flutter_udid: 2e7b3da4b5fdfba86a396b97898f5fe8f4ec1a52
|
||||||
flutter_webrtc: 53c9e1285ab32dfb58afb1e1471416a877e23d7a
|
flutter_webrtc: 53c9e1285ab32dfb58afb1e1471416a877e23d7a
|
||||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
||||||
GoogleAppMeasurement: 987769c4ca6b968f2479fbcc9fe3ce34af454b8e
|
GoogleAppMeasurement: 987769c4ca6b968f2479fbcc9fe3ce34af454b8e
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||||
|
in_app_review: a6a031b9acd03c7d103e341aa334adf2c493fb93
|
||||||
livekit_client: 9fdcb22df3de55e6d4b24bdc3b5eb1c0269d774a
|
livekit_client: 9fdcb22df3de55e6d4b24bdc3b5eb1c0269d774a
|
||||||
media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82
|
media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82
|
||||||
media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5
|
media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5
|
||||||
|
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+37
|
||||||
|
|
||||||
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
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"applinks": {
|
||||||
|
"apps": [],
|
||||||
|
"details": [
|
||||||
|
{
|
||||||
|
"appIDs": [
|
||||||
|
"W7HPZ53V6B.dev.solsynth.solian"
|
||||||
|
],
|
||||||
|
"paths": [
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"/": "/*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"webcredentials": {
|
||||||
|
"apps": [
|
||||||
|
"W7HPZ53V6B.dev.solsynth.solian"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|