mirror of
https://github.com/Docile-Alligator/Infinity-For-Reddit.git
synced 2026-03-04 06:49:51 +00:00
Start implementing modmail using Kotlin + Compose.
This commit is contained in:
@ -1,5 +1,9 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'kotlin-parcelize'
|
||||
id 'kotlin-kapt'
|
||||
id 'com.google.devtools.ksp'
|
||||
}
|
||||
|
||||
android {
|
||||
@ -39,6 +43,18 @@ android {
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.14"
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '11'
|
||||
}
|
||||
|
||||
kapt {
|
||||
correctErrorTypes = true
|
||||
}
|
||||
|
||||
lint {
|
||||
disable 'MissingTranslation'
|
||||
}
|
||||
@ -52,6 +68,7 @@ android {
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
viewBinding = true
|
||||
compose = true
|
||||
}
|
||||
namespace 'ml.docilealligator.infinityforreddit'
|
||||
}
|
||||
@ -63,6 +80,7 @@ dependencies {
|
||||
implementation 'androidx.browser:browser:1.8.0'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.activity:activity:1.9.0'
|
||||
def lifecycleVersion = "2.7.0"
|
||||
@ -75,11 +93,13 @@ dependencies {
|
||||
def pagingVersion = '3.3.0'
|
||||
implementation "androidx.paging:paging-runtime:$pagingVersion"
|
||||
implementation "androidx.paging:paging-guava:$pagingVersion"
|
||||
implementation "androidx.paging:paging-compose:3.3.4"
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||
def roomVersion = "2.6.1"
|
||||
implementation "androidx.room:room-runtime:$roomVersion"
|
||||
annotationProcessor "androidx.room:room-compiler:$roomVersion"
|
||||
kapt "androidx.room:room-compiler:$roomVersion"
|
||||
implementation "androidx.room:room-guava:$roomVersion"
|
||||
implementation 'androidx.viewpager2:viewpager2:1.1.0'
|
||||
implementation 'androidx.work:work-runtime:2.9.0'
|
||||
@ -108,6 +128,7 @@ dependencies {
|
||||
def daggerVersion = '2.51.1'
|
||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
ksp "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
|
||||
// Binding
|
||||
compileOnly 'com.android.databinding:viewbinding:8.5.1'
|
||||
@ -116,6 +137,7 @@ dependencies {
|
||||
def eventbusVersion = "3.3.1"
|
||||
implementation "org.greenrobot:eventbus:$eventbusVersion"
|
||||
annotationProcessor "org.greenrobot:eventbus-annotation-processor:$eventbusVersion"
|
||||
kapt "org.greenrobot:eventbus-annotation-processor:$eventbusVersion"
|
||||
|
||||
// TransactionTooLargeException avoidance
|
||||
implementation 'com.github.livefront:bridge:v2.0.2'
|
||||
@ -125,6 +147,7 @@ dependencies {
|
||||
def stateVersion = "1.4.1"
|
||||
implementation "com.evernote:android-state:$stateVersion"
|
||||
annotationProcessor "com.evernote:android-state-processor:$stateVersion"
|
||||
kapt "com.evernote:android-state-processor:$stateVersion"
|
||||
|
||||
// Object to JSON
|
||||
// NOTE: Replace with Squareup's Moshi?
|
||||
@ -146,6 +169,7 @@ dependencies {
|
||||
def glideVersion = "4.16.0"
|
||||
implementation "com.github.bumptech.glide:glide:$glideVersion"
|
||||
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
|
||||
ksp "com.github.bumptech.glide:compiler:$glideVersion"
|
||||
implementation 'jp.wasabeef:glide-transformations:4.3.0'
|
||||
implementation 'com.github.santalu:aspect-ratio-imageview:1.0.9'
|
||||
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.29'
|
||||
@ -193,6 +217,32 @@ dependencies {
|
||||
|
||||
implementation 'com.giphy.sdk:ui:2.3.15'
|
||||
|
||||
// Compose
|
||||
def composeBom = platform('androidx.compose:compose-bom:2024.10.01')
|
||||
implementation composeBom
|
||||
androidTestImplementation composeBom
|
||||
|
||||
// Choose one of the following:
|
||||
// Material Design 3
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation("androidx.compose.material3.adaptive:adaptive")
|
||||
implementation("androidx.compose.material3.adaptive:adaptive-layout")
|
||||
implementation("androidx.compose.material3.adaptive:adaptive-navigation")
|
||||
|
||||
// Android Studio Preview support
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling'
|
||||
|
||||
// UI Tests
|
||||
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
|
||||
debugImplementation 'androidx.compose.ui:ui-test-manifest'
|
||||
|
||||
// Optional - Integration with activities
|
||||
implementation 'androidx.activity:activity-compose:1.9.3'
|
||||
// Optional - Integration with ViewModels
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7'
|
||||
// Optional - Integration with LiveData
|
||||
implementation 'androidx.compose.runtime:runtime-livedata'
|
||||
|
||||
/**** Builds and flavors ****/
|
||||
// debugImplementation because LeakCanary should only run in debug builds.
|
||||
|
||||
@ -36,13 +36,17 @@
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:replace="android:label">
|
||||
<activity
|
||||
android:name=".activities.LoginChromeCustomTabActivity"
|
||||
android:label="@string/login_activity_label"
|
||||
android:name=".activities.ModmailActivity"
|
||||
android:exported="false"
|
||||
android:parentActivityName=".activities.MainActivity"
|
||||
android:theme="@style/AppTheme.Slidable"
|
||||
android:theme="@style/AppTheme.Slidable" />
|
||||
<activity
|
||||
android:name=".activities.LoginChromeCustomTabActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/login_activity_label"
|
||||
android:launchMode="singleTop"
|
||||
android:exported="true">
|
||||
|
||||
android:parentActivityName=".activities.MainActivity"
|
||||
android:theme="@style/AppTheme.Slidable">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
@ -53,7 +57,6 @@
|
||||
android:host="localhost"
|
||||
android:scheme="infinity" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activities.CommentFilterUsageListingActivity"
|
||||
@ -156,8 +159,8 @@
|
||||
<service
|
||||
android:name=".services.DownloadMediaService"
|
||||
android:enabled="true"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false" />
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.ViewRedditGalleryActivity"
|
||||
@ -174,8 +177,8 @@
|
||||
<service
|
||||
android:name=".services.DownloadRedditVideoService"
|
||||
android:enabled="true"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false" />
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.ViewPrivateMessagesActivity"
|
||||
@ -454,13 +457,13 @@
|
||||
<service
|
||||
android:name=".services.SubmitPostService"
|
||||
android:enabled="true"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false" />
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
<service
|
||||
android:name=".services.EditProfileService"
|
||||
android:enabled="true"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="false" />
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<receiver android:name=".broadcastreceivers.DownloadedMediaDeleteActionBroadcastReceiver" />
|
||||
</application>
|
||||
|
||||
@ -31,6 +31,7 @@ import ml.docilealligator.infinityforreddit.activities.LockScreenActivity;
|
||||
import ml.docilealligator.infinityforreddit.activities.LoginActivity;
|
||||
import ml.docilealligator.infinityforreddit.activities.LoginChromeCustomTabActivity;
|
||||
import ml.docilealligator.infinityforreddit.activities.MainActivity;
|
||||
import ml.docilealligator.infinityforreddit.activities.ModmailActivity;
|
||||
import ml.docilealligator.infinityforreddit.activities.PostFilterPreferenceActivity;
|
||||
import ml.docilealligator.infinityforreddit.activities.PostFilterUsageListingActivity;
|
||||
import ml.docilealligator.infinityforreddit.activities.PostGalleryActivity;
|
||||
@ -312,6 +313,8 @@ public interface AppComponent {
|
||||
|
||||
void inject(LoginChromeCustomTabActivity loginChromeCustomTabActivity);
|
||||
|
||||
void inject(ModmailActivity modMailActivity);
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
AppComponent create(@BindsInstance Application application);
|
||||
|
||||
@ -316,6 +316,9 @@ public class MainActivity extends BaseActivity implements SortTypeSelectionCallb
|
||||
}*/
|
||||
|
||||
initializeNotificationAndBindView();
|
||||
|
||||
Intent intent = new Intent(this, ModmailActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -0,0 +1,179 @@
|
||||
package ml.docilealligator.infinityforreddit.activities
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.layout.AnimatedPane
|
||||
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
|
||||
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import ml.docilealligator.infinityforreddit.Infinity
|
||||
import ml.docilealligator.infinityforreddit.R
|
||||
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper
|
||||
import ml.docilealligator.infinityforreddit.mod.Conversation
|
||||
import ml.docilealligator.infinityforreddit.mod.ModMailConversationViewModel
|
||||
import ml.docilealligator.infinityforreddit.mod.ModMessage
|
||||
import retrofit2.Retrofit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class, ExperimentalMaterial3Api::class)
|
||||
class ModmailActivity : BaseActivity() {
|
||||
@Inject
|
||||
@Named("oauth")
|
||||
lateinit var mOauthRetrofit: Retrofit
|
||||
@Inject
|
||||
@Named("default")
|
||||
lateinit var mSharedPreferences: SharedPreferences
|
||||
@Inject
|
||||
@Named("current_account")
|
||||
lateinit var mCurrentAccountSharedPreferences: SharedPreferences
|
||||
@Inject
|
||||
lateinit var mCustomThemeWrapper: CustomThemeWrapper
|
||||
|
||||
lateinit var conversationViewModel: ModMailConversationViewModel
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
(application as Infinity).appComponent.inject(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (accessToken == null) {
|
||||
Toast.makeText(this, R.string.login_first, Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
enableEdgeToEdge()
|
||||
|
||||
conversationViewModel = ViewModelProvider.create(this, ModMailConversationViewModel.Factory(mOauthRetrofit, accessToken!!, mSharedPreferences))[ModMailConversationViewModel::class]
|
||||
|
||||
setContent {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
colors = topAppBarColors(
|
||||
containerColor = Color(mCustomThemeWrapper.colorPrimary),
|
||||
titleContentColor = Color(mCustomThemeWrapper.toolbarPrimaryTextAndIconColor)
|
||||
),
|
||||
title = {
|
||||
Text(getString(R.string.modmail_activity_label))
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
val navigator = rememberListDetailPaneScaffoldNavigator<Conversation>()
|
||||
|
||||
BackHandler(navigator.canNavigateBack()) {
|
||||
navigator.navigateBack()
|
||||
}
|
||||
|
||||
ListDetailPaneScaffold(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
directive = navigator.scaffoldDirective,
|
||||
value = navigator.scaffoldValue,
|
||||
listPane = {
|
||||
AnimatedPane {
|
||||
ConversationListView(conversationViewModel.flow.collectAsLazyPagingItems())
|
||||
}
|
||||
},
|
||||
detailPane = {
|
||||
AnimatedPane {
|
||||
navigator.currentDestination?.content?.let {
|
||||
ConversationDetailsView(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDefaultSharedPreferences(): SharedPreferences {
|
||||
return mSharedPreferences
|
||||
}
|
||||
|
||||
override fun getCurrentAccountSharedPreferences(): SharedPreferences {
|
||||
return mCurrentAccountSharedPreferences
|
||||
}
|
||||
|
||||
override fun getCustomThemeWrapper(): CustomThemeWrapper {
|
||||
return mCustomThemeWrapper
|
||||
}
|
||||
|
||||
override fun applyCustomTheme() {
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConversationListView(pagingItems: LazyPagingItems<Conversation>) {
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
items(count = pagingItems.itemCount) { index: Int ->
|
||||
val conversation = pagingItems[index]
|
||||
conversation?.let {
|
||||
ConversationView(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConversationView(conversation: Conversation) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
conversation.owner?.displayName?.let {
|
||||
Text(text = it, color = Color(mCustomThemeWrapper.subreddit))
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
for (author in conversation.authors) {
|
||||
author.name?.let {
|
||||
Text(text = it, color = Color(mCustomThemeWrapper.username))
|
||||
}
|
||||
}
|
||||
}
|
||||
conversation.subject?.let {
|
||||
Text(text = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConversationDetailsView(conversation: Conversation) {
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MessageView(message: ModMessage) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package ml.docilealligator.infinityforreddit.apis
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.HeaderMap
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface RedditAPIKt {
|
||||
@GET("/api/mod/conversations")
|
||||
suspend fun getModMailConversations(
|
||||
@HeaderMap headers: Map<String, String>,
|
||||
@Query("after") after: String?
|
||||
): Response<String>
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package ml.docilealligator.infinityforreddit.mod
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class Author(
|
||||
@SerializedName("isMod") var isMod: Boolean? = null,
|
||||
@SerializedName("isAdmin") var isAdmin: Boolean? = null,
|
||||
@SerializedName("name") var name: String? = null,
|
||||
@SerializedName("isOp") var isOp: Boolean? = null,
|
||||
@SerializedName("isParticipant") var isParticipant: Boolean? = null,
|
||||
@SerializedName("isApproved") var isApproved: Boolean? = null,
|
||||
@SerializedName("isHidden") var isHidden: Boolean? = null,
|
||||
@SerializedName("id") var id: String? = null,
|
||||
@SerializedName("isDeleted") var isDeleted: Boolean? = null
|
||||
) : Parcelable
|
||||
@ -0,0 +1,55 @@
|
||||
package ml.docilealligator.infinityforreddit.mod
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class Conversation(
|
||||
@SerializedName("isAuto") var isAuto: Boolean? = null,
|
||||
@SerializedName("participant") var participant: Participant? = Participant(),
|
||||
@SerializedName("objIds") var objIds: ArrayList<ObjId> = arrayListOf(),
|
||||
@SerializedName("isRepliable") var isRepliable: Boolean? = null,
|
||||
@SerializedName("lastUserUpdate") var lastUserUpdate: String? = null,
|
||||
@SerializedName("isInternal") var isInternal: Boolean? = null,
|
||||
@SerializedName("lastModUpdate") var lastModUpdate: String? = null,
|
||||
@SerializedName("authors") var authors: ArrayList<Author> = arrayListOf(),
|
||||
@SerializedName("lastUpdated") var lastUpdated: String? = null,
|
||||
@SerializedName("legacyFirstMessageId") var legacyFirstMessageId: String? = null,
|
||||
@SerializedName("state") var state: Int? = null,
|
||||
@SerializedName("conversationType") var conversationType: String? = null,
|
||||
@SerializedName("lastUnread") var lastUnread: String? = null,
|
||||
@SerializedName("owner") var owner: Owner? = Owner(),
|
||||
@SerializedName("subject") var subject: String? = null,
|
||||
@SerializedName("id") var id: String? = null,
|
||||
@SerializedName("isHighlighted") var isHighlighted: Boolean? = null,
|
||||
@SerializedName("numMessages") var numMessages: Int? = null
|
||||
): Parcelable {
|
||||
val messages: MutableList<ModMessage> = mutableListOf()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Participant(
|
||||
@SerializedName("isMod") var isMod: Boolean? = null,
|
||||
@SerializedName("isAdmin") var isAdmin: Boolean? = null,
|
||||
@SerializedName("name") var name: String? = null,
|
||||
@SerializedName("isOp") var isOp: Boolean? = null,
|
||||
@SerializedName("isParticipant") var isParticipant: Boolean? = null,
|
||||
@SerializedName("isApproved") var isApproved: Boolean? = null,
|
||||
@SerializedName("isHidden") var isHidden: Boolean? = null,
|
||||
@SerializedName("id") var id: String? = null,
|
||||
@SerializedName("isDeleted") var isDeleted: Boolean? = null
|
||||
): Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class ObjId(
|
||||
@SerializedName("id") var id: String? = null,
|
||||
@SerializedName("key") var key: String? = null
|
||||
): Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class Owner(
|
||||
@SerializedName("displayName") var displayName: String? = null,
|
||||
@SerializedName("type") var type: String? = null,
|
||||
@SerializedName("id") var id: String? = null
|
||||
): Parcelable
|
||||
@ -0,0 +1,42 @@
|
||||
package ml.docilealligator.infinityforreddit.mod
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
|
||||
data class ModMail(
|
||||
@SerializedName("viewerId") var viewerId: String? = null
|
||||
) {
|
||||
lateinit var conversations: MutableList<Conversation>;
|
||||
lateinit var messages: MutableList<ModMessage>;
|
||||
val conversationIds: MutableList<String> = arrayListOf()
|
||||
|
||||
fun parseConversations(conversationsJSONObject: JSONObject, gson: Gson) {
|
||||
for (conversationId in conversationIds) {
|
||||
try {
|
||||
conversations.add(gson.fromJson(conversationsJSONObject.getString(conversationId), Conversation::class.java))
|
||||
} catch (ignore: IOException) {
|
||||
ignore.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseModMessages(messagesJSONObject: JSONObject, gson: Gson) {
|
||||
for (conversation in conversations) {
|
||||
for (objId in conversation.objIds) {
|
||||
objId.key?.let { key ->
|
||||
if (key == "messages") {
|
||||
objId.id?.let { id ->
|
||||
try {
|
||||
messages.add(gson.fromJson(messagesJSONObject.getString(id), ModMessage::class.java))
|
||||
} catch (ignore: IOException) {
|
||||
ignore.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package ml.docilealligator.infinityforreddit.mod
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import ml.docilealligator.infinityforreddit.apis.RedditAPI
|
||||
import ml.docilealligator.infinityforreddit.apis.RedditAPIKt
|
||||
import ml.docilealligator.infinityforreddit.utils.APIUtils
|
||||
import ml.docilealligator.infinityforreddit.utils.JSONUtils
|
||||
import org.json.JSONObject
|
||||
import retrofit2.Retrofit
|
||||
import java.io.IOException
|
||||
|
||||
class ModMailConversationPagingSource(val retrofit: Retrofit, val accessToken: String, val sharedPreferences: SharedPreferences): PagingSource<String, Conversation>() {
|
||||
override fun getRefreshKey(state: PagingState<String, Conversation>): String? {
|
||||
return null;
|
||||
}
|
||||
|
||||
override suspend fun load(params: LoadParams<String>): LoadResult<String, Conversation> {
|
||||
try {
|
||||
val response = retrofit.create(RedditAPIKt::class.java)
|
||||
.getModMailConversations(APIUtils.getOAuthHeader(accessToken), params.key)
|
||||
|
||||
if (response.isSuccessful) {
|
||||
response.body()?.let {
|
||||
val json = JSONObject(it)
|
||||
val conversationIdsArray = json.getJSONArray(JSONUtils.CONVERSATION_IDS_KEY)
|
||||
if (conversationIdsArray.length() == 0) {
|
||||
return LoadResult.Page(listOf(), null, null)
|
||||
}
|
||||
|
||||
val gson = Gson()
|
||||
val conversations: MutableList<Conversation> = mutableListOf()
|
||||
|
||||
val messagesJSONObject = json.getJSONObject(JSONUtils.MESSAGES_KEY)
|
||||
for (i in 0 until conversationIdsArray.length()) {
|
||||
val conversationId = conversationIdsArray.getString(i)
|
||||
try {
|
||||
conversations.add(gson.fromJson(json.getJSONObject(JSONUtils.CONVERSATIONS_KEY).getString(conversationId), Conversation::class.java).apply {
|
||||
for (objId in objIds) {
|
||||
objId.key?.let { key ->
|
||||
if (key == "messages") {
|
||||
objId.id?.let { id ->
|
||||
try {
|
||||
messages.add(gson.fromJson(messagesJSONObject.getString(id), ModMessage::class.java))
|
||||
} catch (ignore: IOException) {
|
||||
ignore.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (ignore: IOException) {
|
||||
ignore.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
return LoadResult.Page(
|
||||
conversations, null, conversationIdsArray.getString(conversationIdsArray.length() - 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return LoadResult.Error(Exception("Error getting response"))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package ml.docilealligator.infinityforreddit.mod
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.cachedIn
|
||||
import retrofit2.Retrofit
|
||||
|
||||
class ModMailConversationViewModel(
|
||||
oauthRetrofit: Retrofit,
|
||||
accessToken: String,
|
||||
sharedPreferences: SharedPreferences
|
||||
) : ViewModel() {
|
||||
private val pagingSource: ModMailConversationPagingSource =
|
||||
ModMailConversationPagingSource(oauthRetrofit, accessToken, sharedPreferences)
|
||||
|
||||
val flow = Pager(
|
||||
PagingConfig(20)
|
||||
) {
|
||||
pagingSource
|
||||
}.flow.cachedIn(viewModelScope)
|
||||
|
||||
fun refresh() {
|
||||
pagingSource.invalidate()
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class Factory(
|
||||
private val oauthRetrofit: Retrofit,
|
||||
private val accessToken: String,
|
||||
private val sharedPreferences: SharedPreferences
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ModMailConversationViewModel(oauthRetrofit, accessToken, sharedPreferences) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package ml.docilealligator.infinityforreddit.mod
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class ModMessage(
|
||||
@SerializedName("body") var body: String? = null,
|
||||
@SerializedName("author") var author: Author? = Author(),
|
||||
@SerializedName("isInternal") var isInternal: Boolean? = null,
|
||||
@SerializedName("date") var date: String? = null,
|
||||
@SerializedName("bodyMarkdown") var bodyMarkdown: String? = null,
|
||||
@SerializedName("id") var id: String? = null,
|
||||
@SerializedName("participatingAs") var participatingAs: String? = null
|
||||
)
|
||||
@ -197,6 +197,11 @@ public class JSONUtils {
|
||||
public static final String VARIANTS_KEY = "variants";
|
||||
public static final String PAGE_KEY = "page";
|
||||
public static final String SEND_REPLIES_KEY = "send_replies";
|
||||
public static final String CONVERSATIONS_KEY = "conversations";
|
||||
public static final String VIEWER_ID_KEY = "viewerId";
|
||||
public static final String CONVERSATION_IDS_KEY = "conversationIds";
|
||||
public static final String OBJ_IDS_KEY = "objIds";
|
||||
public static final String MESSAGES_KEY = "messages";
|
||||
|
||||
@Nullable
|
||||
public static Map<String, MediaMetadata> parseMediaMetadata(JSONObject data) {
|
||||
|
||||
@ -47,6 +47,7 @@
|
||||
<string name="subscription_activity_label">Subscription</string>
|
||||
<string name="comment_filter_preference_activity_label">Comment Filter</string>
|
||||
<string name="customize_comment_filter_activity_label">Customize Comment Filter</string>
|
||||
<string name="modmail_activity_label">Modmail</string>
|
||||
|
||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||
@ -1524,5 +1525,7 @@
|
||||
<string name="download_gif">Download Gif</string>
|
||||
<string name="download_video">Download Video</string>
|
||||
<string name="download_all_gallery_images">Download All Gallery Images</string>
|
||||
<!-- TODO: Remove or change this placeholder text -->
|
||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@ -7,6 +7,8 @@ buildscript {
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.5.0'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.24'
|
||||
classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:1.9.24-1.0.20"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
Reference in New Issue
Block a user