Skip to content

Commit ec9cbf7

Browse files
committed
Update
1 parent 39cb39a commit ec9cbf7

31 files changed

+1489
-285
lines changed

app/build.gradle.kts

+14-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ android {
1515
versionCode = 1
1616
versionName = "1.0"
1717
multiDexEnabled = true
18+
buildConfigField("String", "SERVER_URL", "\"https://privateuploader.com\"")
19+
buildConfigField("String", "BUILD_TIME", "\"${System.currentTimeMillis()}\"")
1820

1921
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2022
vectorDrawables {
@@ -25,11 +27,16 @@ android {
2527
buildTypes {
2628
release {
2729
isMinifyEnabled = false
30+
buildConfigField("String", "SERVER_URL", "\"https://privateuploader.com\"")
2831
proguardFiles(
2932
getDefaultProguardFile("proguard-android-optimize.txt"),
3033
"proguard-rules.pro"
3134
)
3235
}
36+
37+
debug {
38+
buildConfigField("String", "SERVER_URL", "\"http://192.168.0.12:34582\"")
39+
}
3340
}
3441
compileOptions {
3542
sourceCompatibility = JavaVersion.VERSION_1_8
@@ -40,6 +47,7 @@ android {
4047
}
4148
buildFeatures {
4249
compose = true
50+
buildConfig = true
4351
}
4452
composeOptions {
4553
kotlinCompilerExtensionVersion = "1.4.3"
@@ -53,14 +61,17 @@ android {
5361

5462
dependencies {
5563
// TPU
64+
implementation("io.coil-kt:coil-gif:2.4.0")
65+
implementation("io.coil-kt:coil-compose:2.4.0")
66+
implementation("com.github.X1nto:OverlappingPanelsCompose:1.2.0")
5667
implementation("io.coil-kt:coil:2.3.0")
5768
implementation("com.github.jeziellago:compose-markdown:0.3.3")
5869
implementation("com.github.bumptech.glide:compose:1.0.0-alpha.1")
5970
implementation("com.squareup.retrofit2:retrofit:2.9.0")
6071
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
6172
implementation("com.squareup.moshi:moshi:1.12.0")
6273
implementation("com.squareup.moshi:moshi-kotlin:1.12.0")
63-
implementation("com.github.bumptech.glide:glide:4.15.1")
74+
implementation("com.github.bumptech.glide:glide:4.16.0-SNAPSHOT")
6475
implementation("com.squareup.okhttp3:logging-interceptor:4.9.1")
6576
implementation("androidx.core:core-ktx:1.10.1")
6677
implementation("com.google.code.gson:gson:2.10.1")
@@ -74,6 +85,8 @@ dependencies {
7485

7586
// Material Design 3
7687
implementation("androidx.compose.material3:material3")
88+
// For SwipeableState
89+
implementation("androidx.compose.material:material:1.4.3")
7790
// or only import the main APIs for the underlying toolkit systems,
7891
// such as input and measurement/layout
7992
implementation("androidx.compose.ui:ui")
Binary file not shown.

app/release/output-metadata.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"version": 3,
3+
"artifactType": {
4+
"type": "APK",
5+
"kind": "Directory"
6+
},
7+
"applicationId": "com.troplo.privateuploader",
8+
"variantName": "release",
9+
"elements": [
10+
{
11+
"type": "SINGLE",
12+
"filters": [],
13+
"attributes": [],
14+
"versionCode": 1,
15+
"versionName": "1.0",
16+
"outputFile": "app-release.apk"
17+
}
18+
],
19+
"elementType": "File"
20+
}

app/src/main/java/com/troplo/privateuploader/MainActivity.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import java.util.Collections
1818

1919
class MainActivity : ComponentActivity() {
2020
override fun onCreate(savedInstanceState: Bundle?) {
21-
val token = SessionManager(this).fetchAuthToken()
21+
val token = SessionManager(this).getAuthToken()
2222
var user: User? = null
2323

2424
if(token != null) {
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,124 @@
11
package com.troplo.privateuploader
22

3+
import androidx.compose.foundation.layout.Column
4+
import androidx.compose.foundation.layout.Spacer
5+
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.foundation.layout.height
37
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.foundation.rememberScrollState
9+
import androidx.compose.foundation.verticalScroll
10+
import androidx.compose.material.ExperimentalMaterialApi
411
import androidx.compose.material3.ExperimentalMaterial3Api
12+
import androidx.compose.material3.ModalDrawerSheet
513
import androidx.compose.material3.Scaffold
614
import androidx.compose.runtime.Composable
15+
import androidx.compose.runtime.LaunchedEffect
16+
import androidx.compose.runtime.getValue
17+
import androidx.compose.runtime.mutableStateOf
18+
import androidx.compose.runtime.remember
19+
import androidx.compose.runtime.setValue
720
import androidx.compose.ui.Modifier
821
import androidx.compose.ui.platform.LocalContext
22+
import androidx.compose.ui.unit.dp
923
import androidx.navigation.compose.rememberNavController
24+
import com.troplo.privateuploader.api.ChatStore
1025
import com.troplo.privateuploader.api.SessionManager
1126
import com.troplo.privateuploader.api.SocketHandler
1227
import com.troplo.privateuploader.api.UserHandler
28+
import com.troplo.privateuploader.components.chat.MemberSidebar
1329
import com.troplo.privateuploader.components.core.BottomBarNav
1430
import com.troplo.privateuploader.components.core.NavGraph
31+
import com.troplo.privateuploader.components.core.NavRoute
32+
import com.troplo.privateuploader.components.core.OverlappingPanels
33+
import com.troplo.privateuploader.components.core.PanelSurface
1534
import com.troplo.privateuploader.components.core.TopBarNav
35+
import com.troplo.privateuploader.components.core.rememberOverlappingPanelsState
1636
import com.troplo.privateuploader.data.model.User
17-
import io.socket.client.IO
18-
import io.socket.engineio.client.Socket
19-
import kotlinx.coroutines.Dispatchers
20-
import java.util.Collections
37+
import com.troplo.privateuploader.screens.HomeScreen
2138

22-
@OptIn(ExperimentalMaterial3Api::class)
39+
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
2340
@Composable
2441
fun MainScreen(user: User?) {
2542
val context = LocalContext.current
26-
val token = SessionManager(context).fetchAuthToken()
43+
val token = SessionManager(context).getAuthToken()
2744
if (token != null) {
2845
SocketHandler.initializeSocket(token, context)
2946
UserHandler.initializeUser(token)
3047
}
3148
val navController = rememberNavController()
49+
val panelState = rememberOverlappingPanelsState()
50+
var closePanels by remember { mutableStateOf(false) }
51+
val closePanelsFunc = {
52+
closePanels = true
53+
}
54+
if (closePanels) {
55+
LaunchedEffect(Unit) {
56+
panelState.closePanels()
57+
closePanels = false
58+
}
59+
}
3260
Scaffold(
3361
topBar = { TopBarNav(navController = navController, user = user) },
34-
bottomBar = { BottomBarNav(navController = navController) }
62+
bottomBar = { BottomBarNav(navController = navController, panelState = panelState, closePanels = closePanelsFunc) },
3563
) { paddingValues ->
36-
NavGraph(
37-
modifier = Modifier.padding(
38-
top = paddingValues.calculateTopPadding(),
39-
bottom = paddingValues.calculateBottomPadding()),
40-
navController = navController,
41-
user = user
64+
OverlappingPanels(
65+
modifier = Modifier.fillMaxSize(),
66+
panelsState = panelState,
67+
gesturesEnabled = true,
68+
panelStart = {
69+
PanelSurface {
70+
ModalDrawerSheet(
71+
modifier = Modifier.padding(
72+
top = paddingValues.calculateTopPadding(),
73+
bottom = paddingValues.calculateBottomPadding())
74+
) {
75+
Spacer(Modifier.height(12.dp))
76+
HomeScreen(
77+
openChat = {
78+
chatId ->
79+
ChatStore.setAssociationId(chatId, context)
80+
navController.navigate("${NavRoute.Chat.path}/$chatId")
81+
closePanels = true
82+
},
83+
panelState = panelState
84+
)
85+
}
86+
}
87+
},
88+
panelCenter = {
89+
PanelSurface {
90+
NavGraph(
91+
modifier = Modifier.padding(
92+
top = paddingValues.calculateTopPadding(),
93+
bottom = paddingValues.calculateBottomPadding()
94+
),
95+
navController = navController,
96+
user = user,
97+
context,
98+
panelsState = panelState
99+
)
100+
}
101+
},
102+
panelEnd = {
103+
PanelSurface {
104+
ModalDrawerSheet(
105+
modifier = Modifier.padding(
106+
top = paddingValues.calculateTopPadding(),
107+
bottom = paddingValues.calculateBottomPadding()
108+
)
109+
) {
110+
Column(
111+
modifier = Modifier
112+
.verticalScroll(rememberScrollState())
113+
.weight(weight = 1f, fill = false)
114+
115+
) {
116+
Spacer(Modifier.height(12.dp))
117+
MemberSidebar()
118+
}
119+
}
120+
}
121+
}
42122
)
43123
}
44124
}

app/src/main/java/com/troplo/privateuploader/api/ApiService.kt

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package com.troplo.privateuploader.api
22

3+
import com.troplo.privateuploader.BuildConfig
34
import com.troplo.privateuploader.data.model.Chat
45
import com.troplo.privateuploader.data.model.Gallery
56
import com.troplo.privateuploader.data.model.LoginRequest
67
import com.troplo.privateuploader.data.model.LoginResponse
78
import com.troplo.privateuploader.data.model.Message
89
import com.troplo.privateuploader.data.model.MessageRequest
10+
import com.troplo.privateuploader.data.model.StarResponse
911
import com.troplo.privateuploader.data.model.User
1012
import okhttp3.OkHttpClient
1113
import okhttp3.logging.HttpLoggingInterceptor
@@ -15,11 +17,12 @@ import retrofit2.converter.gson.GsonConverterFactory
1517
import retrofit2.http.Body
1618
import retrofit2.http.GET
1719
import retrofit2.http.Header
20+
import retrofit2.http.PATCH
1821
import retrofit2.http.POST
1922
import retrofit2.http.Path
2023
import retrofit2.http.Query
2124

22-
private const val BASE_URL = "http://192.168.0.12:34582/api/v3/"
25+
private const val BASE_URL = "${BuildConfig.SERVER_URL}/api/v3/"
2326
val client = OkHttpClient.Builder()
2427
.addInterceptor(HttpLoggingInterceptor().apply {
2528
level = HttpLoggingInterceptor.Level.BODY
@@ -71,6 +74,12 @@ interface TpuApiService {
7174
@Path("id") id: Int,
7275
@Body messageRequest: MessageRequest
7376
): Call<Message>
77+
78+
@POST("gallery/star/{attachment}")
79+
fun star(
80+
@Header("Authorization") token: String,
81+
@Path("attachment") attachment: String
82+
): Call<StarResponse>
7483
}
7584

7685
object TpuApi {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.troplo.privateuploader.api
2+
3+
import android.content.Context
4+
import android.os.Build.VERSION.SDK_INT
5+
import coil.ImageLoader
6+
import coil.decode.GifDecoder
7+
import coil.decode.ImageDecoderDecoder
8+
9+
fun imageLoader(context: Context): ImageLoader {
10+
return ImageLoader.Builder(context)
11+
.components {
12+
if (SDK_INT >= 28) {
13+
add(ImageDecoderDecoder.Factory())
14+
} else {
15+
add(GifDecoder.Factory())
16+
}
17+
}
18+
.build()
19+
}

app/src/main/java/com/troplo/privateuploader/api/SessionManager.kt

+11-1
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,23 @@ class SessionManager (context: Context) {
1717
editor.apply()
1818
}
1919

20-
fun fetchAuthToken(): String? {
20+
fun getAuthToken(): String? {
2121
return prefs.getString(USER_TOKEN, null)
2222
}
2323

24+
fun getLastChatId(): Int {
25+
return prefs.getInt("lastChatId", 0)
26+
}
27+
2428
fun setFolder(folder: String) {
2529
val editor = prefs.edit()
2630
editor.putString("folder", folder)
2731
editor.apply()
2832
}
33+
34+
fun setLastChatId(id: Int) {
35+
val editor = prefs.edit()
36+
editor.putInt("lastChatId", id)
37+
editor.apply()
38+
}
2939
}

app/src/main/java/com/troplo/privateuploader/api/SocketHandler.kt

+21-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import android.app.NotificationManager
55
import android.app.PendingIntent
66
import android.content.Context
77
import android.content.Intent
8+
import android.content.pm.ApplicationInfo
89
import android.graphics.drawable.Icon
910
import androidx.compose.material.icons.Icons
1011
import androidx.compose.material.icons.filled.Reply
@@ -13,6 +14,7 @@ import androidx.core.app.NotificationManagerCompat
1314
import androidx.core.app.RemoteInput
1415
import androidx.core.content.ContextCompat.getSystemService
1516
import com.google.gson.Gson
17+
import com.troplo.privateuploader.BuildConfig
1618
import com.troplo.privateuploader.R
1719
import com.troplo.privateuploader.data.model.MessageEvent
1820
import io.socket.client.IO
@@ -21,9 +23,8 @@ import org.json.JSONObject
2123
import java.net.URISyntaxException
2224
import java.util.Collections
2325

24-
2526
object SocketHandler {
26-
private const val SERVER_URL = "http://192.168.0.12:34582" // Replace with your Socket.io server URL
27+
private const val SERVER_URL = BuildConfig.SERVER_URL
2728

2829
private var socket: Socket? = null
2930
private val gson = Gson()
@@ -34,6 +35,7 @@ object SocketHandler {
3435
options.forceNew = true
3536
options.reconnection = true
3637
options.auth = Collections.singletonMap("token", token)
38+
options.query = "platform=android_kotlin"
3739
socket = IO.socket(SERVER_URL, options)
3840
if(socket != null) {
3941
socket?.open()
@@ -46,6 +48,23 @@ object SocketHandler {
4648
socket?.on(Socket.EVENT_CONNECT_ERROR) {
4749
println("Socket connect error ${socket?.isActive}")
4850
}
51+
socket?.on("message") { it ->
52+
val jsonArray = it[0] as JSONObject
53+
val payload = jsonArray.toString()
54+
val messageEvent = gson.fromJson(payload, MessageEvent::class.java)
55+
56+
val message = messageEvent.message
57+
println("Message received (SocketHandler): $message")
58+
if(messageEvent.association.id != ChatStore.associationId.value) {
59+
// increase unread count
60+
val chat = ChatStore.chats.value.find { it.association?.id == messageEvent.association.id }
61+
println(chat)
62+
if(chat != null) {
63+
chat.unread = chat.unread?.plus(1)
64+
ChatStore.setChats(listOf(chat) + ChatStore.chats.value.filter { it.association?.id != messageEvent.association.id })
65+
}
66+
}
67+
}
4968
/*socket?.on("message") {
5069
val jsonArray = it[0] as JSONObject
5170
val payload = jsonArray.toString()

0 commit comments

Comments
 (0)