Fixes to Polycentric data display.

This commit is contained in:
Koen 2023-10-23 14:21:10 +02:00
commit d6c4b730de
14 changed files with 272 additions and 120 deletions

View file

@ -35,4 +35,8 @@ fun Protocol.ImageBundle?.selectHighestResolutionImage(): Protocol.ImageManifest
fun Protocol.Claim.resolveChannelUrl(): String? { fun Protocol.Claim.resolveChannelUrl(): String? {
return StatePlatform.instance.resolveChannelUrlByClaimTemplates(this.claimType.toInt(), this.claimFieldsList.associate { Pair(it.key.toInt(), it.value) }) 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) })
} }

View file

@ -568,6 +568,23 @@ open class JSClient : IPlatformClient {
}; };
} }
fun resolveChannelUrlsByClaimTemplates(claimType: Int, values: Map<Int, String>): List<String> {
val urls = arrayListOf<String>();
channelClaimTemplates?.let {
if(it.containsKey(claimType)) {
val templates = it[claimType];
if(templates != null)
for(value in values.keys.sortedBy { it }) {
if(templates.containsKey(value)) {
urls.add(templates[value]!!.replace("{{CLAIMVALUE}}", values[value]!!));
}
}
}
};
return urls;
}
private fun <T> isBusyWith(handle: ()->T): T { private fun <T> isBusyWith(handle: ()->T): T {
try { try {

View file

@ -246,28 +246,45 @@ class ChannelFragment : MainFragment() {
if (parameter is String) { if (parameter is String) {
_buttonSubscribe.setSubscribeChannel(parameter); _buttonSubscribe.setSubscribeChannel(parameter);
_textChannel.text = ""; setPolycentricProfileOr(parameter) {
_textChannelSub.text = ""; _textChannel.text = "";
_textChannelSub.text = "";
_creatorThumbnail.setThumbnail(null, true);
Glide.with(_imageBanner)
.clear(_imageBanner);
};
_url = parameter; _url = parameter;
loadChannel(); loadChannel();
} else if (parameter is SerializedChannel) { } else if (parameter is SerializedChannel) {
showChannel(parameter); showChannel(parameter);
_url = parameter.url; _url = parameter.url;
_creatorThumbnail.setThumbnail(parameter.url, false);
loadChannel(); loadChannel();
} else if (parameter is IPlatformChannel) } else if (parameter is IPlatformChannel)
showChannel(parameter); showChannel(parameter);
else if (parameter is PlatformAuthorLink) { else if (parameter is PlatformAuthorLink) {
_textChannel.text = parameter.name; setPolycentricProfileOr(parameter.url) {
_textChannelSub.text = ""; _textChannel.text = parameter.name;
_creatorThumbnail.setThumbnail(parameter.url, false); _textChannelSub.text = "";
_creatorThumbnail.setThumbnail(parameter.thumbnail, true);
Glide.with(_imageBanner)
.clear(_imageBanner);
_taskLoadPolycentricProfile.run(parameter.id);
};
_url = parameter.url; _url = parameter.url;
loadChannel(); loadChannel();
} else if (parameter is Subscription) { } else if (parameter is Subscription) {
_textChannel.text = parameter.channel.name; setPolycentricProfileOr(parameter.channel.url) {
_textChannelSub.text = ""; _textChannel.text = parameter.channel.name;
_creatorThumbnail.setThumbnail(parameter.channel.thumbnail, false); _textChannelSub.text = "";
_creatorThumbnail.setThumbnail(parameter.channel.thumbnail, true);
Glide.with(_imageBanner)
.clear(_imageBanner);
_taskLoadPolycentricProfile.run(parameter.channel.id);
};
_url = parameter.channel.url; _url = parameter.channel.url;
loadChannel(); loadChannel();
@ -360,15 +377,8 @@ class ChannelFragment : MainFragment() {
_fragment.topBar?.assume<NavigationTopBarFragment>()?.setMenuItems(buttons); _fragment.topBar?.assume<NavigationTopBarFragment>()?.setMenuItems(buttons);
_buttonSubscribe.setSubscribeChannel(channel); _buttonSubscribe.setSubscribeChannel(channel);
_textChannel.text = channel.name;
_textChannelSub.text = if(channel.subscribers > 0) "${channel.subscribers.toHumanNumber()} subscribers" else ""; _textChannelSub.text = if(channel.subscribers > 0) "${channel.subscribers.toHumanNumber()} subscribers" else "";
_creatorThumbnail.setThumbnail(channel.thumbnail, true);
Glide.with(_imageBanner)
.load(channel.banner)
.crossfade()
.into(_imageBanner)
//TODO: Find a better way to access the adapter fragments.. //TODO: Find a better way to access the adapter fragments..
(_viewPager.adapter as ChannelViewPagerAdapter?)?.let { (_viewPager.adapter as ChannelViewPagerAdapter?)?.let {
@ -381,51 +391,68 @@ class ChannelFragment : MainFragment() {
this.channel = channel; this.channel = channel;
val cachedProfile = PolycentricCache.instance.getCachedProfile(channel.url); setPolycentricProfileOr(channel.url) {
_textChannel.text = channel.name;
_creatorThumbnail.setThumbnail(channel.thumbnail, true);
Glide.with(_imageBanner)
.load(channel.banner)
.crossfade()
.into(_imageBanner);
_taskLoadPolycentricProfile.run(channel.id);
};
}
private fun setPolycentricProfileOr(url: String, or: () -> Unit) {
val cachedProfile = channel?.let { PolycentricCache.instance.getCachedProfile(it.url) };
if (cachedProfile != null) { if (cachedProfile != null) {
setPolycentricProfile(cachedProfile, animate = false); setPolycentricProfile(cachedProfile, animate = false);
} else { } else {
setPolycentricProfile(null, animate = false); setPolycentricProfile(null, animate = false);
_taskLoadPolycentricProfile.run(channel.id); or();
} }
} }
private fun setPolycentricProfile(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) { private fun setPolycentricProfile(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
Log.i(TAG, "setPolycentricProfile(cachedPolycentricProfile = $cachedPolycentricProfile, animate = $animate)") Log.i(TAG, "setPolycentricProfile(cachedPolycentricProfile = $cachedPolycentricProfile, animate = $animate)")
val polycentricProfile = cachedPolycentricProfile?.profile; val dp_35 = 35.dp(resources)
if (polycentricProfile != null) { val profile = cachedPolycentricProfile?.profile;
_fragment.topBar?.onShown(polycentricProfile); val avatar = profile?.systemState?.avatar?.selectBestImage(dp_35 * dp_35)
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
if (polycentricProfile.systemState.username.isNotBlank()) if (avatar != null) {
_textChannel.text = polycentricProfile.systemState.username; _creatorThumbnail.setThumbnail(avatar, animate);
} else {
_creatorThumbnail.setThumbnail(channel?.thumbnail, animate);
_creatorThumbnail.setHarborAvailable(profile != null, animate);
}
val dp_35 = 35.dp(resources) val banner = profile?.systemState?.banner?.selectHighestResolutionImage()
val avatar = polycentricProfile.systemState.avatar?.selectBestImage(dp_35 * dp_35) ?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
?.let { it.toURLInfoSystemLinkUrl(polycentricProfile.system.toProto(), it.process, polycentricProfile.systemState.servers.toList()) };
if (avatar != null) { if (banner != null) {
_creatorThumbnail.setThumbnail(avatar, true); Glide.with(_imageBanner)
} else { .load(banner)
_creatorThumbnail.setHarborAvailable(true, true); .crossfade()
} .into(_imageBanner);
} else {
Glide.with(_imageBanner)
.load(channel?.banner)
.crossfade()
.into(_imageBanner);
}
val banner = polycentricProfile.systemState.banner?.selectHighestResolutionImage() if (profile != null) {
?.let { it.toURLInfoSystemLinkUrl(polycentricProfile.system.toProto(), it.process, polycentricProfile.systemState.servers.toList()) }; _fragment.topBar?.onShown(profile);
_textChannel.text = profile.systemState.username;
if (banner != null) {
Glide.with(_imageBanner)
.load(banner)
.crossfade()
.into(_imageBanner);
}
} }
(_viewPager.adapter as ChannelViewPagerAdapter?)?.let { (_viewPager.adapter as ChannelViewPagerAdapter?)?.let {
it.getFragment<ChannelAboutFragment>().setPolycentricProfile(polycentricProfile, animate); it.getFragment<ChannelAboutFragment>().setPolycentricProfile(profile, animate);
it.getFragment<ChannelMonetizationFragment>().setPolycentricProfile(polycentricProfile, animate); it.getFragment<ChannelMonetizationFragment>().setPolycentricProfile(profile, animate);
it.getFragment<ChannelListFragment>().setPolycentricProfile(polycentricProfile, animate); it.getFragment<ChannelListFragment>().setPolycentricProfile(profile, animate);
it.getFragment<ChannelContentsFragment>().setPolycentricProfile(polycentricProfile, animate); it.getFragment<ChannelContentsFragment>().setPolycentricProfile(profile, animate);
//TODO: Call on other tabs as needed //TODO: Call on other tabs as needed
} }
} }

View file

@ -101,14 +101,12 @@ class ContentSearchResultsFragment : MainFragment() {
fun onShown(parameter: Any?, isBack: Boolean) { fun onShown(parameter: Any?, isBack: Boolean) {
if(parameter is SuggestionsFragmentData) { if(parameter is SuggestionsFragmentData) {
if(!isBack) { setQuery(parameter.query, false);
setQuery(parameter.query, false); setChannelUrl(parameter.channelUrl, false);
setChannelUrl(parameter.channelUrl, false);
fragment.topBar?.apply { fragment.topBar?.apply {
if (this is SearchTopBarFragment) { if (this is SearchTopBarFragment) {
this.setText(parameter.query); this.setText(parameter.query);
}
} }
} }
} }

View file

@ -71,16 +71,14 @@ class CreatorSearchResultsFragment : MainFragment() {
fun onShown(parameter: Any?, isBack: Boolean) { fun onShown(parameter: Any?, isBack: Boolean) {
if(parameter is String) { if(parameter is String) {
if(!isBack) { setQuery(parameter);
setQuery(parameter);
fragment.topBar?.apply { fragment.topBar?.apply {
if (this is SearchTopBarFragment) { if (this is SearchTopBarFragment) {
setText(parameter); setText(parameter);
onSearch.subscribe(this) { onSearch.subscribe(this) {
setQuery(it); setQuery(it);
}; };
}
} }
} }
} }

View file

@ -73,16 +73,14 @@ class PlaylistSearchResultsFragment : MainFragment() {
fun onShown(parameter: Any?, isBack: Boolean) { fun onShown(parameter: Any?, isBack: Boolean) {
if(parameter is String) { if(parameter is String) {
if(!isBack) { setQuery(parameter);
setQuery(parameter);
fragment.topBar?.apply { fragment.topBar?.apply {
if (this is SearchTopBarFragment) { if (this is SearchTopBarFragment) {
setText(parameter); setText(parameter);
onSearch.subscribe(this) { onSearch.subscribe(this) {
setQuery(it); setQuery(it);
}; };
}
} }
} }
} }

View file

@ -870,7 +870,6 @@ class VideoDetailView : ConstraintLayout {
_commentsList.clear(); _commentsList.clear();
_platform.setPlatformFromClientID(video.id.pluginId); _platform.setPlatformFromClientID(video.id.pluginId);
_subTitle.text = subTitleSegments.joinToString(""); _subTitle.text = subTitleSegments.joinToString("");
_channelName.text = video.author.name;
_playWhenReady = true; _playWhenReady = true;
if(video.author.subscribers != null) { if(video.author.subscribers != null) {
_channelMeta.text = if((video.author.subscribers ?: 0) > 0) video.author.subscribers!!.toHumanNumber() + " subscribers" else ""; _channelMeta.text = if((video.author.subscribers ?: 0) > 0) video.author.subscribers!!.toHumanNumber() + " subscribers" else "";
@ -897,6 +896,7 @@ class VideoDetailView : ConstraintLayout {
} else { } else {
setPolycentricProfile(null, animate = false); setPolycentricProfile(null, animate = false);
_taskLoadPolycentricProfile.run(video.author.id); _taskLoadPolycentricProfile.run(video.author.id);
_channelName.text = video.author.name;
} }
_player.clear(); _player.clear();
@ -1971,14 +1971,24 @@ class VideoDetailView : ConstraintLayout {
private fun setPolycentricProfile(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) { private fun setPolycentricProfile(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
_polycentricProfile = cachedPolycentricProfile; _polycentricProfile = cachedPolycentricProfile;
if (cachedPolycentricProfile?.profile == null) { val dp_35 = 35.dp(context.resources)
_layoutMonetization.visibility = View.GONE; val profile = cachedPolycentricProfile?.profile;
_creatorThumbnail.setHarborAvailable(false, animate); val avatar = profile?.systemState?.avatar?.selectBestImage(dp_35 * dp_35)
return; ?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
if (avatar != null) {
_creatorThumbnail.setThumbnail(avatar, animate);
} else {
_creatorThumbnail.setThumbnail(video?.author?.thumbnail, animate);
_creatorThumbnail.setHarborAvailable(profile != null, animate);
} }
_layoutMonetization.visibility = View.VISIBLE; if (profile != null) {
_creatorThumbnail.setHarborAvailable(true, animate); _channelName.text = cachedPolycentricProfile.profile.systemState.username;
_layoutMonetization.visibility = View.VISIBLE;
} else {
_layoutMonetization.visibility = View.GONE;
}
} }
fun setProgressBarOverlayed(isOverlayed: Boolean?) { fun setProgressBarOverlayed(isOverlayed: Boolean?) {

View file

@ -8,6 +8,7 @@ import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
import com.futo.platformplayer.getNowDiffSeconds import com.futo.platformplayer.getNowDiffSeconds
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.resolveChannelUrl import com.futo.platformplayer.resolveChannelUrl
import com.futo.platformplayer.resolveChannelUrls
import com.futo.platformplayer.serializers.OffsetDateTimeSerializer import com.futo.platformplayer.serializers.OffsetDateTimeSerializer
import com.futo.platformplayer.stores.CachedPolycentricProfileStorage import com.futo.platformplayer.stores.CachedPolycentricProfileStorage
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
@ -37,7 +38,8 @@ class PolycentricCache {
ContentType.AVATAR.value, ContentType.AVATAR.value,
ContentType.USERNAME.value, ContentType.USERNAME.value,
ContentType.DESCRIPTION.value, ContentType.DESCRIPTION.value,
ContentType.STORE.value ContentType.STORE.value,
ContentType.SERVER.value
) )
).eventsList.map { e -> SignedEvent.fromProto(e) }; ).eventsList.map { e -> SignedEvent.fromProto(e) };
@ -88,8 +90,9 @@ class PolycentricCache {
if (result.profile != null) { if (result.profile != null) {
for (claim in result.profile.ownedClaims) { for (claim in result.profile.ownedClaims) {
val url = claim.claim.resolveChannelUrl() ?: continue; val urls = claim.claim.resolveChannelUrls();
_profileUrlCache.map[url] = result; for (url in urls)
_profileUrlCache.map[url] = result;
} }
} }

View file

@ -784,6 +784,15 @@ class StatePlatform {
return null; return null;
} }
fun resolveChannelUrlsByClaimTemplates(claimType: Int, claimValues: Map<Int, String>): List<String> {
val urls = arrayListOf<String>();
for(client in getClientsByClaimType(claimType).filter { it is JSClient }) {
val res = (client as JSClient).resolveChannelUrlsByClaimTemplates(claimType, claimValues);
urls.addAll(res);
}
return urls;
}
fun getPlaylistClientOrNull(url: String): IPlatformClient? = getEnabledClients().find { it.isPlaylistUrl(url) } fun getPlaylistClientOrNull(url: String): IPlatformClient? = getEnabledClients().find { it.isPlaylistUrl(url) }
fun getPlaylistClient(url: String): IPlatformClient = getEnabledClients().find { it.isPlaylistUrl(url) } fun getPlaylistClient(url: String): IPlatformClient = getEnabledClients().find { it.isPlaylistUrl(url) }
?: throw NoPlatformClientException("No client enabled that supports this playlist url (${url})"); ?: throw NoPlatformClientException("No client enabled that supports this playlist url (${url})");

View file

@ -93,6 +93,7 @@ class CommentViewHolder : ViewHolder {
fun bind(comment: IPlatformComment, readonly: Boolean) { fun bind(comment: IPlatformComment, readonly: Boolean) {
_creatorThumbnail.setThumbnail(comment.author.thumbnail, false); _creatorThumbnail.setThumbnail(comment.author.thumbnail, false);
_creatorThumbnail.setHarborAvailable(comment is PolycentricPlatformComment,false);
_textAuthor.text = comment.author.name; _textAuthor.text = comment.author.name;
val date = comment.date; val date = comment.date;

View file

@ -32,6 +32,7 @@ import com.futo.platformplayer.views.others.CreatorThumbnail
import com.futo.platformplayer.views.FeedStyle import com.futo.platformplayer.views.FeedStyle
import com.futo.platformplayer.views.platform.PlatformIndicator import com.futo.platformplayer.views.platform.PlatformIndicator
import com.futo.platformplayer.views.video.FutoThumbnailPlayer import com.futo.platformplayer.views.video.FutoThumbnailPlayer
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
open class PreviewVideoView : LinearLayout { open class PreviewVideoView : LinearLayout {
@ -58,11 +59,12 @@ open class PreviewVideoView : LinearLayout {
protected val _exoPlayer: PlayerManager?; protected val _exoPlayer: PlayerManager?;
private val _taskLoadValidClaims = TaskHandler<PlatformID, PolycentricCache.CachedOwnedClaims>(StateApp.instance.scopeGetter, private val _taskLoadProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(
{ PolycentricCache.instance.getValidClaimsAsync(it).await() }) StateApp.instance.scopeGetter,
.success { it -> updateClaimsLayout(it, animate = true) } { PolycentricCache.instance.getProfileAsync(it) })
.success { it -> onProfileLoaded(it, true) }
.exception<Throwable> { .exception<Throwable> {
Logger.w(TAG, "Failed to load claims.", it); Logger.w(TAG, "Failed to load profile.", it);
}; };
val onVideoClicked = Event2<IPlatformVideo, Long>(); val onVideoClicked = Event2<IPlatformVideo, Long>();
@ -145,15 +147,7 @@ open class PreviewVideoView : LinearLayout {
open fun bind(content: IPlatformContent) { open fun bind(content: IPlatformContent) {
_taskLoadValidClaims.cancel(); _taskLoadProfile.cancel();
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);
}
isClickable = true; isClickable = true;
@ -161,16 +155,25 @@ open class PreviewVideoView : LinearLayout {
stopPreview(); stopPreview();
if(_imageChannel != null) val cachedProfile = PolycentricCache.instance.getCachedProfile(content.author.url, true);
Glide.with(_imageChannel) if (cachedProfile != null) {
.load(content.author.thumbnail) onProfileLoaded(cachedProfile, false);
.placeholder(R.drawable.placeholder_channel_thumbnail) } else {
.into(_imageChannel); _imageNeopassChannel?.visibility = View.GONE;
_creatorThumbnail?.setThumbnail(content.author.thumbnail, false);
_imageChannel?.let {
Glide.with(_imageChannel)
.load(content.author.thumbnail)
.placeholder(R.drawable.placeholder_channel_thumbnail)
.into(_imageChannel);
}
_taskLoadProfile.run(content.author.id);
_textChannelName.text = content.author.name
}
_imageChannel?.clipToOutline = true; _imageChannel?.clipToOutline = true;
_textVideoName.text = content.name; _textVideoName.text = content.name;
_textChannelName.text = content.author.name
_layoutDownloaded.visibility = if (StateDownloads.instance.isDownloaded(content.id)) VISIBLE else GONE; _layoutDownloaded.visibility = if (StateDownloads.instance.isDownloaded(content.id)) VISIBLE else GONE;
_platformIndicator.setPlatformFromClientID(content.id.pluginId); _platformIndicator.setPlatformFromClientID(content.id.pluginId);
@ -296,22 +299,50 @@ open class PreviewVideoView : LinearLayout {
_playerVideoThumbnail?.setMuteChangedListener(callback); _playerVideoThumbnail?.setMuteChangedListener(callback);
} }
private fun updateClaimsLayout(claims: PolycentricCache.CachedOwnedClaims?, animate: Boolean) { private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
_neopassAnimator?.cancel(); _neopassAnimator?.cancel();
_neopassAnimator = null; _neopassAnimator = null;
val harborAvailable = claims != null && !claims.ownedClaims.isNullOrEmpty(); val profile = cachedPolycentricProfile?.profile;
if (harborAvailable) { if (_creatorThumbnail != null) {
_imageNeopassChannel?.visibility = View.VISIBLE val dp_32 = 32.dp(context.resources);
if (animate) { val avatar = profile?.systemState?.avatar?.selectBestImage(dp_32 * dp_32)
_neopassAnimator = ObjectAnimator.ofFloat(_imageNeopassChannel, "alpha", 0.0f, 1.0f).setDuration(500) ?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
_neopassAnimator?.start()
if (avatar != null) {
_creatorThumbnail.setThumbnail(avatar, animate);
} else {
_creatorThumbnail.setThumbnail(content?.author?.thumbnail, animate);
_creatorThumbnail.setHarborAvailable(profile != null, animate);
}
} 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
} }
} else {
_imageNeopassChannel?.visibility = View.GONE
} }
_creatorThumbnail?.setHarborAvailable(harborAvailable, animate) if (profile != null) {
_textChannelName.text = profile.systemState.username
}
} }
companion object { companion object {

View file

@ -72,22 +72,27 @@ class SubscriptionViewHolder : ViewHolder {
} else { } else {
_creatorThumbnail.setThumbnail(sub.channel.thumbnail, false); _creatorThumbnail.setThumbnail(sub.channel.thumbnail, false);
_taskLoadProfile.run(sub.channel.id); _taskLoadProfile.run(sub.channel.id);
_textName.text = sub.channel.name;
} }
_textName.text = sub.channel.name;
_platformIndicator.setPlatformFromClientID(sub.channel.id.pluginId); _platformIndicator.setPlatformFromClientID(sub.channel.id.pluginId);
} }
private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) { private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
val dp_46 = 46.dp(itemView.context.resources); val dp_46 = 46.dp(itemView.context.resources);
val avatar = cachedPolycentricProfile?.profile?.systemState?.avatar?.selectBestImage(dp_46 * dp_46) val profile = cachedPolycentricProfile?.profile;
?.let { it.toURLInfoSystemLinkUrl(cachedPolycentricProfile.profile.system.toProto(), it.process, cachedPolycentricProfile.profile.systemState.servers.toList()) }; 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) { if (avatar != null) {
_creatorThumbnail.setThumbnail(avatar, animate); _creatorThumbnail.setThumbnail(avatar, animate);
} else { } else {
_creatorThumbnail.setThumbnail(this.subscription?.channel?.thumbnail, animate); _creatorThumbnail.setThumbnail(this.subscription?.channel?.thumbnail, animate);
_creatorThumbnail.setHarborAvailable(cachedPolycentricProfile?.profile != null, animate); _creatorThumbnail.setHarborAvailable(profile != null, animate);
}
if (profile != null) {
_textName.text = profile.systemState.username;
} }
} }

View file

@ -6,13 +6,23 @@ import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.api.media.PlatformID
import com.futo.platformplayer.api.media.models.PlatformAuthorLink import com.futo.platformplayer.api.media.models.PlatformAuthorLink
import com.futo.platformplayer.constructs.Event1 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.states.StatePolycentric
import com.futo.platformplayer.toHumanNumber import com.futo.platformplayer.toHumanNumber
import com.futo.platformplayer.views.adapters.AnyAdapter import com.futo.platformplayer.views.adapters.AnyAdapter
import com.futo.platformplayer.views.adapters.SubscriptionViewHolder
import com.futo.platformplayer.views.others.CreatorThumbnail import com.futo.platformplayer.views.others.CreatorThumbnail
import com.futo.platformplayer.views.platform.PlatformIndicator import com.futo.platformplayer.views.platform.PlatformIndicator
import com.futo.platformplayer.views.subscriptions.SubscribeButton import com.futo.platformplayer.views.subscriptions.SubscribeButton
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
class CreatorViewHolder(private val _viewGroup: ViewGroup, private val _tiny: Boolean) : AnyAdapter.AnyViewHolder<PlatformAuthorLink>( class CreatorViewHolder(private val _viewGroup: ViewGroup, private val _tiny: Boolean) : AnyAdapter.AnyViewHolder<PlatformAuthorLink>(
LayoutInflater.from(_viewGroup.context).inflate(R.layout.list_creator, _viewGroup, false)) { LayoutInflater.from(_viewGroup.context).inflate(R.layout.list_creator, _viewGroup, false)) {
@ -25,7 +35,15 @@ class CreatorViewHolder(private val _viewGroup: ViewGroup, private val _tiny: Bo
private var _authorLink: PlatformAuthorLink? = null; private var _authorLink: PlatformAuthorLink? = null;
val onClick = Event1<PlatformAuthorLink>(); 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 { init {
_textName = _view.findViewById(R.id.text_channel_name); _textName = _view.findViewById(R.id.text_channel_name);
_creatorThumbnail = _view.findViewById(R.id.creator_thumbnail); _creatorThumbnail = _view.findViewById(R.id.creator_thumbnail);
@ -45,12 +63,21 @@ class CreatorViewHolder(private val _viewGroup: ViewGroup, private val _tiny: Bo
} }
override fun bind(authorLink: PlatformAuthorLink) { override fun bind(authorLink: PlatformAuthorLink) {
_textName.text = authorLink.name; _taskLoadProfile.cancel();
_creatorThumbnail.setThumbnail(authorLink.thumbnail, false);
val cachedProfile = PolycentricCache.instance.getCachedProfile(authorLink.url, true);
if (cachedProfile != null) {
onProfileLoaded(cachedProfile, false);
} else {
_creatorThumbnail.setThumbnail(authorLink.thumbnail, false);
_taskLoadProfile.run(authorLink.id);
_textName.text = authorLink.name;
}
if(authorLink.subscribers == null || (authorLink.subscribers ?: 0) <= 0L) if(authorLink.subscribers == null || (authorLink.subscribers ?: 0) <= 0L)
_textMetadata.visibility = View.GONE; _textMetadata.visibility = View.GONE;
else { else {
_textMetadata.text = if(authorLink?.subscribers ?: 0 > 0) authorLink.subscribers!!.toHumanNumber() + " subscribers" else ""; _textMetadata.text = if((authorLink.subscribers ?: 0) > 0) authorLink.subscribers!!.toHumanNumber() + " subscribers" else "";
_textMetadata.visibility = View.VISIBLE; _textMetadata.visibility = View.VISIBLE;
} }
_buttonSubscribe.setSubscribeChannel(authorLink.url); _buttonSubscribe.setSubscribeChannel(authorLink.url);
@ -58,6 +85,25 @@ class CreatorViewHolder(private val _viewGroup: ViewGroup, private val _tiny: Bo
_authorLink = authorLink; _authorLink = authorLink;
} }
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);
}
if (profile != null) {
_textName.text = profile.systemState.username;
}
}
companion object { companion object {
private const val TAG = "CreatorViewHolder"; private const val TAG = "CreatorViewHolder";
} }

View file

@ -57,22 +57,27 @@ class SubscriptionBarViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.
} else { } else {
_creatorThumbnail.setThumbnail(subscription.channel.thumbnail, false); _creatorThumbnail.setThumbnail(subscription.channel.thumbnail, false);
_taskLoadProfile.run(subscription.channel.id); _taskLoadProfile.run(subscription.channel.id);
_name.text = subscription.channel.name;
} }
_name.text = subscription.channel.name;
_subscription = subscription; _subscription = subscription;
} }
private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) { private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
val dp_55 = 55.dp(itemView.context.resources) val dp_55 = 55.dp(itemView.context.resources)
val avatar = cachedPolycentricProfile?.profile?.systemState?.avatar?.selectBestImage(dp_55 * dp_55) val profile = cachedPolycentricProfile?.profile;
?.let { it.toURLInfoSystemLinkUrl(cachedPolycentricProfile.profile.system.toProto(), it.process, cachedPolycentricProfile.profile.systemState.servers.toList()) }; 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) { if (avatar != null) {
_creatorThumbnail.setThumbnail(avatar, animate); _creatorThumbnail.setThumbnail(avatar, animate);
} else { } else {
_creatorThumbnail.setThumbnail(_channel?.thumbnail, animate); _creatorThumbnail.setThumbnail(_channel?.thumbnail, animate);
_creatorThumbnail.setHarborAvailable(cachedPolycentricProfile?.profile != null, animate); _creatorThumbnail.setHarborAvailable(profile != null, animate);
}
if (profile != null) {
_name.text = profile.systemState.username;
} }
} }