mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-04-19 19:14:51 +00:00
Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay
This commit is contained in:
commit
b556d1e81d
46 changed files with 136 additions and 1109 deletions
|
@ -1,13 +1,13 @@
|
|||
package com.futo.platformplayer
|
||||
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.states.AnnouncementType
|
||||
import com.futo.platformplayer.states.StateAnnouncement
|
||||
import com.futo.platformplayer.states.StatePlatform
|
||||
import com.futo.polycentric.core.ProcessHandle
|
||||
import com.futo.polycentric.core.Store
|
||||
import com.futo.polycentric.core.SystemState
|
||||
import com.futo.polycentric.core.base64UrlToByteArray
|
||||
import userpackage.Protocol
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
|
@ -40,33 +40,25 @@ fun Protocol.ImageBundle?.selectHighestResolutionImage(): Protocol.ImageManifest
|
|||
return imageManifestsList.filter { it.byteCount < maximumFileSize }.maxByOrNull { abs(it.width * it.height) }
|
||||
}
|
||||
|
||||
fun String.getDataLinkFromUrl(): Protocol.URLInfoDataLink? {
|
||||
val urlData = if (this.startsWith("polycentric://")) {
|
||||
this.substring("polycentric://".length)
|
||||
} else this;
|
||||
|
||||
val urlBytes = urlData.base64UrlToByteArray();
|
||||
val urlInfo = Protocol.URLInfo.parseFrom(urlBytes);
|
||||
if (urlInfo.urlType != 4L) {
|
||||
return null
|
||||
}
|
||||
|
||||
val dataLink = Protocol.URLInfoDataLink.parseFrom(urlInfo.body);
|
||||
return dataLink
|
||||
}
|
||||
|
||||
fun Protocol.Claim.resolveChannelUrl(): String? {
|
||||
return StatePlatform.instance.resolveChannelUrlByClaimTemplates(this.claimType.toInt(), this.claimFieldsList.associate { Pair(it.key.toInt(), it.value) })
|
||||
}
|
||||
|
||||
fun Protocol.Claim.resolveChannelUrls(): List<String> {
|
||||
return StatePlatform.instance.resolveChannelUrlsByClaimTemplates(this.claimType.toInt(), this.claimFieldsList.associate { Pair(it.key.toInt(), it.value) })
|
||||
}
|
||||
|
||||
suspend fun ProcessHandle.fullyBackfillServersAnnounceExceptions() {
|
||||
val systemState = SystemState.fromStorageTypeSystemState(Store.instance.getSystemState(system))
|
||||
if (!systemState.servers.contains(PolycentricCache.SERVER)) {
|
||||
Logger.w("Backfill", "Polycentric prod server not added, adding it.")
|
||||
addServer(PolycentricCache.SERVER)
|
||||
}
|
||||
|
||||
val exceptions = fullyBackfillServers()
|
||||
for (pair in exceptions) {
|
||||
val server = pair.key
|
||||
val exception = pair.value
|
||||
|
||||
StateAnnouncement.instance.registerAnnouncement(
|
||||
"backfill-failed",
|
||||
"Backfill failed",
|
||||
"Failed to backfill server $server. $exception",
|
||||
AnnouncementType.SESSION_RECURRING
|
||||
);
|
||||
|
||||
Logger.e("Backfill", "Failed to backfill server $server.", exception)
|
||||
}
|
||||
}
|
|
@ -11,16 +11,16 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.UIDialogs
|
||||
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.polycentric.PolycentricStorage
|
||||
import com.futo.platformplayer.setNavigationBarColorAndIcons
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StatePolycentric
|
||||
import com.futo.platformplayer.views.LoaderView
|
||||
import com.futo.polycentric.core.ApiMethods
|
||||
import com.futo.polycentric.core.ProcessHandle
|
||||
import com.futo.polycentric.core.Store
|
||||
import com.futo.polycentric.core.fullyBackfillServersAnnounceExceptions
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -87,7 +87,7 @@ class PolycentricCreateProfileActivity : AppCompatActivity() {
|
|||
Logger.e(TAG, "Failed to save process secret to secret storage.", e)
|
||||
}
|
||||
|
||||
processHandle.addServer(PolycentricCache.SERVER);
|
||||
processHandle.addServer(ApiMethods.SERVER);
|
||||
processHandle.setUsername(username);
|
||||
StatePolycentric.instance.setProcessHandle(processHandle);
|
||||
} catch (e: Throwable) {
|
||||
|
|
|
@ -12,12 +12,12 @@ import androidx.lifecycle.lifecycleScope
|
|||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.UIDialogs
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.polycentric.PolycentricStorage
|
||||
import com.futo.platformplayer.setNavigationBarColorAndIcons
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StatePolycentric
|
||||
import com.futo.platformplayer.views.overlays.LoaderOverlay
|
||||
import com.futo.polycentric.core.ApiMethods
|
||||
import com.futo.polycentric.core.KeyPair
|
||||
import com.futo.polycentric.core.Process
|
||||
import com.futo.polycentric.core.ProcessSecret
|
||||
|
@ -145,7 +145,7 @@ class PolycentricImportProfileActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
StatePolycentric.instance.setProcessHandle(processHandle);
|
||||
processHandle.fullyBackfillClient(PolycentricCache.SERVER);
|
||||
processHandle.fullyBackfillClient(ApiMethods.SERVER);
|
||||
withContext(Dispatchers.Main) {
|
||||
startActivity(Intent(this@PolycentricImportProfileActivity, PolycentricProfileActivity::class.java));
|
||||
finish();
|
||||
|
|
|
@ -21,10 +21,8 @@ import com.bumptech.glide.Glide
|
|||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.UIDialogs
|
||||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.polycentric.PolycentricStorage
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.setNavigationBarColorAndIcons
|
||||
|
@ -32,8 +30,10 @@ import com.futo.platformplayer.states.StateApp
|
|||
import com.futo.platformplayer.states.StatePolycentric
|
||||
import com.futo.platformplayer.views.buttons.BigButton
|
||||
import com.futo.platformplayer.views.overlays.LoaderOverlay
|
||||
import com.futo.polycentric.core.ApiMethods
|
||||
import com.futo.polycentric.core.Store
|
||||
import com.futo.polycentric.core.SystemState
|
||||
import com.futo.polycentric.core.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.polycentric.core.systemToURLInfoSystemLinkUrl
|
||||
import com.futo.polycentric.core.toBase64Url
|
||||
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
|
||||
|
@ -145,7 +145,7 @@ class PolycentricProfileActivity : AppCompatActivity() {
|
|||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
processHandle.fullyBackfillClient(PolycentricCache.SERVER)
|
||||
processHandle.fullyBackfillClient(ApiMethods.SERVER)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
updateUI();
|
||||
|
|
|
@ -22,7 +22,6 @@ import com.futo.platformplayer.api.media.models.comments.PolycentricPlatformComm
|
|||
import com.futo.platformplayer.api.media.models.ratings.RatingLikeDislikes
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
|
@ -30,6 +29,7 @@ import com.futo.platformplayer.states.StatePolycentric
|
|||
import com.futo.polycentric.core.ClaimType
|
||||
import com.futo.polycentric.core.Store
|
||||
import com.futo.polycentric.core.SystemState
|
||||
import com.futo.polycentric.core.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.polycentric.core.systemToURLInfoSystemLinkUrl
|
||||
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
|
||||
import com.google.android.material.button.MaterialButton
|
||||
|
|
|
@ -13,7 +13,6 @@ import com.futo.platformplayer.R
|
|||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.fixHtmlLinks
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.resolveChannelUrl
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
|
@ -21,6 +20,7 @@ import com.futo.platformplayer.setPlatformPlayerLinkMovementMethod
|
|||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.toHumanNumber
|
||||
import com.futo.platformplayer.views.platform.PlatformLinkView
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
import com.futo.polycentric.core.toName
|
||||
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
|
||||
|
||||
|
@ -134,9 +134,7 @@ class ChannelAboutFragment : Fragment, IChannelTabFragment {
|
|||
}
|
||||
}
|
||||
if(!map.containsKey("Harbor"))
|
||||
this.context?.let {
|
||||
map.set("Harbor", polycentricProfile.getHarborUrl(it));
|
||||
}
|
||||
map.set("Harbor", polycentricProfile.getHarborUrl());
|
||||
|
||||
if (map.isNotEmpty())
|
||||
setLinks(map, if (polycentricProfile.systemState.username.isNotBlank()) polycentricProfile.systemState.username else _lastChannel?.name ?: "")
|
||||
|
|
|
@ -29,7 +29,6 @@ import com.futo.platformplayer.engine.exceptions.PluginException
|
|||
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
|
||||
import com.futo.platformplayer.exceptions.ChannelException
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.FeedView
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.states.StateCache
|
||||
import com.futo.platformplayer.states.StatePlatform
|
||||
|
@ -39,6 +38,7 @@ import com.futo.platformplayer.views.FeedStyle
|
|||
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
|
||||
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||
import com.futo.platformplayer.views.adapters.feedtypes.PreviewContentListAdapter
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.max
|
||||
|
|
|
@ -16,12 +16,12 @@ import com.futo.platformplayer.constructs.Event1
|
|||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.ChannelFragment
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.resolveChannelUrl
|
||||
import com.futo.platformplayer.states.StatePlatform
|
||||
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||
import com.futo.platformplayer.views.adapters.viewholders.CreatorViewHolder
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
|
||||
class ChannelListFragment : Fragment, IChannelTabFragment {
|
||||
private var _channels: ArrayList<IPlatformChannel> = arrayListOf();
|
||||
|
|
|
@ -8,8 +8,8 @@ import android.widget.TextView
|
|||
import androidx.fragment.app.Fragment
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.views.SupportView
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
|
||||
|
||||
class ChannelMonetizationFragment : Fragment, IChannelTabFragment {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.futo.platformplayer.fragment.channel.tab
|
||||
|
||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
|
||||
interface IChannelTabFragment {
|
||||
fun setChannel(channel: IPlatformChannel)
|
||||
|
|
|
@ -42,7 +42,6 @@ import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
|||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.SearchType
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.selectHighestResolutionImage
|
||||
import com.futo.platformplayer.states.StatePlatform
|
||||
|
@ -55,29 +54,14 @@ import com.futo.platformplayer.views.adapters.ChannelViewPagerAdapter
|
|||
import com.futo.platformplayer.views.others.CreatorThumbnail
|
||||
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
|
||||
import com.futo.platformplayer.views.subscriptions.SubscribeButton
|
||||
import com.futo.polycentric.core.OwnedClaim
|
||||
import com.futo.polycentric.core.PublicKey
|
||||
import com.futo.polycentric.core.Store
|
||||
import com.futo.polycentric.core.SystemState
|
||||
import com.futo.polycentric.core.systemToURLInfoSystemLinkUrl
|
||||
import com.futo.polycentric.core.ApiMethods
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PolycentricProfile(
|
||||
val system: PublicKey, val systemState: SystemState, val ownedClaims: List<OwnedClaim>
|
||||
) {
|
||||
fun getHarborUrl(context: Context): String{
|
||||
val systemState = SystemState.fromStorageTypeSystemState(Store.instance.getSystemState(system));
|
||||
val url = system.systemToURLInfoSystemLinkUrl(systemState.servers.asIterable());
|
||||
return "https://harbor.social/" + url.substring("polycentric://".length);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelFragment : MainFragment() {
|
||||
override val isMainView: Boolean = true
|
||||
|
@ -144,15 +128,14 @@ class ChannelFragment : MainFragment() {
|
|||
|
||||
private val _onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {}
|
||||
|
||||
private val _taskLoadPolycentricProfile: TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>
|
||||
private val _taskLoadPolycentricProfile: TaskHandler<PlatformID, PolycentricProfile?>
|
||||
private val _taskGetChannel: TaskHandler<String, IPlatformChannel>
|
||||
|
||||
init {
|
||||
inflater.inflate(R.layout.fragment_channel, this)
|
||||
_taskLoadPolycentricProfile =
|
||||
TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>({ fragment.lifecycleScope },
|
||||
_taskLoadPolycentricProfile = TaskHandler<PlatformID, PolycentricProfile?>({ fragment.lifecycleScope },
|
||||
{ id ->
|
||||
return@TaskHandler PolycentricCache.instance.getProfileAsync(id)
|
||||
return@TaskHandler ApiMethods.getPolycentricProfileByClaim(ApiMethods.SERVER, ApiMethods.FUTO_TRUST_ROOT, id.claimFieldType.toLong(), id.claimType.toLong(), id.value!!)
|
||||
}).success { setPolycentricProfile(it, animate = true) }.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load polycentric profile.", it)
|
||||
}
|
||||
|
@ -328,7 +311,7 @@ class ChannelFragment : MainFragment() {
|
|||
_creatorThumbnail.setThumbnail(parameter.thumbnail, true)
|
||||
Glide.with(_imageBanner).clear(_imageBanner)
|
||||
|
||||
loadPolycentricProfile(parameter.id, parameter.url)
|
||||
loadPolycentricProfile(parameter.id)
|
||||
}
|
||||
|
||||
_url = parameter.url
|
||||
|
@ -342,7 +325,7 @@ class ChannelFragment : MainFragment() {
|
|||
_creatorThumbnail.setThumbnail(parameter.channel.thumbnail, true)
|
||||
Glide.with(_imageBanner).clear(_imageBanner)
|
||||
|
||||
loadPolycentricProfile(parameter.channel.id, parameter.channel.url)
|
||||
loadPolycentricProfile(parameter.channel.id)
|
||||
}
|
||||
|
||||
_url = parameter.channel.url
|
||||
|
@ -359,16 +342,8 @@ class ChannelFragment : MainFragment() {
|
|||
_tabs.selectTab(_tabs.getTabAt(selectedTabIndex))
|
||||
}
|
||||
|
||||
private fun loadPolycentricProfile(id: PlatformID, url: String) {
|
||||
val cachedPolycentricProfile = PolycentricCache.instance.getCachedProfile(url, true)
|
||||
if (cachedPolycentricProfile != null) {
|
||||
setPolycentricProfile(cachedPolycentricProfile, animate = true)
|
||||
if (cachedPolycentricProfile.expired) {
|
||||
_taskLoadPolycentricProfile.run(id)
|
||||
}
|
||||
} else {
|
||||
_taskLoadPolycentricProfile.run(id)
|
||||
}
|
||||
private fun loadPolycentricProfile(id: PlatformID) {
|
||||
_taskLoadPolycentricProfile.run(id)
|
||||
}
|
||||
|
||||
private fun setLoading(isLoading: Boolean) {
|
||||
|
@ -533,20 +508,13 @@ class ChannelFragment : MainFragment() {
|
|||
|
||||
private fun setPolycentricProfileOr(url: String, or: () -> Unit) {
|
||||
setPolycentricProfile(null, animate = false)
|
||||
|
||||
val cachedProfile = channel?.let { PolycentricCache.instance.getCachedProfile(url) }
|
||||
if (cachedProfile != null) {
|
||||
setPolycentricProfile(cachedProfile, animate = false)
|
||||
} else {
|
||||
or()
|
||||
}
|
||||
or()
|
||||
}
|
||||
|
||||
private fun setPolycentricProfile(
|
||||
cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean
|
||||
profile: PolycentricProfile?, animate: Boolean
|
||||
) {
|
||||
val dp35 = 35.dp(resources)
|
||||
val profile = cachedPolycentricProfile?.profile
|
||||
val avatar = profile?.systemState?.avatar?.selectBestImage(dp35 * dp35)?.let {
|
||||
it.toURLInfoSystemLinkUrl(
|
||||
profile.system.toProto(), it.process, profile.systemState.servers.toList()
|
||||
|
|
|
@ -23,7 +23,6 @@ import com.futo.platformplayer.api.media.models.comments.IPlatformComment
|
|||
import com.futo.platformplayer.api.media.models.comments.PolycentricPlatformComment
|
||||
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
||||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StatePlatform
|
||||
|
@ -32,6 +31,7 @@ import com.futo.platformplayer.views.adapters.CommentWithReferenceViewHolder
|
|||
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||
import com.futo.platformplayer.views.overlays.RepliesOverlay
|
||||
import com.futo.polycentric.core.PublicKey
|
||||
import com.futo.polycentric.core.fullyBackfillServersAnnounceExceptions
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.UnknownHostException
|
||||
|
|
|
@ -33,10 +33,8 @@ import com.futo.platformplayer.api.media.models.ratings.RatingLikes
|
|||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.fixHtmlWhitespace
|
||||
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.setPlatformPlayerLinkMovementMethod
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StatePlatform
|
||||
|
@ -47,7 +45,6 @@ import com.futo.platformplayer.views.adapters.ChannelTab
|
|||
import com.futo.platformplayer.views.adapters.feedtypes.PreviewPostView
|
||||
import com.futo.platformplayer.views.comments.AddCommentView
|
||||
import com.futo.platformplayer.views.others.CreatorThumbnail
|
||||
import com.futo.platformplayer.views.others.Toggle
|
||||
import com.futo.platformplayer.views.overlays.RepliesOverlay
|
||||
import com.futo.platformplayer.views.pills.PillRatingLikesDislikes
|
||||
import com.futo.platformplayer.views.platform.PlatformIndicator
|
||||
|
@ -57,6 +54,8 @@ import com.futo.polycentric.core.ApiMethods
|
|||
import com.futo.polycentric.core.ContentType
|
||||
import com.futo.polycentric.core.Models
|
||||
import com.futo.polycentric.core.Opinion
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
import com.futo.polycentric.core.fullyBackfillServersAnnounceExceptions
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.material.imageview.ShapeableImageView
|
||||
import com.google.android.material.shape.CornerFamily
|
||||
|
@ -112,7 +111,7 @@ class PostDetailFragment : MainFragment {
|
|||
private var _isLoading = false;
|
||||
private var _post: IPlatformPostDetails? = null;
|
||||
private var _postOverview: IPlatformPost? = null;
|
||||
private var _polycentricProfile: PolycentricCache.CachedPolycentricProfile? = null;
|
||||
private var _polycentricProfile: PolycentricProfile? = null;
|
||||
private var _version = 0;
|
||||
private var _isRepliesVisible: Boolean = false;
|
||||
private var _repliesAnimator: ViewPropertyAnimator? = null;
|
||||
|
@ -169,7 +168,7 @@ class PostDetailFragment : MainFragment {
|
|||
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_post), it, ::fetchPost, null, _fragment);
|
||||
} else TaskHandler(IPlatformPostDetails::class.java) { _fragment.lifecycleScope };
|
||||
|
||||
private val _taskLoadPolycentricProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(StateApp.instance.scopeGetter, { PolycentricCache.instance.getProfileAsync(it) })
|
||||
private val _taskLoadPolycentricProfile = TaskHandler<PlatformID, PolycentricProfile?>(StateApp.instance.scopeGetter, { ApiMethods.getPolycentricProfileByClaim(ApiMethods.SERVER, ApiMethods.FUTO_TRUST_ROOT, it.claimFieldType.toLong(), it.claimType.toLong(), it.value!!) })
|
||||
.success { it -> setPolycentricProfile(it, animate = true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load claims.", it);
|
||||
|
@ -274,7 +273,7 @@ class PostDetailFragment : MainFragment {
|
|||
};
|
||||
|
||||
_buttonStore.setOnClickListener {
|
||||
_polycentricProfile?.profile?.systemState?.store?.let {
|
||||
_polycentricProfile?.systemState?.store?.let {
|
||||
try {
|
||||
val uri = Uri.parse(it);
|
||||
val intent = Intent(Intent.ACTION_VIEW);
|
||||
|
@ -334,7 +333,7 @@ class PostDetailFragment : MainFragment {
|
|||
}
|
||||
|
||||
try {
|
||||
val queryReferencesResponse = ApiMethods.getQueryReferences(PolycentricCache.SERVER, ref, null,null,
|
||||
val queryReferencesResponse = ApiMethods.getQueryReferences(ApiMethods.SERVER, ref, null,null,
|
||||
arrayListOf(
|
||||
Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder().setFromType(
|
||||
ContentType.OPINION.value).setValue(
|
||||
|
@ -604,16 +603,8 @@ class PostDetailFragment : MainFragment {
|
|||
|
||||
private fun fetchPolycentricProfile() {
|
||||
val author = _post?.author ?: _postOverview?.author ?: return;
|
||||
val cachedPolycentricProfile = PolycentricCache.instance.getCachedProfile(author.url, true);
|
||||
if (cachedPolycentricProfile != null) {
|
||||
setPolycentricProfile(cachedPolycentricProfile, animate = false);
|
||||
if (cachedPolycentricProfile.expired) {
|
||||
_taskLoadPolycentricProfile.run(author.id);
|
||||
}
|
||||
} else {
|
||||
setPolycentricProfile(null, animate = false);
|
||||
_taskLoadPolycentricProfile.run(author.id);
|
||||
}
|
||||
}
|
||||
|
||||
private fun setChannelMeta(value: IPlatformPost?) {
|
||||
|
@ -639,17 +630,18 @@ class PostDetailFragment : MainFragment {
|
|||
_repliesOverlay.cleanup();
|
||||
}
|
||||
|
||||
private fun setPolycentricProfile(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||
_polycentricProfile = cachedPolycentricProfile;
|
||||
private fun setPolycentricProfile(polycentricProfile: PolycentricProfile?, animate: Boolean) {
|
||||
_polycentricProfile = polycentricProfile;
|
||||
|
||||
if (cachedPolycentricProfile?.profile == null) {
|
||||
val pp = _polycentricProfile;
|
||||
if (pp == null) {
|
||||
_layoutMonetization.visibility = View.GONE;
|
||||
_creatorThumbnail.setHarborAvailable(false, animate, null);
|
||||
return;
|
||||
}
|
||||
|
||||
_layoutMonetization.visibility = View.VISIBLE;
|
||||
_creatorThumbnail.setHarborAvailable(true, animate, cachedPolycentricProfile.profile.system.toProto());
|
||||
_creatorThumbnail.setHarborAvailable(true, animate, pp.system.toProto());
|
||||
}
|
||||
|
||||
private fun fetchPost() {
|
||||
|
|
|
@ -94,12 +94,10 @@ import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException
|
|||
import com.futo.platformplayer.exceptions.UnsupportedCastException
|
||||
import com.futo.platformplayer.fixHtmlLinks
|
||||
import com.futo.platformplayer.fixHtmlWhitespace
|
||||
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.platformplayer.getNowDiffSeconds
|
||||
import com.futo.platformplayer.helpers.VideoHelper
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.receivers.MediaControlReceiver
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.states.AnnouncementType
|
||||
|
@ -158,6 +156,8 @@ import com.futo.polycentric.core.ApiMethods
|
|||
import com.futo.polycentric.core.ContentType
|
||||
import com.futo.polycentric.core.Models
|
||||
import com.futo.polycentric.core.Opinion
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
import com.futo.polycentric.core.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
|
||||
import com.google.protobuf.ByteString
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -294,7 +294,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
private set;
|
||||
private var _historicalPosition: Long = 0;
|
||||
private var _commentsCount = 0;
|
||||
private var _polycentricProfile: PolycentricCache.CachedPolycentricProfile? = null;
|
||||
private var _polycentricProfile: PolycentricProfile? = null;
|
||||
private var _slideUpOverlay: SlideUpMenuOverlay? = null;
|
||||
private var _autoplayVideo: IPlatformVideo? = null
|
||||
|
||||
|
@ -409,12 +409,12 @@ class VideoDetailView : ConstraintLayout {
|
|||
};
|
||||
|
||||
_monetization.onSupportTap.subscribe {
|
||||
_container_content_support.setPolycentricProfile(_polycentricProfile?.profile);
|
||||
_container_content_support.setPolycentricProfile(_polycentricProfile);
|
||||
switchContentView(_container_content_support);
|
||||
};
|
||||
|
||||
_monetization.onStoreTap.subscribe {
|
||||
_polycentricProfile?.profile?.systemState?.store?.let {
|
||||
_polycentricProfile?.systemState?.store?.let {
|
||||
try {
|
||||
val uri = Uri.parse(it);
|
||||
val intent = Intent(Intent.ACTION_VIEW);
|
||||
|
@ -1236,16 +1236,8 @@ class VideoDetailView : ConstraintLayout {
|
|||
_creatorThumbnail.setThumbnail(video.author.thumbnail, false);
|
||||
_channelName.text = video.author.name;
|
||||
|
||||
val cachedPolycentricProfile = PolycentricCache.instance.getCachedProfile(video.author.url, true);
|
||||
if (cachedPolycentricProfile != null) {
|
||||
setPolycentricProfile(cachedPolycentricProfile, animate = false);
|
||||
if (cachedPolycentricProfile.expired) {
|
||||
_taskLoadPolycentricProfile.run(video.author.id);
|
||||
}
|
||||
} else {
|
||||
setPolycentricProfile(null, animate = false);
|
||||
_taskLoadPolycentricProfile.run(video.author.id);
|
||||
}
|
||||
setPolycentricProfile(null, animate = false);
|
||||
_taskLoadPolycentricProfile.run(video.author.id);
|
||||
|
||||
_player.clear();
|
||||
|
||||
|
@ -1405,11 +1397,8 @@ class VideoDetailView : ConstraintLayout {
|
|||
setTabIndex(2, true)
|
||||
} else {
|
||||
when (Settings.instance.comments.defaultCommentSection) {
|
||||
0 -> if (Settings.instance.other.polycentricEnabled) setTabIndex(
|
||||
0,
|
||||
true
|
||||
) else setTabIndex(1, true);
|
||||
1 -> setTabIndex(1, true);
|
||||
0 -> if (Settings.instance.other.polycentricEnabled) setTabIndex(0, true) else setTabIndex(1, true)
|
||||
1 -> setTabIndex(1, true)
|
||||
2 -> setTabIndex(StateMeta.instance.getLastCommentSection(), true)
|
||||
}
|
||||
}
|
||||
|
@ -1447,16 +1436,8 @@ class VideoDetailView : ConstraintLayout {
|
|||
_buttonSubscribe.setSubscribeChannel(video.author.url);
|
||||
setDescription(video.description.fixHtmlLinks());
|
||||
_creatorThumbnail.setThumbnail(video.author.thumbnail, false);
|
||||
|
||||
|
||||
val cachedPolycentricProfile =
|
||||
PolycentricCache.instance.getCachedProfile(video.author.url, true);
|
||||
if (cachedPolycentricProfile != null) {
|
||||
setPolycentricProfile(cachedPolycentricProfile, animate = false);
|
||||
} else {
|
||||
setPolycentricProfile(null, animate = false);
|
||||
_taskLoadPolycentricProfile.run(video.author.id);
|
||||
}
|
||||
setPolycentricProfile(null, animate = false);
|
||||
_taskLoadPolycentricProfile.run(video.author.id);
|
||||
|
||||
_platform.setPlatformFromClientID(video.id.pluginId);
|
||||
val subTitleSegments: ArrayList<String> = ArrayList();
|
||||
|
@ -1485,7 +1466,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val queryReferencesResponse = ApiMethods.getQueryReferences(
|
||||
PolycentricCache.SERVER, ref, null, null,
|
||||
ApiMethods.SERVER, ref, null, null,
|
||||
arrayListOf(
|
||||
Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder()
|
||||
.setFromType(ContentType.OPINION.value).setValue(
|
||||
|
@ -1501,10 +1482,8 @@ class VideoDetailView : ConstraintLayout {
|
|||
|
||||
val likes = queryReferencesResponse.countsList[0];
|
||||
val dislikes = queryReferencesResponse.countsList[1];
|
||||
val hasLiked =
|
||||
StatePolycentric.instance.hasLiked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasLiked(it) } ?: false*/;
|
||||
val hasDisliked =
|
||||
StatePolycentric.instance.hasDisliked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasDisliked(it) } ?: false*/;
|
||||
val hasLiked = StatePolycentric.instance.hasLiked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasLiked(it) } ?: false*/;
|
||||
val hasDisliked = StatePolycentric.instance.hasDisliked(ref.toByteArray())/* || extraBytesRef?.let { StatePolycentric.instance.hasDisliked(it) } ?: false*/;
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
_rating.visibility = View.VISIBLE;
|
||||
|
@ -2805,13 +2784,12 @@ class VideoDetailView : ConstraintLayout {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setPolycentricProfile(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||
_polycentricProfile = cachedPolycentricProfile;
|
||||
private fun setPolycentricProfile(profile: PolycentricProfile?, animate: Boolean) {
|
||||
_polycentricProfile = profile
|
||||
|
||||
val dp_35 = 35.dp(context.resources)
|
||||
val profile = cachedPolycentricProfile?.profile;
|
||||
val avatar = profile?.systemState?.avatar?.selectBestImage(dp_35 * dp_35)
|
||||
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
|
||||
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) }
|
||||
|
||||
if (avatar != null) {
|
||||
_creatorThumbnail.setThumbnail(avatar, animate);
|
||||
|
@ -2820,12 +2798,12 @@ class VideoDetailView : ConstraintLayout {
|
|||
_creatorThumbnail.setHarborAvailable(profile != null, animate, profile?.system?.toProto());
|
||||
}
|
||||
|
||||
val username = cachedPolycentricProfile?.profile?.systemState?.username
|
||||
val username = profile?.systemState?.username
|
||||
if (username != null) {
|
||||
_channelName.text = username
|
||||
}
|
||||
|
||||
_monetization.setPolycentricProfile(cachedPolycentricProfile);
|
||||
_monetization.setPolycentricProfile(profile);
|
||||
}
|
||||
|
||||
fun setProgressBarOverlayed(isOverlayed: Boolean?) {
|
||||
|
@ -3013,7 +2991,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
Logger.w(TAG, "Failed to load recommendations.", it);
|
||||
};
|
||||
|
||||
private val _taskLoadPolycentricProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(StateApp.instance.scopeGetter, { PolycentricCache.instance.getProfileAsync(it) })
|
||||
private val _taskLoadPolycentricProfile = TaskHandler<PlatformID, PolycentricProfile?>(StateApp.instance.scopeGetter, { ApiMethods.getPolycentricProfileByClaim(ApiMethods.SERVER, ApiMethods.FUTO_TRUST_ROOT, it.claimFieldType.toLong(), it.claimType.toLong(), it.value!!) })
|
||||
.success { it -> setPolycentricProfile(it, animate = true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load claims.", it);
|
||||
|
|
|
@ -14,9 +14,9 @@ import com.futo.platformplayer.R
|
|||
import com.futo.platformplayer.api.media.IPlatformClient
|
||||
import com.futo.platformplayer.api.media.models.PlatformAuthorLink
|
||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.models.Playlist
|
||||
import com.futo.platformplayer.views.casting.CastButton
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
|
||||
class NavigationTopBarFragment : TopFragment() {
|
||||
private var _buttonBack: ImageButton? = null;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.futo.platformplayer.images;
|
||||
|
||||
import static com.futo.platformplayer.Extensions_PolycentricKt.getDataLinkFromUrl;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -12,10 +14,14 @@ import com.bumptech.glide.load.model.ModelLoader;
|
|||
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
||||
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
|
||||
import com.bumptech.glide.signature.ObjectKey;
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache;
|
||||
import com.futo.polycentric.core.ApiMethods;
|
||||
|
||||
import kotlin.Unit;
|
||||
import kotlinx.coroutines.CoroutineScopeKt;
|
||||
import kotlinx.coroutines.Deferred;
|
||||
import kotlinx.coroutines.Dispatchers;
|
||||
import userpackage.Protocol;
|
||||
|
||||
import java.lang.Exception;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CancellationException;
|
||||
|
@ -60,7 +66,14 @@ public class PolycentricModelLoader implements ModelLoader<String, ByteBuffer> {
|
|||
@Override
|
||||
public void loadData(@NonNull Priority priority, @NonNull DataFetcher.DataCallback<? super ByteBuffer> callback) {
|
||||
Log.i("PolycentricModelLoader", this._model);
|
||||
_deferred = PolycentricCache.getInstance().getDataAsync(_model);
|
||||
|
||||
Protocol.URLInfoDataLink dataLink = getDataLinkFromUrl(_model);
|
||||
if (dataLink == null) {
|
||||
callback.onLoadFailed(new Exception("Data link cannot be null"));
|
||||
return;
|
||||
}
|
||||
|
||||
_deferred = ApiMethods.Companion.getDataFromServerAndReassemble(CoroutineScopeKt.CoroutineScope(Dispatchers.getIO()), dataLink);
|
||||
_deferred.invokeOnCompletion(throwable -> {
|
||||
if (throwable != null) {
|
||||
Log.e("PolycentricModelLoader", "getDataAsync failed throwable: " + throwable.toString());
|
||||
|
|
|
@ -1,353 +0,0 @@
|
|||
package com.futo.platformplayer.polycentric
|
||||
|
||||
import com.futo.platformplayer.api.media.PlatformID
|
||||
import com.futo.platformplayer.constructs.BatchedTaskHandler
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.getNowDiffSeconds
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.resolveChannelUrls
|
||||
import com.futo.platformplayer.serializers.OffsetDateTimeSerializer
|
||||
import com.futo.platformplayer.states.StatePolycentric
|
||||
import com.futo.platformplayer.stores.CachedPolycentricProfileStorage
|
||||
import com.futo.platformplayer.stores.FragmentedStorage
|
||||
import com.futo.polycentric.core.ApiMethods
|
||||
import com.futo.polycentric.core.ContentType
|
||||
import com.futo.polycentric.core.OwnedClaim
|
||||
import com.futo.polycentric.core.PublicKey
|
||||
import com.futo.polycentric.core.SignedEvent
|
||||
import com.futo.polycentric.core.StorageTypeSystemState
|
||||
import com.futo.polycentric.core.SystemState
|
||||
import com.futo.polycentric.core.base64ToByteArray
|
||||
import com.futo.polycentric.core.base64UrlToByteArray
|
||||
import com.futo.polycentric.core.getClaimIfValid
|
||||
import com.futo.polycentric.core.getValidClaims
|
||||
import com.google.protobuf.ByteString
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.serialization.Serializable
|
||||
import userpackage.Protocol
|
||||
import java.nio.ByteBuffer
|
||||
import java.time.OffsetDateTime
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
class PolycentricCache {
|
||||
data class CachedOwnedClaims(val ownedClaims: List<OwnedClaim>?, val creationTime: OffsetDateTime = OffsetDateTime.now()) {
|
||||
val expired get() = creationTime.getNowDiffSeconds() > CACHE_EXPIRATION_SECONDS
|
||||
}
|
||||
@Serializable
|
||||
data class CachedPolycentricProfile(val profile: PolycentricProfile?, @Serializable(with = OffsetDateTimeSerializer::class) val creationTime: OffsetDateTime = OffsetDateTime.now()) {
|
||||
val expired get() = creationTime.getNowDiffSeconds() > CACHE_EXPIRATION_SECONDS
|
||||
}
|
||||
|
||||
private val _cache = hashMapOf<PlatformID, CachedOwnedClaims>()
|
||||
private val _profileCache = hashMapOf<PublicKey, CachedPolycentricProfile>()
|
||||
private val _profileUrlCache: CachedPolycentricProfileStorage;
|
||||
private val _scope = CoroutineScope(Dispatchers.IO);
|
||||
init {
|
||||
Logger.i(TAG, "Initializing Polycentric cache");
|
||||
val time = measureTimeMillis {
|
||||
_profileUrlCache = FragmentedStorage.get<CachedPolycentricProfileStorage>("profileUrlCache")
|
||||
}
|
||||
Logger.i(TAG, "Initialized Polycentric cache (${_profileUrlCache.map.size}, ${time}ms)");
|
||||
}
|
||||
|
||||
private val _taskGetProfile = BatchedTaskHandler<PublicKey, CachedPolycentricProfile>(_scope,
|
||||
{ system ->
|
||||
val signedEventsList = ApiMethods.getQueryLatest(
|
||||
SERVER,
|
||||
system.toProto(),
|
||||
listOf(
|
||||
ContentType.BANNER.value,
|
||||
ContentType.AVATAR.value,
|
||||
ContentType.USERNAME.value,
|
||||
ContentType.DESCRIPTION.value,
|
||||
ContentType.STORE.value,
|
||||
ContentType.SERVER.value,
|
||||
ContentType.STORE_DATA.value,
|
||||
ContentType.PROMOTION_BANNER.value,
|
||||
ContentType.PROMOTION.value,
|
||||
ContentType.MEMBERSHIP_URLS.value,
|
||||
ContentType.DONATION_DESTINATIONS.value
|
||||
)
|
||||
).eventsList.map { e -> SignedEvent.fromProto(e) };
|
||||
|
||||
val signedProfileEvents = signedEventsList.groupBy { e -> e.event.contentType }
|
||||
.map { (_, events) -> events.maxBy { it.event.unixMilliseconds ?: 0 } };
|
||||
|
||||
val storageSystemState = StorageTypeSystemState.create()
|
||||
for (signedEvent in signedProfileEvents) {
|
||||
storageSystemState.update(signedEvent.event)
|
||||
}
|
||||
|
||||
val signedClaimEvents = ApiMethods.getQueryIndex(
|
||||
SERVER,
|
||||
system.toProto(),
|
||||
ContentType.CLAIM.value,
|
||||
limit = 200
|
||||
).eventsList.map { e -> SignedEvent.fromProto(e) };
|
||||
|
||||
val ownedClaims: ArrayList<OwnedClaim> = arrayListOf()
|
||||
for (signedEvent in signedClaimEvents) {
|
||||
if (signedEvent.event.contentType != ContentType.CLAIM.value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
val response = ApiMethods.getQueryReferences(
|
||||
SERVER,
|
||||
Protocol.Reference.newBuilder()
|
||||
.setReference(signedEvent.toPointer().toProto().toByteString())
|
||||
.setReferenceType(2)
|
||||
.build(),
|
||||
null,
|
||||
Protocol.QueryReferencesRequestEvents.newBuilder()
|
||||
.setFromType(ContentType.VOUCH.value)
|
||||
.build()
|
||||
);
|
||||
|
||||
val ownedClaim = response.itemsList.map { SignedEvent.fromProto(it.event) }.getClaimIfValid(signedEvent);
|
||||
if (ownedClaim != null) {
|
||||
ownedClaims.add(ownedClaim);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.i(TAG, "Retrieved profile (ownedClaims = $ownedClaims)");
|
||||
val systemState = SystemState.fromStorageTypeSystemState(storageSystemState);
|
||||
return@BatchedTaskHandler CachedPolycentricProfile(PolycentricProfile(system, systemState, ownedClaims));
|
||||
},
|
||||
{ system -> return@BatchedTaskHandler getCachedProfile(system); },
|
||||
{ system, result ->
|
||||
synchronized(_cache) {
|
||||
_profileCache[system] = result;
|
||||
|
||||
if (result.profile != null) {
|
||||
for (claim in result.profile.ownedClaims) {
|
||||
val urls = claim.claim.resolveChannelUrls();
|
||||
for (url in urls)
|
||||
_profileUrlCache.map[url] = result;
|
||||
}
|
||||
}
|
||||
|
||||
_profileUrlCache.save();
|
||||
}
|
||||
});
|
||||
|
||||
private val _batchTaskGetClaims = BatchedTaskHandler<PlatformID, CachedOwnedClaims>(_scope,
|
||||
{ id ->
|
||||
val resolved = if (id.claimFieldType == -1) ApiMethods.getResolveClaim(SERVER, system, id.claimType.toLong(), id.value!!)
|
||||
else ApiMethods.getResolveClaim(SERVER, system, id.claimType.toLong(), id.claimFieldType.toLong(), id.value!!);
|
||||
Logger.v(TAG, "getResolveClaim(url = $SERVER, system = $system, id = $id, claimType = ${id.claimType}, matchAnyField = ${id.value})");
|
||||
val protoEvents = resolved.matchesList.flatMap { arrayListOf(it.claim).apply { addAll(it.proofChainList) } }
|
||||
val resolvedEvents = protoEvents.map { i -> SignedEvent.fromProto(i) };
|
||||
return@BatchedTaskHandler CachedOwnedClaims(resolvedEvents.getValidClaims());
|
||||
},
|
||||
{ id -> return@BatchedTaskHandler getCachedValidClaims(id); },
|
||||
{ id, result ->
|
||||
synchronized(_cache) {
|
||||
_cache[id] = result;
|
||||
}
|
||||
});
|
||||
|
||||
private val _batchTaskGetData = BatchedTaskHandler<String, ByteBuffer>(_scope,
|
||||
{
|
||||
val dataLink = getDataLinkFromUrl(it) ?: throw Exception("Only URLInfoDataLink is supported");
|
||||
return@BatchedTaskHandler ApiMethods.getDataFromServerAndReassemble(dataLink);
|
||||
},
|
||||
{ return@BatchedTaskHandler null },
|
||||
{ _, _ -> });
|
||||
|
||||
fun getCachedValidClaims(id: PlatformID, ignoreExpired: Boolean = false): CachedOwnedClaims? {
|
||||
if (!StatePolycentric.instance.enabled || id.claimType <= 0) {
|
||||
return CachedOwnedClaims(null);
|
||||
}
|
||||
|
||||
synchronized(_cache) {
|
||||
val cached = _cache[id]
|
||||
if (cached == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!ignoreExpired && cached.expired) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Review all return null in this file, perhaps it should be CachedX(null) instead
|
||||
fun getValidClaimsAsync(id: PlatformID): Deferred<CachedOwnedClaims> {
|
||||
if (!StatePolycentric.instance.enabled || id.value == null || id.claimType <= 0) {
|
||||
return _scope.async { CachedOwnedClaims(null) };
|
||||
}
|
||||
|
||||
Logger.v(TAG, "getValidClaims (id: $id)")
|
||||
val def = _batchTaskGetClaims.execute(id);
|
||||
def.invokeOnCompletion {
|
||||
if (it == null) {
|
||||
return@invokeOnCompletion
|
||||
}
|
||||
|
||||
handleException(it, handleNetworkException = { /* Do nothing (do not cache) */ }, handleOtherException = {
|
||||
//Cache failed result
|
||||
synchronized(_cache) {
|
||||
_cache[id] = CachedOwnedClaims(null);
|
||||
}
|
||||
})
|
||||
};
|
||||
return def;
|
||||
}
|
||||
|
||||
fun getDataAsync(url: String): Deferred<ByteBuffer> {
|
||||
StatePolycentric.instance.ensureEnabled()
|
||||
return _batchTaskGetData.execute(url);
|
||||
}
|
||||
|
||||
fun getCachedProfile(url: String, ignoreExpired: Boolean = false): CachedPolycentricProfile? {
|
||||
if (!StatePolycentric.instance.enabled) {
|
||||
return CachedPolycentricProfile(null)
|
||||
}
|
||||
|
||||
synchronized (_profileCache) {
|
||||
val cached = _profileUrlCache.get(url) ?: return null;
|
||||
if (!ignoreExpired && cached.expired) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
fun getCachedProfile(system: PublicKey, ignoreExpired: Boolean = false): CachedPolycentricProfile? {
|
||||
if (!StatePolycentric.instance.enabled) {
|
||||
return CachedPolycentricProfile(null)
|
||||
}
|
||||
|
||||
synchronized(_profileCache) {
|
||||
val cached = _profileCache[system] ?: return null;
|
||||
if (!ignoreExpired && cached.expired) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getProfileAsync(id: PlatformID, urlNullCache: String? = null): CachedPolycentricProfile? {
|
||||
if (!StatePolycentric.instance.enabled || id.claimType <= 0) {
|
||||
return CachedPolycentricProfile(null);
|
||||
}
|
||||
|
||||
val cachedClaims = getCachedValidClaims(id);
|
||||
if (cachedClaims != null) {
|
||||
if (!cachedClaims.ownedClaims.isNullOrEmpty()) {
|
||||
Logger.v(TAG, "getProfileAsync (id: $id) != null (with cached valid claims)")
|
||||
return getProfileAsync(cachedClaims.ownedClaims.first().system).await();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
Logger.v(TAG, "getProfileAsync (id: $id) no cached valid claims, will be retrieved")
|
||||
|
||||
val claims = getValidClaimsAsync(id).await()
|
||||
if (!claims.ownedClaims.isNullOrEmpty()) {
|
||||
Logger.v(TAG, "getProfileAsync (id: $id) != null (with retrieved valid claims)")
|
||||
return getProfileAsync(claims.ownedClaims.first().system).await()
|
||||
} else {
|
||||
synchronized (_cache) {
|
||||
if (urlNullCache != null) {
|
||||
_profileUrlCache.setAndSave(urlNullCache, CachedPolycentricProfile(null))
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getProfileAsync(system: PublicKey): Deferred<CachedPolycentricProfile?> {
|
||||
if (!StatePolycentric.instance.enabled) {
|
||||
return _scope.async { CachedPolycentricProfile(null) };
|
||||
}
|
||||
|
||||
Logger.i(TAG, "getProfileAsync (system: ${system})")
|
||||
val def = _taskGetProfile.execute(system);
|
||||
def.invokeOnCompletion {
|
||||
if (it == null) {
|
||||
return@invokeOnCompletion
|
||||
}
|
||||
|
||||
handleException(it, handleNetworkException = { /* Do nothing (do not cache) */ }, handleOtherException = {
|
||||
//Cache failed result
|
||||
synchronized(_cache) {
|
||||
val cachedProfile = CachedPolycentricProfile(null);
|
||||
_profileCache[system] = cachedProfile;
|
||||
}
|
||||
})
|
||||
};
|
||||
return def;
|
||||
}
|
||||
|
||||
private fun handleException(e: Throwable, handleNetworkException: () -> Unit, handleOtherException: () -> Unit) {
|
||||
val isNetworkException = when(e) {
|
||||
is java.net.UnknownHostException,
|
||||
is java.net.SocketTimeoutException,
|
||||
is java.net.ConnectException -> true
|
||||
else -> when(e.cause) {
|
||||
is java.net.UnknownHostException,
|
||||
is java.net.SocketTimeoutException,
|
||||
is java.net.ConnectException -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
if (isNetworkException) {
|
||||
handleNetworkException()
|
||||
} else {
|
||||
handleOtherException()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val system = Protocol.PublicKey.newBuilder()
|
||||
.setKeyType(1)
|
||||
.setKey(ByteString.copyFrom("gX0eCWctTm6WHVGot4sMAh7NDAIwWsIM5tRsOz9dX04=".base64ToByteArray())) //Production key
|
||||
//.setKey(ByteString.copyFrom("LeQkzn1j625YZcZHayfCmTX+6ptrzsA+CdAyq+BcEdQ".base64ToByteArray())) //Test key koen-futo
|
||||
.build();
|
||||
|
||||
private const val TAG = "PolycentricCache"
|
||||
const val SERVER = "https://srv1-prod.polycentric.io"
|
||||
private var _instance: PolycentricCache? = null;
|
||||
private val CACHE_EXPIRATION_SECONDS = 60 * 5;
|
||||
|
||||
@JvmStatic
|
||||
val instance: PolycentricCache
|
||||
get(){
|
||||
if(_instance == null)
|
||||
_instance = PolycentricCache();
|
||||
return _instance!!;
|
||||
};
|
||||
|
||||
fun finish() {
|
||||
_instance?.let {
|
||||
_instance = null;
|
||||
it._scope.cancel("PolycentricCache finished");
|
||||
}
|
||||
}
|
||||
|
||||
fun getDataLinkFromUrl(it: String): Protocol.URLInfoDataLink? {
|
||||
val urlData = if (it.startsWith("polycentric://")) {
|
||||
it.substring("polycentric://".length)
|
||||
} else it;
|
||||
|
||||
val urlBytes = urlData.base64UrlToByteArray();
|
||||
val urlInfo = Protocol.URLInfo.parseFrom(urlBytes);
|
||||
if (urlInfo.urlType != 4L) {
|
||||
return null
|
||||
}
|
||||
|
||||
val dataLink = Protocol.URLInfoDataLink.parseFrom(urlInfo.body);
|
||||
return dataLink
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import com.futo.platformplayer.api.media.structures.DedupContentPager
|
|||
import com.futo.platformplayer.api.media.structures.IPager
|
||||
import com.futo.platformplayer.api.media.structures.MultiChronoContentPager
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.resolveChannelUrl
|
||||
import com.futo.platformplayer.serializers.PlatformContentSerializer
|
||||
import com.futo.platformplayer.stores.db.ManagedDBStore
|
||||
|
@ -50,14 +49,7 @@ class StateCache {
|
|||
val subs = StateSubscriptions.instance.getSubscriptions();
|
||||
Logger.i(TAG, "Subscriptions CachePager polycentric urls");
|
||||
val allUrls = subs
|
||||
.map {
|
||||
val otherUrls = PolycentricCache.instance.getCachedProfile(it.channel.url)?.profile?.ownedClaims?.mapNotNull { c -> c.claim.resolveChannelUrl() } ?: listOf();
|
||||
if(!otherUrls.contains(it.channel.url))
|
||||
return@map listOf(listOf(it.channel.url), otherUrls).flatten();
|
||||
else
|
||||
return@map otherUrls;
|
||||
}
|
||||
.flatten()
|
||||
.map { it.channel.url }
|
||||
.distinct()
|
||||
.filter { StatePlatform.instance.hasEnabledChannelClient(it) };
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ class StateMeta {
|
|||
return when(lastCommentSection.value){
|
||||
"Polycentric" -> 0;
|
||||
"Platform" -> 1;
|
||||
else -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
fun setLastCommentSection(value: Int) {
|
||||
|
|
|
@ -21,9 +21,7 @@ import com.futo.platformplayer.api.media.structures.IPager
|
|||
import com.futo.platformplayer.api.media.structures.MultiChronoContentPager
|
||||
import com.futo.platformplayer.awaitFirstDeferred
|
||||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.polycentric.PolycentricStorage
|
||||
import com.futo.platformplayer.resolveChannelUrl
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
|
@ -33,6 +31,7 @@ import com.futo.polycentric.core.ApiMethods
|
|||
import com.futo.polycentric.core.ClaimType
|
||||
import com.futo.polycentric.core.ContentType
|
||||
import com.futo.polycentric.core.Opinion
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
import com.futo.polycentric.core.ProcessHandle
|
||||
import com.futo.polycentric.core.PublicKey
|
||||
import com.futo.polycentric.core.SignedEvent
|
||||
|
@ -234,34 +233,7 @@ class StatePolycentric {
|
|||
if (!enabled) {
|
||||
return Pair(false, listOf(url));
|
||||
}
|
||||
var polycentricProfile: PolycentricProfile? = null;
|
||||
try {
|
||||
val polycentricCached = PolycentricCache.instance.getCachedProfile(url, cacheOnly)
|
||||
polycentricProfile = polycentricCached?.profile;
|
||||
if (polycentricCached == null && channelId != null) {
|
||||
Logger.i("StateSubscriptions", "Get polycentric profile not cached");
|
||||
if(!cacheOnly) {
|
||||
polycentricProfile = runBlocking { PolycentricCache.instance.getProfileAsync(channelId, if(doCacheNull) url else null) }?.profile;
|
||||
didUpdate = true;
|
||||
}
|
||||
} else {
|
||||
Logger.i("StateSubscriptions", "Get polycentric profile cached");
|
||||
}
|
||||
}
|
||||
catch(ex: Throwable) {
|
||||
Logger.w(StateSubscriptions.TAG, "Polycentric getCachedProfile failed for subscriptions", ex);
|
||||
//TODO: Some way to communicate polycentric failing without blocking here
|
||||
}
|
||||
if(polycentricProfile != null) {
|
||||
val urls = polycentricProfile.ownedClaims.groupBy { it.claim.claimType }
|
||||
.mapNotNull { it.value.firstOrNull()?.claim?.resolveChannelUrl() }.toMutableList();
|
||||
if(urls.any { it.equals(url, true) })
|
||||
return Pair(didUpdate, urls);
|
||||
else
|
||||
return Pair(didUpdate, listOf(url) + urls);
|
||||
}
|
||||
else
|
||||
return Pair(didUpdate, listOf(url));
|
||||
return Pair(didUpdate, listOf(url));
|
||||
}
|
||||
|
||||
fun getChannelContent(scope: CoroutineScope, profile: PolycentricProfile, isSubscriptionOptimized: Boolean = false, channelConcurrency: Int = -1): IPager<IPlatformContent>? {
|
||||
|
@ -325,7 +297,7 @@ class StatePolycentric {
|
|||
id = PlatformID("polycentric", author, null, ClaimType.POLYCENTRIC.value.toInt()),
|
||||
name = systemState.username,
|
||||
url = author,
|
||||
thumbnail = systemState.avatar?.selectBestImage(dp_25 * dp_25)?.let { img -> img.toURLInfoSystemLinkUrl(system.toProto(), img.process, listOf(PolycentricCache.SERVER)) },
|
||||
thumbnail = systemState.avatar?.selectBestImage(dp_25 * dp_25)?.let { img -> img.toURLInfoSystemLinkUrl(system.toProto(), img.process, listOf(ApiMethods.SERVER)) },
|
||||
subscribers = null
|
||||
),
|
||||
msg = if (post.content.count() > PolycentricPlatformComment.MAX_COMMENT_SIZE) post.content.substring(0, PolycentricPlatformComment.MAX_COMMENT_SIZE) else post.content,
|
||||
|
@ -349,7 +321,7 @@ class StatePolycentric {
|
|||
suspend fun getLikesDislikesReplies(reference: Protocol.Reference): LikesDislikesReplies {
|
||||
ensureEnabled()
|
||||
|
||||
val response = ApiMethods.getQueryReferences(PolycentricCache.SERVER, reference, null,
|
||||
val response = ApiMethods.getQueryReferences(ApiMethods.SERVER, reference, null,
|
||||
null,
|
||||
listOf(
|
||||
Protocol.QueryReferencesRequestCountLWWElementReferences.newBuilder()
|
||||
|
@ -382,7 +354,7 @@ class StatePolycentric {
|
|||
}
|
||||
|
||||
val pointer = Protocol.Pointer.parseFrom(reference.reference)
|
||||
val events = ApiMethods.getEvents(PolycentricCache.SERVER, pointer.system, Protocol.RangesForSystem.newBuilder()
|
||||
val events = ApiMethods.getEvents(ApiMethods.SERVER, pointer.system, Protocol.RangesForSystem.newBuilder()
|
||||
.addRangesForProcesses(Protocol.RangesForProcess.newBuilder()
|
||||
.setProcess(pointer.process)
|
||||
.addRanges(Protocol.Range.newBuilder()
|
||||
|
@ -400,11 +372,11 @@ class StatePolycentric {
|
|||
}
|
||||
|
||||
val post = Protocol.Post.parseFrom(ev.content);
|
||||
val systemLinkUrl = ev.system.systemToURLInfoSystemLinkUrl(listOf(PolycentricCache.SERVER));
|
||||
val systemLinkUrl = ev.system.systemToURLInfoSystemLinkUrl(listOf(ApiMethods.SERVER));
|
||||
val dp_25 = 25.dp(StateApp.instance.context.resources)
|
||||
|
||||
val profileEvents = ApiMethods.getQueryLatest(
|
||||
PolycentricCache.SERVER,
|
||||
ApiMethods.SERVER,
|
||||
ev.system.toProto(),
|
||||
listOf(
|
||||
ContentType.AVATAR.value,
|
||||
|
@ -433,7 +405,7 @@ class StatePolycentric {
|
|||
id = PlatformID("polycentric", systemLinkUrl, null, ClaimType.POLYCENTRIC.value.toInt()),
|
||||
name = nameEvent?.event?.lwwElement?.value?.decodeToString() ?: "Unknown",
|
||||
url = systemLinkUrl,
|
||||
thumbnail = imageBundle?.selectBestImage(dp_25 * dp_25)?.let { img -> img.toURLInfoSystemLinkUrl(ev.system.toProto(), img.process, listOf(PolycentricCache.SERVER)) },
|
||||
thumbnail = imageBundle?.selectBestImage(dp_25 * dp_25)?.let { img -> img.toURLInfoSystemLinkUrl(ev.system.toProto(), img.process, listOf(ApiMethods.SERVER)) },
|
||||
subscribers = null
|
||||
),
|
||||
msg = if (post.content.count() > PolycentricPlatformComment.MAX_COMMENT_SIZE) post.content.substring(0, PolycentricPlatformComment.MAX_COMMENT_SIZE) else post.content,
|
||||
|
@ -445,12 +417,12 @@ class StatePolycentric {
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun getCommentPager(contextUrl: String, reference: Protocol.Reference, extraByteReferences: List<ByteArray>? = null): IPager<IPlatformComment> {
|
||||
suspend fun getCommentPager(contextUrl: String, reference: Reference, extraByteReferences: List<ByteArray>? = null): IPager<IPlatformComment> {
|
||||
if (!enabled) {
|
||||
return EmptyPager()
|
||||
}
|
||||
|
||||
val response = ApiMethods.getQueryReferences(PolycentricCache.SERVER, reference, null,
|
||||
val response = ApiMethods.getQueryReferences(ApiMethods.SERVER, reference, null,
|
||||
Protocol.QueryReferencesRequestEvents.newBuilder()
|
||||
.setFromType(ContentType.POST.value)
|
||||
.addAllCountLwwElementReferences(arrayListOf(
|
||||
|
@ -486,7 +458,7 @@ class StatePolycentric {
|
|||
}
|
||||
|
||||
override suspend fun nextPageAsync() {
|
||||
val nextPageResponse = ApiMethods.getQueryReferences(PolycentricCache.SERVER, reference, _cursor,
|
||||
val nextPageResponse = ApiMethods.getQueryReferences(ApiMethods.SERVER, reference, _cursor,
|
||||
Protocol.QueryReferencesRequestEvents.newBuilder()
|
||||
.setFromType(ContentType.POST.value)
|
||||
.addAllCountLwwElementReferences(arrayListOf(
|
||||
|
@ -534,7 +506,7 @@ class StatePolycentric {
|
|||
return@mapNotNull LazyComment(scope.async(_commentPoolDispatcher){
|
||||
Logger.i(TAG, "Fetching comment data for [" + ev.system.key.toBase64() + "]");
|
||||
val profileEvents = ApiMethods.getQueryLatest(
|
||||
PolycentricCache.SERVER,
|
||||
ApiMethods.SERVER,
|
||||
ev.system.toProto(),
|
||||
listOf(
|
||||
ContentType.AVATAR.value,
|
||||
|
@ -558,7 +530,7 @@ class StatePolycentric {
|
|||
|
||||
val unixMilliseconds = ev.unixMilliseconds
|
||||
//TODO: Don't use single hardcoded sderver here
|
||||
val systemLinkUrl = ev.system.systemToURLInfoSystemLinkUrl(listOf(PolycentricCache.SERVER));
|
||||
val systemLinkUrl = ev.system.systemToURLInfoSystemLinkUrl(listOf(ApiMethods.SERVER));
|
||||
val dp_25 = 25.dp(StateApp.instance.context.resources)
|
||||
return@async PolycentricPlatformComment(
|
||||
contextUrl = contextUrl,
|
||||
|
@ -566,7 +538,7 @@ class StatePolycentric {
|
|||
id = PlatformID("polycentric", systemLinkUrl, null, ClaimType.POLYCENTRIC.value.toInt()),
|
||||
name = nameEvent?.event?.lwwElement?.value?.decodeToString() ?: "Unknown",
|
||||
url = systemLinkUrl,
|
||||
thumbnail = imageBundle?.selectBestImage(dp_25 * dp_25)?.let { img -> img.toURLInfoSystemLinkUrl(ev.system.toProto(), img.process, listOf(PolycentricCache.SERVER)) },
|
||||
thumbnail = imageBundle?.selectBestImage(dp_25 * dp_25)?.let { img -> img.toURLInfoSystemLinkUrl(ev.system.toProto(), img.process, listOf(ApiMethods.SERVER)) },
|
||||
subscribers = null
|
||||
),
|
||||
msg = if (post.content.count() > PolycentricPlatformComment.MAX_COMMENT_SIZE) post.content.substring(0, PolycentricPlatformComment.MAX_COMMENT_SIZE) else post.content,
|
||||
|
|
|
@ -1,54 +1,17 @@
|
|||
package com.futo.platformplayer.states
|
||||
|
||||
import com.futo.platformplayer.Settings
|
||||
import com.futo.platformplayer.UIDialogs
|
||||
import com.futo.platformplayer.api.media.models.ResultCapabilities
|
||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.api.media.models.channels.SerializedChannel
|
||||
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
||||
import com.futo.platformplayer.api.media.platforms.js.JSClient
|
||||
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
|
||||
import com.futo.platformplayer.api.media.structures.*
|
||||
import com.futo.platformplayer.api.media.structures.ReusablePager.Companion.asReusable
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.constructs.Event2
|
||||
import com.futo.platformplayer.engine.exceptions.PluginException
|
||||
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
|
||||
import com.futo.platformplayer.engine.exceptions.ScriptCriticalException
|
||||
import com.futo.platformplayer.exceptions.ChannelException
|
||||
import com.futo.platformplayer.findNonRuntimeException
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.getNowDiffDays
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.models.SubscriptionGroup
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.resolveChannelUrl
|
||||
import com.futo.platformplayer.states.StateHistory.Companion
|
||||
import com.futo.platformplayer.stores.FragmentedStorage
|
||||
import com.futo.platformplayer.stores.StringDateMapStorage
|
||||
import com.futo.platformplayer.stores.SubscriptionStorage
|
||||
import com.futo.platformplayer.stores.v2.ReconstructStore
|
||||
import com.futo.platformplayer.stores.v2.ManagedStore
|
||||
import com.futo.platformplayer.subscription.SubscriptionFetchAlgorithm
|
||||
import com.futo.platformplayer.subscription.SubscriptionFetchAlgorithms
|
||||
import com.futo.platformplayer.sync.internal.GJSyncOpcodes
|
||||
import com.futo.platformplayer.sync.models.SyncSubscriptionGroupsPackage
|
||||
import com.futo.platformplayer.sync.models.SyncSubscriptionsPackage
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
import java.util.concurrent.ForkJoinTask
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.streams.asSequence
|
||||
import kotlin.streams.toList
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
/***
|
||||
* Used to maintain subscription groups
|
||||
|
|
|
@ -15,7 +15,6 @@ import com.futo.platformplayer.logging.Logger
|
|||
import com.futo.platformplayer.models.ImportCache
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.models.SubscriptionGroup
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.resolveChannelUrl
|
||||
import com.futo.platformplayer.stores.FragmentedStorage
|
||||
import com.futo.platformplayer.stores.StringDateMapStorage
|
||||
|
@ -335,12 +334,6 @@ class StateSubscriptions {
|
|||
return true;
|
||||
}
|
||||
|
||||
//TODO: This causes issues, because what if the profile is not cached yet when the susbcribe button is loaded for example?
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(urls.first(), true)?.profile;
|
||||
if (cachedProfile != null) {
|
||||
return cachedProfile.ownedClaims.any { c -> _subscriptions.hasItem { s -> c.claim.resolveChannelUrl() == s.channel.url } };
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package com.futo.platformplayer.stores
|
||||
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
class CachedPolycentricProfileStorage : FragmentedStorageFileJson() {
|
||||
var map: HashMap<String, PolycentricCache.CachedPolycentricProfile> = hashMapOf();
|
||||
|
||||
override fun encode(): String {
|
||||
val encoded = Json.encodeToString(this);
|
||||
return encoded;
|
||||
}
|
||||
|
||||
fun get(key: String) : PolycentricCache.CachedPolycentricProfile? {
|
||||
return map[key];
|
||||
}
|
||||
|
||||
fun setAndSave(key: String, value: PolycentricCache.CachedPolycentricProfile) : PolycentricCache.CachedPolycentricProfile {
|
||||
map[key] = value;
|
||||
save();
|
||||
return value;
|
||||
}
|
||||
|
||||
fun setAndSaveBlocking(key: String, value: PolycentricCache.CachedPolycentricProfile) : PolycentricCache.CachedPolycentricProfile {
|
||||
map[key] = value;
|
||||
saveBlocking();
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -16,11 +16,11 @@ import com.futo.platformplayer.constructs.Event0
|
|||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
|
||||
import com.futo.platformplayer.views.adapters.viewholders.StoreItemViewHolder
|
||||
import com.futo.platformplayer.views.platform.PlatformIndicator
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
|
@ -125,8 +125,7 @@ class MonetizationView : LinearLayout {
|
|||
}
|
||||
}
|
||||
|
||||
fun setPolycentricProfile(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?) {
|
||||
val profile = cachedPolycentricProfile?.profile;
|
||||
fun setPolycentricProfile(profile: PolycentricProfile?) {
|
||||
if (profile != null) {
|
||||
if (profile.systemState.store.isNotEmpty()) {
|
||||
_buttonStore.visibility = View.VISIBLE;
|
||||
|
|
|
@ -14,10 +14,10 @@ import androidx.core.view.isVisible
|
|||
import androidx.core.view.size
|
||||
import com.bumptech.glide.Glide
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.views.buttons.BigButton
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
|
||||
import com.google.android.material.imageview.ShapeableImageView
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import com.futo.platformplayer.fragment.channel.tab.ChannelListFragment
|
|||
import com.futo.platformplayer.fragment.channel.tab.ChannelMonetizationFragment
|
||||
import com.futo.platformplayer.fragment.channel.tab.ChannelPlaylistsFragment
|
||||
import com.futo.platformplayer.fragment.channel.tab.IChannelTabFragment
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@ import com.futo.platformplayer.api.media.models.ratings.RatingLikeDislikes
|
|||
import com.futo.platformplayer.api.media.models.ratings.RatingLikes
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.fixHtmlLinks
|
||||
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.setPlatformPlayerLinkMovementMethod
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StatePolycentric
|
||||
|
@ -29,6 +27,7 @@ import com.futo.platformplayer.views.LoaderView
|
|||
import com.futo.platformplayer.views.others.CreatorThumbnail
|
||||
import com.futo.platformplayer.views.pills.PillButton
|
||||
import com.futo.platformplayer.views.pills.PillRatingLikesDislikes
|
||||
import com.futo.polycentric.core.ApiMethods
|
||||
import com.futo.polycentric.core.Opinion
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -81,24 +80,18 @@ class CommentViewHolder : ViewHolder {
|
|||
throw Exception("Not implemented for non polycentric comments")
|
||||
}
|
||||
|
||||
if (args.hasLiked) {
|
||||
args.processHandle.opinion(c.reference, Opinion.like);
|
||||
val newOpinion: Opinion = if (args.hasLiked) {
|
||||
Opinion.like
|
||||
} else if (args.hasDisliked) {
|
||||
args.processHandle.opinion(c.reference, Opinion.dislike);
|
||||
Opinion.dislike
|
||||
} else {
|
||||
args.processHandle.opinion(c.reference, Opinion.neutral);
|
||||
Opinion.neutral
|
||||
}
|
||||
|
||||
_layoutComment.alpha = if (args.dislikes > 2 && args.dislikes.toFloat() / (args.likes + args.dislikes).toFloat() >= 0.7f) 0.5f else 1.0f;
|
||||
|
||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||
try {
|
||||
Logger.i(TAG, "Started backfill");
|
||||
args.processHandle.fullyBackfillServersAnnounceExceptions();
|
||||
Logger.i(TAG, "Finished backfill");
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, "Failed to backfill servers.", e)
|
||||
}
|
||||
ApiMethods.setOpinion(args.processHandle, c.reference, newOpinion)
|
||||
}
|
||||
|
||||
StatePolycentric.instance.updateLikeMap(c.reference, args.hasLiked, args.hasDisliked)
|
||||
|
|
|
@ -16,7 +16,6 @@ import com.futo.platformplayer.api.media.models.ratings.RatingLikeDislikes
|
|||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.fixHtmlLinks
|
||||
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.setPlatformPlayerLinkMovementMethod
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
|
@ -26,6 +25,7 @@ import com.futo.platformplayer.views.others.CreatorThumbnail
|
|||
import com.futo.platformplayer.views.pills.PillButton
|
||||
import com.futo.platformplayer.views.pills.PillRatingLikesDislikes
|
||||
import com.futo.polycentric.core.Opinion
|
||||
import com.futo.polycentric.core.fullyBackfillServersAnnounceExceptions
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.IdentityHashMap
|
||||
|
|
|
@ -16,7 +16,6 @@ import com.futo.platformplayer.constructs.Event1
|
|||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.views.FeedStyle
|
||||
import com.futo.platformplayer.views.others.CreatorThumbnail
|
||||
|
@ -29,21 +28,12 @@ open class PlaylistView : LinearLayout {
|
|||
protected val _imageThumbnail: ImageView
|
||||
protected val _imageChannel: ImageView?
|
||||
protected val _creatorThumbnail: CreatorThumbnail?
|
||||
protected val _imageNeopassChannel: ImageView?;
|
||||
protected val _platformIndicator: PlatformIndicator;
|
||||
protected val _textPlaylistName: TextView
|
||||
protected val _textVideoCount: TextView
|
||||
protected val _textVideoCountLabel: TextView;
|
||||
protected val _textPlaylistItems: TextView
|
||||
protected val _textChannelName: TextView
|
||||
protected var _neopassAnimator: ObjectAnimator? = null;
|
||||
|
||||
private val _taskLoadValidClaims = TaskHandler<PlatformID, PolycentricCache.CachedOwnedClaims>(StateApp.instance.scopeGetter,
|
||||
{ PolycentricCache.instance.getValidClaimsAsync(it).await() })
|
||||
.success { it -> updateClaimsLayout(it, animate = true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load claims.", it);
|
||||
};
|
||||
|
||||
val onPlaylistClicked = Event1<IPlatformPlaylist>();
|
||||
val onChannelClicked = Event1<PlatformAuthorLink>();
|
||||
|
@ -66,7 +56,6 @@ open class PlaylistView : LinearLayout {
|
|||
_textVideoCountLabel = findViewById(R.id.text_video_count_label);
|
||||
_textChannelName = findViewById(R.id.text_channel_name);
|
||||
_textPlaylistItems = findViewById(R.id.text_playlist_items);
|
||||
_imageNeopassChannel = findViewById(R.id.image_neopass_channel);
|
||||
|
||||
setOnClickListener { onOpenClicked() };
|
||||
_imageChannel?.setOnClickListener { currentPlaylist?.let { onChannelClicked.emit(it.author) } };
|
||||
|
@ -88,20 +77,6 @@ open class PlaylistView : LinearLayout {
|
|||
|
||||
|
||||
open fun bind(content: IPlatformContent) {
|
||||
_taskLoadValidClaims.cancel();
|
||||
|
||||
if (content.author.id.claimType > 0) {
|
||||
val cachedClaims = PolycentricCache.instance.getCachedValidClaims(content.author.id);
|
||||
if (cachedClaims != null) {
|
||||
updateClaimsLayout(cachedClaims, animate = false);
|
||||
} else {
|
||||
updateClaimsLayout(null, animate = false);
|
||||
_taskLoadValidClaims.run(content.author.id);
|
||||
}
|
||||
} else {
|
||||
updateClaimsLayout(null, animate = false);
|
||||
}
|
||||
|
||||
isClickable = true;
|
||||
|
||||
_imageChannel?.let {
|
||||
|
@ -155,25 +130,6 @@ open class PlaylistView : LinearLayout {
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateClaimsLayout(claims: PolycentricCache.CachedOwnedClaims?, animate: Boolean) {
|
||||
_neopassAnimator?.cancel();
|
||||
_neopassAnimator = null;
|
||||
|
||||
val firstClaim = claims?.ownedClaims?.firstOrNull();
|
||||
val harborAvailable = firstClaim != null
|
||||
if (harborAvailable) {
|
||||
_imageNeopassChannel?.visibility = View.VISIBLE
|
||||
if (animate) {
|
||||
_neopassAnimator = ObjectAnimator.ofFloat(_imageNeopassChannel, "alpha", 0.0f, 1.0f).setDuration(500)
|
||||
_neopassAnimator?.start()
|
||||
}
|
||||
} else {
|
||||
_imageNeopassChannel?.visibility = View.GONE
|
||||
}
|
||||
|
||||
_creatorThumbnail?.setHarborAvailable(harborAvailable, animate, firstClaim?.system?.toProto())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "VideoPreviewViewHolder"
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import com.futo.platformplayer.constructs.TaskHandler
|
|||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.toHumanTimeIndicator
|
||||
|
@ -32,14 +31,6 @@ class SubscriptionViewHolder : ViewHolder {
|
|||
private val _platformIndicator : PlatformIndicator;
|
||||
private val _textMeta: TextView;
|
||||
|
||||
private val _taskLoadProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(
|
||||
StateApp.instance.scopeGetter,
|
||||
{ PolycentricCache.instance.getProfileAsync(it) })
|
||||
.success { it -> onProfileLoaded(null, it, true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load profile.", it);
|
||||
};
|
||||
|
||||
var subscription: Subscription? = null
|
||||
private set;
|
||||
|
||||
|
@ -74,45 +65,12 @@ class SubscriptionViewHolder : ViewHolder {
|
|||
}
|
||||
|
||||
fun bind(sub: Subscription) {
|
||||
_taskLoadProfile.cancel();
|
||||
|
||||
this.subscription = sub;
|
||||
|
||||
_creatorThumbnail.setThumbnail(sub.channel.thumbnail, false);
|
||||
_textName.text = sub.channel.name;
|
||||
bindViewMetrics(sub);
|
||||
_platformIndicator.setPlatformFromClientID(sub.channel.id.pluginId);
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(sub.channel.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(sub, cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(sub.channel.id);
|
||||
}
|
||||
} else {
|
||||
_taskLoadProfile.run(sub.channel.id);
|
||||
}
|
||||
}
|
||||
|
||||
private fun onProfileLoaded(sub: Subscription?, cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||
val dp_46 = 46.dp(itemView.context.resources);
|
||||
val profile = cachedPolycentricProfile?.profile;
|
||||
val avatar = profile?.systemState?.avatar?.selectBestImage(dp_46 * dp_46)
|
||||
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
|
||||
|
||||
if (avatar != null) {
|
||||
_creatorThumbnail.setThumbnail(avatar, animate);
|
||||
} else {
|
||||
_creatorThumbnail.setThumbnail(this.subscription?.channel?.thumbnail, animate);
|
||||
_creatorThumbnail.setHarborAvailable(profile != null, animate, profile?.system?.toProto());
|
||||
}
|
||||
|
||||
if (profile != null) {
|
||||
_textName.text = profile.systemState.username;
|
||||
}
|
||||
|
||||
if(sub != null)
|
||||
bindViewMetrics(sub)
|
||||
}
|
||||
|
||||
fun bindViewMetrics(sub: Subscription?) {
|
||||
|
|
|
@ -30,7 +30,6 @@ import com.futo.platformplayer.dp
|
|||
import com.futo.platformplayer.fixHtmlWhitespace
|
||||
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.toHumanNowDiffString
|
||||
import com.futo.platformplayer.views.FeedStyle
|
||||
|
@ -44,7 +43,6 @@ class PreviewPostView : LinearLayout {
|
|||
|
||||
private val _imageAuthorThumbnail: ImageView;
|
||||
private val _textAuthorName: TextView;
|
||||
private val _imageNeopassChannel: ImageView;
|
||||
private val _textMetadata: TextView;
|
||||
private val _textTitle: TextView;
|
||||
private val _textDescription: TextView;
|
||||
|
@ -64,15 +62,6 @@ class PreviewPostView : LinearLayout {
|
|||
private val _layoutComments: LinearLayout?;
|
||||
private val _textComments: TextView?;
|
||||
|
||||
private var _neopassAnimator: ObjectAnimator? = null;
|
||||
|
||||
private val _taskLoadValidClaims = TaskHandler<PlatformID, PolycentricCache.CachedOwnedClaims>(StateApp.instance.scopeGetter,
|
||||
{ PolycentricCache.instance.getValidClaimsAsync(it).await() })
|
||||
.success { it -> updateClaimsLayout(it, animate = true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load claims.", it);
|
||||
};
|
||||
|
||||
val content: IPlatformContent? get() = _content;
|
||||
|
||||
val onContentClicked = Event1<IPlatformContent>();
|
||||
|
@ -83,7 +72,6 @@ class PreviewPostView : LinearLayout {
|
|||
|
||||
_imageAuthorThumbnail = findViewById(R.id.image_author_thumbnail);
|
||||
_textAuthorName = findViewById(R.id.text_author_name);
|
||||
_imageNeopassChannel = findViewById(R.id.image_neopass_channel);
|
||||
_textMetadata = findViewById(R.id.text_metadata);
|
||||
_textTitle = findViewById(R.id.text_title);
|
||||
_textDescription = findViewById(R.id.text_description);
|
||||
|
@ -130,21 +118,8 @@ class PreviewPostView : LinearLayout {
|
|||
}
|
||||
|
||||
fun bind(content: IPlatformContent) {
|
||||
_taskLoadValidClaims.cancel();
|
||||
_content = content;
|
||||
|
||||
if (content.author.id.claimType > 0) {
|
||||
val cachedClaims = PolycentricCache.instance.getCachedValidClaims(content.author.id);
|
||||
if (cachedClaims != null) {
|
||||
updateClaimsLayout(cachedClaims, animate = false);
|
||||
} else {
|
||||
updateClaimsLayout(null, animate = false);
|
||||
_taskLoadValidClaims.run(content.author.id);
|
||||
}
|
||||
} else {
|
||||
updateClaimsLayout(null, animate = false);
|
||||
}
|
||||
|
||||
_textAuthorName.text = content.author.name;
|
||||
_textMetadata.text = content.datetime?.toHumanNowDiffString()?.let { "$it ago" } ?: "";
|
||||
|
||||
|
@ -292,25 +267,6 @@ class PreviewPostView : LinearLayout {
|
|||
};
|
||||
}
|
||||
|
||||
private fun updateClaimsLayout(claims: PolycentricCache.CachedOwnedClaims?, animate: Boolean) {
|
||||
_neopassAnimator?.cancel();
|
||||
_neopassAnimator = null;
|
||||
|
||||
val harborAvailable = claims != null && !claims.ownedClaims.isNullOrEmpty();
|
||||
if (harborAvailable) {
|
||||
_imageNeopassChannel.visibility = View.VISIBLE
|
||||
if (animate) {
|
||||
_neopassAnimator = ObjectAnimator.ofFloat(_imageNeopassChannel, "alpha", 0.0f, 1.0f).setDuration(500)
|
||||
_neopassAnimator?.start()
|
||||
}
|
||||
} else {
|
||||
_imageNeopassChannel.visibility = View.GONE
|
||||
}
|
||||
|
||||
//TODO: Necessary if we decide to use creator thumbnail with neopass indicator instead
|
||||
//_creatorThumbnail?.setHarborAvailable(harborAvailable, animate)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = "PreviewPostView";
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import com.futo.platformplayer.getNowDiffSeconds
|
|||
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
||||
import com.futo.platformplayer.images.GlideHelper.Companion.loadThumbnails
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StateDownloads
|
||||
|
@ -47,7 +46,6 @@ open class PreviewVideoView : LinearLayout {
|
|||
protected val _imageVideo: ImageView
|
||||
protected val _imageChannel: ImageView?
|
||||
protected val _creatorThumbnail: CreatorThumbnail?
|
||||
protected val _imageNeopassChannel: ImageView?;
|
||||
protected val _platformIndicator: PlatformIndicator;
|
||||
protected val _textVideoName: TextView
|
||||
protected val _textChannelName: TextView
|
||||
|
@ -57,7 +55,6 @@ open class PreviewVideoView : LinearLayout {
|
|||
protected var _playerVideoThumbnail: FutoThumbnailPlayer? = null;
|
||||
protected val _containerLive: LinearLayout;
|
||||
protected val _playerContainer: FrameLayout;
|
||||
protected var _neopassAnimator: ObjectAnimator? = null;
|
||||
protected val _layoutDownloaded: FrameLayout;
|
||||
|
||||
protected val _button_add_to_queue : View;
|
||||
|
@ -65,15 +62,6 @@ open class PreviewVideoView : LinearLayout {
|
|||
protected val _button_add_to : View;
|
||||
|
||||
protected val _exoPlayer: PlayerManager?;
|
||||
|
||||
private val _taskLoadProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(
|
||||
StateApp.instance.scopeGetter,
|
||||
{ PolycentricCache.instance.getProfileAsync(it) })
|
||||
.success { it -> onProfileLoaded(it, true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load profile.", it);
|
||||
};
|
||||
|
||||
private val _timeBar: ProgressBar?;
|
||||
|
||||
val onVideoClicked = Event2<IPlatformVideo, Long>();
|
||||
|
@ -108,7 +96,6 @@ open class PreviewVideoView : LinearLayout {
|
|||
_button_add_to_queue = findViewById(R.id.button_add_to_queue);
|
||||
_button_add_to_watch_later = findViewById(R.id.button_add_to_watch_later);
|
||||
_button_add_to = findViewById(R.id.button_add_to);
|
||||
_imageNeopassChannel = findViewById(R.id.image_neopass_channel);
|
||||
_layoutDownloaded = findViewById(R.id.layout_downloaded);
|
||||
_timeBar = findViewById(R.id.time_bar)
|
||||
|
||||
|
@ -160,15 +147,12 @@ open class PreviewVideoView : LinearLayout {
|
|||
|
||||
|
||||
open fun bind(content: IPlatformContent) {
|
||||
_taskLoadProfile.cancel();
|
||||
|
||||
isClickable = true;
|
||||
|
||||
val isPlanned = (content.datetime?.getNowDiffSeconds() ?: 0) < 0;
|
||||
|
||||
stopPreview();
|
||||
|
||||
_imageNeopassChannel?.visibility = View.GONE;
|
||||
_creatorThumbnail?.setThumbnail(content.author.thumbnail, false);
|
||||
|
||||
val thumbnail = content.author.thumbnail
|
||||
|
@ -186,16 +170,6 @@ open class PreviewVideoView : LinearLayout {
|
|||
|
||||
_textChannelName.text = content.author.name
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(content.author.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(content.author.id);
|
||||
}
|
||||
} else {
|
||||
_taskLoadProfile.run(content.author.id);
|
||||
}
|
||||
|
||||
_imageChannel?.clipToOutline = true;
|
||||
|
||||
_textVideoName.text = content.name;
|
||||
|
@ -335,52 +309,6 @@ open class PreviewVideoView : LinearLayout {
|
|||
_playerVideoThumbnail?.setMuteChangedListener(callback);
|
||||
}
|
||||
|
||||
private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||
_neopassAnimator?.cancel();
|
||||
_neopassAnimator = null;
|
||||
|
||||
val profile = cachedPolycentricProfile?.profile;
|
||||
if (_creatorThumbnail != null) {
|
||||
val dp_32 = 32.dp(context.resources);
|
||||
val avatar = profile?.systemState?.avatar?.selectBestImage(dp_32 * dp_32)
|
||||
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
|
||||
|
||||
if (avatar != null) {
|
||||
_creatorThumbnail.setThumbnail(avatar, animate);
|
||||
} else {
|
||||
_creatorThumbnail.setThumbnail(content?.author?.thumbnail, animate);
|
||||
_creatorThumbnail.setHarborAvailable(profile != null, animate, profile?.system?.toProto());
|
||||
}
|
||||
} else if (_imageChannel != null) {
|
||||
val dp_28 = 28.dp(context.resources);
|
||||
val avatar = profile?.systemState?.avatar?.selectBestImage(dp_28 * dp_28)
|
||||
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
|
||||
|
||||
if (avatar != null) {
|
||||
_imageChannel.let {
|
||||
Glide.with(_imageChannel)
|
||||
.load(avatar)
|
||||
.placeholder(R.drawable.placeholder_channel_thumbnail)
|
||||
.into(_imageChannel);
|
||||
}
|
||||
|
||||
_imageNeopassChannel?.visibility = View.VISIBLE
|
||||
if (animate) {
|
||||
_neopassAnimator = ObjectAnimator.ofFloat(_imageNeopassChannel, "alpha", 0.0f, 1.0f).setDuration(500)
|
||||
_neopassAnimator?.start()
|
||||
} else {
|
||||
_imageNeopassChannel?.alpha = 1.0f;
|
||||
}
|
||||
} else {
|
||||
_imageNeopassChannel?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
if (profile != null) {
|
||||
_textChannelName.text = profile.systemState.username
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "VideoPreviewViewHolder"
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import com.futo.platformplayer.constructs.Event1
|
|||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||
|
@ -27,14 +26,6 @@ class CreatorBarViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.AnyVi
|
|||
|
||||
val onClick = Event1<IPlatformChannel>();
|
||||
|
||||
private val _taskLoadProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(
|
||||
StateApp.instance.scopeGetter,
|
||||
{ PolycentricCache.instance.getProfileAsync(it) })
|
||||
.success { onProfileLoaded(it, true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load profile.", it);
|
||||
};
|
||||
|
||||
init {
|
||||
_creatorThumbnail = _view.findViewById(R.id.creator_thumbnail);
|
||||
_name = _view.findViewById(R.id.text_channel_name);
|
||||
|
@ -45,40 +36,10 @@ class CreatorBarViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.AnyVi
|
|||
}
|
||||
|
||||
override fun bind(value: IPlatformChannel) {
|
||||
_taskLoadProfile.cancel();
|
||||
|
||||
_channel = value;
|
||||
|
||||
_creatorThumbnail.setThumbnail(value.thumbnail, false);
|
||||
_name.text = value.name;
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(value.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(value.id);
|
||||
}
|
||||
} else {
|
||||
_taskLoadProfile.run(value.id);
|
||||
}
|
||||
}
|
||||
|
||||
private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||
val dp_55 = 55.dp(itemView.context.resources)
|
||||
val profile = cachedPolycentricProfile?.profile;
|
||||
val avatar = profile?.systemState?.avatar?.selectBestImage(dp_55 * dp_55)
|
||||
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
|
||||
|
||||
if (avatar != null) {
|
||||
_creatorThumbnail.setThumbnail(avatar, animate);
|
||||
} else {
|
||||
_creatorThumbnail.setThumbnail(_channel?.thumbnail, animate);
|
||||
_creatorThumbnail.setHarborAvailable(profile != null, animate, profile?.system?.toProto());
|
||||
}
|
||||
|
||||
if (profile != null) {
|
||||
_name.text = profile.systemState.username;
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -94,14 +55,6 @@ class SelectableCreatorBarViewHolder(private val _viewGroup: ViewGroup) : AnyAda
|
|||
|
||||
val onClick = Event1<Selectable>();
|
||||
|
||||
private val _taskLoadProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(
|
||||
StateApp.instance.scopeGetter,
|
||||
{ PolycentricCache.instance.getProfileAsync(it) })
|
||||
.success { onProfileLoaded(it, true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load profile.", it);
|
||||
};
|
||||
|
||||
init {
|
||||
_creatorThumbnail = _view.findViewById(R.id.creator_thumbnail);
|
||||
_name = _view.findViewById(R.id.text_channel_name);
|
||||
|
@ -112,8 +65,6 @@ class SelectableCreatorBarViewHolder(private val _viewGroup: ViewGroup) : AnyAda
|
|||
}
|
||||
|
||||
override fun bind(value: Selectable) {
|
||||
_taskLoadProfile.cancel();
|
||||
|
||||
_channel = value;
|
||||
|
||||
if(value.active)
|
||||
|
@ -123,34 +74,6 @@ class SelectableCreatorBarViewHolder(private val _viewGroup: ViewGroup) : AnyAda
|
|||
|
||||
_creatorThumbnail.setThumbnail(value.channel.thumbnail, false);
|
||||
_name.text = value.channel.name;
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(value.channel.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(value.channel.id);
|
||||
}
|
||||
} else {
|
||||
_taskLoadProfile.run(value.channel.id);
|
||||
}
|
||||
}
|
||||
|
||||
private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||
val dp_55 = 55.dp(itemView.context.resources)
|
||||
val profile = cachedPolycentricProfile?.profile;
|
||||
val avatar = profile?.systemState?.avatar?.selectBestImage(dp_55 * dp_55)
|
||||
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
|
||||
|
||||
if (avatar != null) {
|
||||
_creatorThumbnail.setThumbnail(avatar, animate);
|
||||
} else {
|
||||
_creatorThumbnail.setThumbnail(_channel?.channel?.thumbnail, animate);
|
||||
_creatorThumbnail.setHarborAvailable(profile != null, animate, profile?.system?.toProto());
|
||||
}
|
||||
|
||||
if (profile != null) {
|
||||
_name.text = profile.systemState.username;
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -12,7 +12,6 @@ import com.futo.platformplayer.constructs.Event1
|
|||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.toHumanNumber
|
||||
|
@ -34,14 +33,6 @@ class CreatorViewHolder(private val _viewGroup: ViewGroup, private val _tiny: Bo
|
|||
|
||||
val onClick = Event1<PlatformAuthorLink>();
|
||||
|
||||
private val _taskLoadProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(
|
||||
StateApp.instance.scopeGetter,
|
||||
{ PolycentricCache.instance.getProfileAsync(it) })
|
||||
.success { it -> onProfileLoaded(it, true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load profile.", it);
|
||||
};
|
||||
|
||||
init {
|
||||
_textName = _view.findViewById(R.id.text_channel_name);
|
||||
_creatorThumbnail = _view.findViewById(R.id.creator_thumbnail);
|
||||
|
@ -61,21 +52,9 @@ class CreatorViewHolder(private val _viewGroup: ViewGroup, private val _tiny: Bo
|
|||
}
|
||||
|
||||
override fun bind(value: PlatformAuthorLink) {
|
||||
_taskLoadProfile.cancel();
|
||||
|
||||
_creatorThumbnail.setThumbnail(value.thumbnail, false);
|
||||
_textName.text = value.name;
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(value.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(value.id);
|
||||
}
|
||||
} else {
|
||||
_taskLoadProfile.run(value.id);
|
||||
}
|
||||
|
||||
if(value.subscribers == null || (value.subscribers ?: 0) <= 0L)
|
||||
_textMetadata.visibility = View.GONE;
|
||||
else {
|
||||
|
@ -87,25 +66,6 @@ class CreatorViewHolder(private val _viewGroup: ViewGroup, private val _tiny: Bo
|
|||
_authorLink = value;
|
||||
}
|
||||
|
||||
private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||
val dp_61 = 61.dp(itemView.context.resources);
|
||||
|
||||
val profile = cachedPolycentricProfile?.profile;
|
||||
val avatar = profile?.systemState?.avatar?.selectBestImage(dp_61 * dp_61)
|
||||
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
|
||||
|
||||
if (avatar != null) {
|
||||
_creatorThumbnail.setThumbnail(avatar, animate);
|
||||
} else {
|
||||
_creatorThumbnail.setThumbnail(_authorLink?.thumbnail, animate);
|
||||
_creatorThumbnail.setHarborAvailable(profile != null, animate, profile?.system?.toProto());
|
||||
}
|
||||
|
||||
if (profile != null) {
|
||||
_textName.text = profile.systemState.username;
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CreatorViewHolder";
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import com.futo.platformplayer.constructs.TaskHandler
|
|||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||
|
@ -27,14 +26,6 @@ class SubscriptionBarViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.
|
|||
private var _subscription: Subscription? = null;
|
||||
private var _channel: SerializedChannel? = null;
|
||||
|
||||
private val _taskLoadProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(
|
||||
StateApp.instance.scopeGetter,
|
||||
{ PolycentricCache.instance.getProfileAsync(it) })
|
||||
.success { onProfileLoaded(it, true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load profile.", it);
|
||||
};
|
||||
|
||||
val onClick = Event1<Subscription>();
|
||||
|
||||
init {
|
||||
|
@ -47,44 +38,14 @@ class SubscriptionBarViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.
|
|||
}
|
||||
|
||||
override fun bind(value: Subscription) {
|
||||
_taskLoadProfile.cancel();
|
||||
|
||||
_channel = value.channel;
|
||||
|
||||
_creatorThumbnail.setThumbnail(value.channel.thumbnail, false);
|
||||
_name.text = value.channel.name;
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(value.channel.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(value.channel.id);
|
||||
}
|
||||
} else {
|
||||
_taskLoadProfile.run(value.channel.id);
|
||||
}
|
||||
|
||||
_subscription = value;
|
||||
}
|
||||
|
||||
private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||
val dp_55 = 55.dp(itemView.context.resources)
|
||||
val profile = cachedPolycentricProfile?.profile;
|
||||
val avatar = profile?.systemState?.avatar?.selectBestImage(dp_55 * dp_55)
|
||||
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
|
||||
|
||||
if (avatar != null) {
|
||||
_creatorThumbnail.setThumbnail(avatar, animate);
|
||||
} else {
|
||||
_creatorThumbnail.setThumbnail(_channel?.thumbnail, animate);
|
||||
_creatorThumbnail.setHarborAvailable(profile != null, animate, profile?.system?.toProto());
|
||||
}
|
||||
|
||||
if (profile != null) {
|
||||
_name.text = profile.systemState.username;
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SubscriptionBarViewHolder";
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import com.futo.platformplayer.dp
|
|||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.models.SubscriptionGroup
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||
|
|
|
@ -11,8 +11,8 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
|||
import com.bumptech.glide.Glide
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.getDataLinkFromUrl
|
||||
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.views.IdenticonView
|
||||
import userpackage.Protocol
|
||||
|
||||
|
@ -68,7 +68,7 @@ class CreatorThumbnail : ConstraintLayout {
|
|||
|
||||
if (url.startsWith("polycentric://")) {
|
||||
try {
|
||||
val dataLink = PolycentricCache.getDataLinkFromUrl(url)
|
||||
val dataLink = url.getDataLinkFromUrl()
|
||||
setHarborAvailable(true, animate, dataLink?.system);
|
||||
} catch (e: Throwable) {
|
||||
setHarborAvailable(false, animate, null);
|
||||
|
|
|
@ -5,8 +5,8 @@ import android.util.AttributeSet
|
|||
import android.widget.LinearLayout
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.views.SupportView
|
||||
import com.futo.polycentric.core.PolycentricProfile
|
||||
|
||||
class SupportOverlay : LinearLayout {
|
||||
val onClose = Event0();
|
||||
|
|
|
@ -6,9 +6,7 @@ import android.webkit.WebView
|
|||
import android.widget.LinearLayout
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.views.SupportView
|
||||
|
||||
class WebviewOverlay : LinearLayout {
|
||||
val onClose = Event0();
|
||||
|
|
|
@ -22,12 +22,12 @@ import com.futo.platformplayer.api.media.structures.IPager
|
|||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException
|
||||
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StatePolycentric
|
||||
import com.futo.platformplayer.views.adapters.CommentViewHolder
|
||||
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||
import com.futo.polycentric.core.fullyBackfillServersAnnounceExceptions
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.UnknownHostException
|
||||
|
|
|
@ -254,7 +254,7 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/text_channel_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
|
@ -268,23 +268,10 @@
|
|||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintLeft_toRightOf="@id/image_channel_thumbnail"
|
||||
app:layout_constraintRight_toLeftOf="@id/image_neopass_channel"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_video_name"
|
||||
android:layout_marginStart="4dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_neopass_channel"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:contentDescription="@string/neopass_channel"
|
||||
app:srcCompat="@drawable/neopass"
|
||||
app:layout_constraintLeft_toRightOf="@id/text_channel_name"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/text_channel_name"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_channel_name"
|
||||
android:layout_marginStart="4dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_video_metadata"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/text_channel_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="1"
|
||||
|
@ -117,21 +117,8 @@
|
|||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="@id/image_neopass_channel"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_playlist_name" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_neopass_channel"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:contentDescription="@string/neopass_channel"
|
||||
app:srcCompat="@drawable/neopass"
|
||||
app:layout_constraintLeft_toRightOf="@id/text_channel_name"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/text_channel_name"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_channel_name"
|
||||
android:layout_marginStart="4dp"
|
||||
android:visibility="gone"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/text_playlist_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_playlist_items"
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/text_author_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
|
@ -51,24 +51,10 @@
|
|||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintLeft_toRightOf="@id/image_author_thumbnail"
|
||||
app:layout_constraintRight_toLeftOf="@id/image_neopass_channel"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/image_author_thumbnail"
|
||||
tools:text="Two Minute Papers" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_neopass_channel"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:contentDescription="@string/neopass_channel"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_author_name"
|
||||
app:layout_constraintLeft_toRightOf="@id/text_author_name"
|
||||
app:layout_constraintRight_toLeftOf="@id/platform_indicator"
|
||||
app:layout_constraintTop_toTopOf="@id/text_author_name"
|
||||
app:srcCompat="@drawable/neopass" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_metadata"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
@ -36,9 +36,10 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/text_author_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/inter_extra_light"
|
||||
|
@ -50,24 +51,10 @@
|
|||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintLeft_toRightOf="@id/image_author_thumbnail"
|
||||
app:layout_constraintRight_toLeftOf="@id/image_neopass_channel"
|
||||
app:layout_constraintRight_toLeftOf="@id/platform_indicator"
|
||||
app:layout_constraintTop_toTopOf="@id/image_author_thumbnail"
|
||||
tools:text="Two Minute Papers" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_neopass_channel"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:contentDescription="@string/neopass_channel"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_author_name"
|
||||
app:layout_constraintLeft_toRightOf="@id/text_author_name"
|
||||
app:layout_constraintRight_toLeftOf="@id/platform_indicator"
|
||||
app:layout_constraintTop_toTopOf="@id/text_author_name"
|
||||
app:srcCompat="@drawable/neopass" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_metadata"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
@ -232,7 +232,7 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/text_channel_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
|
@ -246,23 +246,10 @@
|
|||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintLeft_toRightOf="@id/image_channel_thumbnail"
|
||||
app:layout_constraintRight_toLeftOf="@id/image_neopass_channel"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_video_name"
|
||||
android:layout_marginStart="4dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_neopass_channel"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:contentDescription="@string/neopass_channel"
|
||||
app:srcCompat="@drawable/neopass"
|
||||
app:layout_constraintLeft_toRightOf="@id/text_channel_name"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/text_channel_name"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_channel_name"
|
||||
android:layout_marginStart="4dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_video_metadata"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
@ -270,7 +270,7 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/text_channel_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
|
@ -284,23 +284,10 @@
|
|||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintLeft_toRightOf="@id/image_channel_thumbnail"
|
||||
app:layout_constraintRight_toLeftOf="@id/image_neopass_channel"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_video_name"
|
||||
android:layout_marginStart="4dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_neopass_channel"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:contentDescription="@string/neopass_channel"
|
||||
app:srcCompat="@drawable/neopass"
|
||||
app:layout_constraintLeft_toRightOf="@id/text_channel_name"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/text_channel_name"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_channel_name"
|
||||
android:layout_marginStart="4dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_video_metadata"
|
||||
android:layout_width="0dp"
|
||||
|
@ -317,8 +304,6 @@
|
|||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:layout_marginStart="4dp"/>
|
||||
|
||||
|
||||
|
||||
<com.futo.platformplayer.views.platform.PlatformIndicator
|
||||
android:id="@+id/thumbnail_platform_nested"
|
||||
android:layout_width="20dp"
|
||||
|
|
Loading…
Add table
Reference in a new issue