fix: most profiles

This commit is contained in:
rebelonion 2024-03-05 02:29:00 -06:00
parent db50975174
commit a2ecc5e30e
8 changed files with 112 additions and 30 deletions

View file

@ -784,7 +784,9 @@ fun copyToClipboard(string: String, toast: Boolean = true) {
val clipboard = getSystemService(activity, ClipboardManager::class.java)
val clip = ClipData.newPlainText("label", string)
clipboard?.setPrimaryClip(clip)
if (toast) snackString(activity.getString(R.string.copied_text, string))
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
if (toast) snackString(activity.getString(R.string.copied_text, string))
}
}
@SuppressLint("SetTextI18n")
@ -1108,7 +1110,7 @@ fun buildMarkwon(activity: Activity, userInputContent: Boolean = true): Markwon
val markwon = Markwon.builder(activity)
.usePlugin(object : AbstractMarkwonPlugin() {
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
builder.linkResolver { view, link ->
builder.linkResolver { _, link ->
copyToClipboard(link, true)
}
}
@ -1119,7 +1121,7 @@ fun buildMarkwon(activity: Activity, userInputContent: Boolean = true): Markwon
.usePlugin(TablePlugin.create(activity))
.usePlugin(TaskListPlugin.create(activity))
.usePlugin(HtmlPlugin.create { plugin ->
if (!userInputContent) {
if (userInputContent) {
plugin.addHandler(
TagHandlerNoOp.create("h1", "h2", "h3", "h4", "h5", "h6", "hr", "pre", "a")
)

View file

@ -61,7 +61,7 @@ class AnilistQueries {
suspend fun getUserProfile(id: Int): Query.UserProfileResponse? {
return executeQuery<Query.UserProfileResponse>(
"""{user:User(id:$id){id,name,about(asHtml:true)avatar{medium,large},bannerImage,isFollowing,isFollower,isBlocked,favourites{anime{nodes{coverImage{extraLarge,large,medium,color}}}manga{nodes{id,coverImage{extraLarge,large,medium,color}}}characters{nodes{id,image{large,medium}}}staff{nodes{id,image{large,medium}}}studios{nodes{id,name}}}statistics{anime{count,meanScore,standardDeviation,minutesWatched,episodesWatched,chaptersRead,volumesRead}manga{count,meanScore,standardDeviation,minutesWatched,episodesWatched,chaptersRead,volumesRead}}siteUrl}}""",
"""{user:User(id:$id){id,name,about(asHtml:true)avatar{medium,large},bannerImage,isFollowing,isFollower,isBlocked,favourites{anime{nodes{id,coverImage{extraLarge,large,medium,color}}}manga{nodes{id,coverImage{extraLarge,large,medium,color}}}characters{nodes{id,image{large,medium}}}staff{nodes{id,image{large,medium}}}studios{nodes{id,name}}}statistics{anime{count,meanScore,standardDeviation,minutesWatched,episodesWatched,chaptersRead,volumesRead}manga{count,meanScore,standardDeviation,minutesWatched,episodesWatched,chaptersRead,volumesRead}}siteUrl}}""",
force = true
)
}

View file

@ -46,7 +46,7 @@ data class Character(
// Notes for site moderators
@SerialName("modNotes") var modNotes: String?,
)
) : java.io.Serializable
@Serializable
data class CharacterConnection(
@ -56,7 +56,7 @@ data class CharacterConnection(
// The pagination information
// @SerialName("pageInfo") var pageInfo: PageInfo?,
)
) : java.io.Serializable
@Serializable
data class CharacterEdge(
@ -82,7 +82,7 @@ data class CharacterEdge(
// The order the character should be displayed from the users favourites
@SerialName("favouriteOrder") var favouriteOrder: Int?,
)
) : java.io.Serializable
@Serializable
data class CharacterName(
@ -109,7 +109,7 @@ data class CharacterName(
// The currently authenticated users preferred name language. Default romaji for non-authenticated
@SerialName("userPreferred") var userPreferred: String?,
)
) : java.io.Serializable
@Serializable
data class CharacterImage(
@ -118,4 +118,4 @@ data class CharacterImage(
// The character's image of media at medium size
@SerialName("medium") var medium: String?,
)
) : java.io.Serializable

View file

@ -143,7 +143,7 @@ class Query {
data class ToggleFollow(
@SerialName("data")
val data: Data?
) {
) : java.io.Serializable {
@Serializable
data class Data(
@SerialName("ToggleFollow")
@ -156,7 +156,7 @@ class Query {
data class GenreCollection(
@SerialName("data")
val data: Data
) {
) : java.io.Serializable {
@Serializable
data class Data(
@SerialName("GenreCollection")
@ -168,7 +168,7 @@ class Query {
data class MediaTagCollection(
@SerialName("data")
val data: Data
) {
) : java.io.Serializable {
@Serializable
data class Data(
@SerialName("MediaTagCollection")
@ -180,7 +180,7 @@ class Query {
data class User(
@SerialName("data")
val data: Data
) {
) : java.io.Serializable {
@Serializable
data class Data(
@SerialName("User")
@ -192,7 +192,7 @@ class Query {
data class UserProfileResponse(
@SerialName("data")
val data: Data
) {
) : java.io.Serializable {
@Serializable
data class Data(
@SerialName("user")
@ -218,7 +218,7 @@ class Query {
val isFollower: Boolean,
@SerialName("isBlocked")
val isBlocked: Boolean,
@SerialName("favorites")
@SerialName("favourites")
val favorites: UserFavorites?,
@SerialName("statistics")
val statistics: NNUserStatisticTypes,

View file

@ -251,7 +251,7 @@ data class MediaCoverImage(
// Average #hex color of cover image
@SerialName("color") var color: String?,
)
) : java.io.Serializable
@Serializable
data class MediaList(
@ -490,7 +490,7 @@ data class MediaExternalLink(
// isDisabled: Boolean
@SerialName("notes") var notes: String?,
)
) : java.io.Serializable
@Serializable
enum class ExternalLinkType {
@ -512,13 +512,13 @@ data class MediaListCollection(
// If there is another chunk
@SerialName("hasNextChunk") var hasNextChunk: Boolean?,
)
) : java.io.Serializable
@Serializable
data class FollowData(
@SerialName("id") var id: Int,
@SerialName("isFollowing") var isFollowing: Boolean,
)
) : java.io.Serializable
@Serializable
data class MediaListGroup(
@ -532,4 +532,4 @@ data class MediaListGroup(
@SerialName("isSplitCompletedList") var isSplitCompletedList: Boolean?,
@SerialName("status") var status: MediaListStatus?,
)
) : java.io.Serializable

View file

@ -2,10 +2,12 @@ package ani.dantotsu.profile
import android.content.Intent
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.LayoutAnimationController
import android.webkit.WebView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
@ -13,7 +15,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import ani.dantotsu.buildMarkwon
import ani.dantotsu.connections.anilist.ProfileViewModel
import ani.dantotsu.connections.anilist.api.Query
import ani.dantotsu.databinding.FragmentProfileBinding
@ -23,10 +24,12 @@ import ani.dantotsu.media.MediaAdaptor
import ani.dantotsu.media.user.ListActivity
import ani.dantotsu.setSlideIn
import ani.dantotsu.setSlideUp
import ani.dantotsu.util.ColorEditor.Companion.toCssColor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class ProfileFragment(): Fragment() {
class ProfileFragment() : Fragment() {
lateinit var binding: FragmentProfileBinding
private lateinit var activity: ProfileActivity
private lateinit var user: Query.UserProfile
@ -44,8 +47,36 @@ class ProfileFragment(): Fragment() {
super.onViewCreated(view, savedInstanceState)
activity = requireActivity() as ProfileActivity
user = arguments?.getSerializable("user") as Query.UserProfile
val markwon = buildMarkwon(activity, false)
markwon.setMarkdown(binding.profileUserBio, user.about?:"")
val backGroundColorTypedValue = TypedValue()
val textColorTypedValue = TypedValue()
activity.theme.resolveAttribute(
android.R.attr.windowBackground,
backGroundColorTypedValue,
true
)
activity.theme.resolveAttribute(
com.google.android.material.R.attr.colorOnBackground,
textColorTypedValue,
true
)
binding.profileUserBio.settings.loadWithOverviewMode = true
binding.profileUserBio.settings.useWideViewPort = true
binding.profileUserBio.settings.javaScriptEnabled = true
WebView.setWebContentsDebuggingEnabled(true)
binding.profileUserBio.setInitialScale(1)
binding.profileUserBio.loadDataWithBaseURL(
null,
styled(
convertMarkdownToHtml(user.about ?: ""),
backGroundColorTypedValue.data,
textColorTypedValue.data
),
"text/html; charset=utf-8",
"UTF-8",
null
)
binding.userInfoContainer.visibility = if (user.about != null) View.VISIBLE else View.GONE
binding.profileAnimeList.setOnClickListener {
@ -67,7 +98,8 @@ class ProfileFragment(): Fragment() {
binding.profileAnimeListImage.loadImage("https://bit.ly/31bsIHq")
binding.profileMangaListImage.loadImage("https://bit.ly/2ZGfcuG")
binding.statsEpisodesWatched.text = user.statistics.anime.episodesWatched.toString()
binding.statsDaysWatched.text = (user.statistics.anime.minutesWatched / (24 * 60)).toString()
binding.statsDaysWatched.text =
(user.statistics.anime.minutesWatched / (24 * 60)).toString()
binding.statsTotalAnime.text = user.statistics.anime.count.toString()
binding.statsAnimeMeanScore.text = user.statistics.anime.meanScore.toString()
binding.statsChaptersRead.text = user.statistics.manga.chaptersRead.toString()
@ -80,7 +112,7 @@ class ProfileFragment(): Fragment() {
model.setAnimeFav(user.id)
model.setMangaFav(user.id)
}
initRecyclerView(
model.getAnimeFav(),
binding.profileFavAnimeContainer,
@ -89,7 +121,7 @@ class ProfileFragment(): Fragment() {
binding.profileFavAnimeEmpty,
binding.profileFavAnime
)
initRecyclerView(
model.getMangaFav(),
binding.profileFavMangaContainer,
@ -107,6 +139,16 @@ class ProfileFragment(): Fragment() {
}
}
private fun convertMarkdownToHtml(markdown: String): String {
val regex = """\[\!\[(.*?)\]\((.*?)\)\]\((.*?)\)""".toRegex()
return regex.replace(markdown) { matchResult ->
val altText = matchResult.groupValues[1]
val imageUrl = matchResult.groupValues[2]
val linkUrl = matchResult.groupValues[3]
"""<a href="$linkUrl"><img src="$imageUrl" alt="$altText"></a>"""
}
}
private fun initRecyclerView(
mode: LiveData<ArrayList<Media>>,
container: View,
@ -146,6 +188,34 @@ class ProfileFragment(): Fragment() {
}
}
private fun styled(html: String, backGroundColor: Int, textColor: Int): String {
return """
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, charset=UTF-8">
<style>
body {
background-color: ${backGroundColor.toCssColor()};
color: ${textColor.toCssColor()};
margin: 0;
padding: 0;
max-width: 100%;
overflow-x: hidden; /* Prevent horizontal scrolling */
}
img {
max-width: 100%;
height: auto; /* Maintain aspect ratio */
}
/* Add responsive design elements for other content as needed */
</style>
</head>
<body>
$html
</body>
""".trimIndent()
}
companion object {
fun newInstance(query: Query.UserProfile): ProfileFragment {
val args = Bundle().apply {

View file

@ -84,5 +84,14 @@ class ColorEditor {
}
return adjustedColor
}
fun Int.toCssColor(): String {
var base = "rgba("
base += "${Color.red(this)}, "
base += "${Color.green(this)}, "
base += "${Color.blue(this)}, "
base += "${Color.alpha(this) / 255.0})"
return base
}
}
}

View file

@ -332,7 +332,7 @@
<LinearLayout
android:id="@+id/userInfoContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
@ -347,12 +347,13 @@
android:textSize="16sp"
tools:ignore="HardcodedText" />
<TextView
<WebView
android:id="@+id/profileUserBio"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textAlignment="textStart"
android:ellipsize="end"
android:padding="16dp"
tools:text="@string/slogan" />