diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 50de3686bd..41bcf55a06 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -815,6 +815,14 @@
+
+
+
+
() {
- override fun onSuccess(data: ResponseBody) {
- ToastUtils.toast("举报成功")
- }
+ interface CommunityReporter {
- override fun onFailure(exception: Exception) {
- super.onFailure(exception)
- if (exception is HttpException) {
- ErrorHelper.handleError(
- HaloApp.getInstance().application,
- exception.response().errorBody()?.string()
- )
+ fun createReportSingle(content: JSONObject): Single
+
+ @SuppressLint("CheckResult")
+ fun postReport(content: JSONObject) {
+ createReportSingle(content)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: ResponseBody) {
+ ToastUtils.toast("举报成功")
}
- }
- })
+
+ override fun onFailure(exception: Exception) {
+ super.onFailure(exception)
+ if (exception is HttpException) {
+ ErrorHelper.handleError(
+ HaloApp.getInstance().application,
+ exception.response().errorBody()?.string()
+ )
+ }
+ }
+ })
+ }
+ }
+
+ class PostsReporter(private val contentId: String) : CommunityReporter {
+
+ override fun createReportSingle(content: JSONObject): Single =
+ RetrofitManager.getInstance()
+ .api
+ .postBbsReport(contentId, content.toRequestBody())
+ }
+
+ class ImageArticleReporter(private val contentId: String) : CommunityReporter {
+
+ override fun createReportSingle(content: JSONObject): Single =
+ RetrofitManager.getInstance().api
+ .postImageArticleReport(contentId, content.toRequestBody())
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/common/util/CommentHelper.kt b/app/src/main/java/com/gh/common/util/CommentHelper.kt
index 667f987aa1..f5a3d0ba63 100644
--- a/app/src/main/java/com/gh/common/util/CommentHelper.kt
+++ b/app/src/main/java/com/gh/common/util/CommentHelper.kt
@@ -119,6 +119,23 @@ object CommentHelper {
)
}
+ fun showImageArticleCommentOptions(
+ view: View,
+ commentEntity: CommentEntity,
+ imageArticleId: String,
+ isShowTop: Boolean = false,
+ listener: OnCommentOptionClickListener?
+ ) {
+ showCommentOptions(
+ view,
+ commentEntity,
+ imageArticleId = imageArticleId,
+ showConversation = false,
+ isShowTopOrAccept = isShowTop,
+ listener = listener
+ )
+ }
+
private fun showCommentOptions(
view: View,
commentEntity: CommentEntity,
@@ -129,6 +146,7 @@ object CommentHelper {
questionId: String? = null,
videoId: String? = null,
gameCollectionId: String? = null,
+ imageArticleId: String? = null,
isShowTopOrAccept: Boolean = false,
ignoreModerator: Boolean = false,
isVideoAuthor: Boolean = false,
@@ -137,8 +155,7 @@ object CommentHelper {
val context = view.context
val dialogOptions = ArrayList()
val isContentAuthor = commentEntity.me?.isContentAuthor == true
-
- if (isShowTopOrAccept && (articleId != null || questionId != null || videoId != null) && isContentAuthor) {
+ if (isShowTopOrAccept && (articleId != null || questionId != null || videoId != null || imageArticleId != null) && isContentAuthor) {
dialogOptions.add(if (commentEntity.isTop) "取消置顶" else "置顶")
}
if (isShowTopOrAccept && questionId != null && isContentAuthor) {
@@ -229,6 +246,7 @@ object CommentHelper {
commentListener
)
}
+
articleId != null -> {
PostCommentUtils.reportCommunityArticleComment(
commentEntity.id,
@@ -236,6 +254,7 @@ object CommentHelper {
commentListener
)
}
+
questionId != null -> {
PostCommentUtils.reportQuestionComment(
questionId,
@@ -244,6 +263,7 @@ object CommentHelper {
commentListener
)
}
+
gameCollectionId != null -> {
PostCommentUtils.reportGameCollectionComment(
gameCollectionId,
@@ -252,6 +272,15 @@ object CommentHelper {
commentListener
)
}
+
+ imageArticleId != null -> {
+ PostCommentUtils.reportImageArticleComment(
+ commentEntity.id,
+ reportType,
+ commentListener
+ )
+ }
+
else -> {
PostCommentUtils.reportVideoComment(
videoId,
@@ -287,6 +316,17 @@ object CommentHelper {
null
)
)
+ } else if (imageArticleId != null) {
+ context.startActivity(
+ CommentDetailActivity
+ .getImageArticleCommentIntent(
+ context,
+ imageArticleId,
+ commentEntity.id,
+ communityId,
+ null
+ )
+ )
} else {
context.startActivity(
CommentDetailActivity
diff --git a/app/src/main/java/com/gh/common/util/CommentUtils.java b/app/src/main/java/com/gh/common/util/CommentUtils.java
index af4da8c50d..0d45262734 100644
--- a/app/src/main/java/com/gh/common/util/CommentUtils.java
+++ b/app/src/main/java/com/gh/common/util/CommentUtils.java
@@ -305,6 +305,7 @@ public class CommentUtils {
String articleCommunityId,
String videoId,
String questionId,
+ String imageArticleId,
final CommentEntity commentEntity,
final TextView commentLikeCountTv,
final ImageView commentLikeIv,
@@ -328,7 +329,7 @@ public class CommentUtils {
commentLikeCountTv.setText(NumberUtils.transSimpleCount(commentEntity.getVote()));
commentLikeCountTv.setVisibility(View.VISIBLE);
- PostCommentUtils.likeComment(answerId, articleId, videoId, questionId, commentEntity.getId(),
+ PostCommentUtils.likeComment(answerId, articleId, videoId, questionId, imageArticleId, commentEntity.getId(),
new PostCommentUtils.PostCommentListener() {
@Override
public void postSuccess(JSONObject response) {
@@ -374,6 +375,7 @@ public class CommentUtils {
String articleId,
String articleCommunityId,
String videoId,
+ String imageArticleId,
final CommentEntity commentEntity,
final TextView commentLikeCountTv,
final ImageView commentLikeIv,
@@ -382,7 +384,7 @@ public class CommentUtils {
String entrance = "视频流-评论-点赞";
CheckLoginUtils.checkLogin(context, entrance, () -> {
- PostCommentUtils.likeComment(answerId, articleId, videoId, "", commentEntity.getId(),
+ PostCommentUtils.likeComment(answerId, articleId, videoId, "", imageArticleId, commentEntity.getId(),
new PostCommentUtils.PostCommentListener() {
@Override
public void postSuccess(JSONObject response) {
diff --git a/app/src/main/java/com/gh/common/util/DirectUtils.kt b/app/src/main/java/com/gh/common/util/DirectUtils.kt
index 4b0583a25b..5bb60656f8 100644
--- a/app/src/main/java/com/gh/common/util/DirectUtils.kt
+++ b/app/src/main/java/com/gh/common/util/DirectUtils.kt
@@ -568,6 +568,10 @@ object DirectUtils {
}
}
+ "image_article" -> ARouter.getInstance().build(RouteConsts.activity.imageArticleDetailActivity)
+ .withString(KEY_IMAGE_ARTICLE_ID, linkEntity.link)
+ .navigation()
+
"" -> {
// do nothing
}
diff --git a/app/src/main/java/com/gh/common/util/PostCommentUtils.java b/app/src/main/java/com/gh/common/util/PostCommentUtils.java
index 8e0b9e94ca..7ce612976e 100644
--- a/app/src/main/java/com/gh/common/util/PostCommentUtils.java
+++ b/app/src/main/java/com/gh/common/util/PostCommentUtils.java
@@ -77,6 +77,7 @@ public class PostCommentUtils {
String articleId,
String videoId,
String questionId,
+ final String imageArticleId,
final String commentId,
final PostCommentListener listener) {
@@ -88,6 +89,8 @@ public class PostCommentUtils {
observable = RetrofitManager.getInstance().getApi().postVoteCommunityArticleComment(commentId);
} else if (!TextUtils.isEmpty(questionId)) {
observable = RetrofitManager.getInstance().getApi().postVoteQuestionComment(questionId, commentId);
+ } else if (!TextUtils.isEmpty(imageArticleId)) {
+ observable = RetrofitManager.getInstance().getApi().postVoteToImageArticle(commentId);
} else {
observable = RetrofitManager.getInstance().getApi().postVoteToVideo(videoId, commentId);
}
@@ -116,6 +119,7 @@ public class PostCommentUtils {
String articleCommunityId,
String videoId,
String questionId,
+ String imageArticleId,
final String commentId,
final PostCommentListener listener) {
Observable observable;
@@ -124,6 +128,8 @@ public class PostCommentUtils {
observable = RetrofitManager.getInstance().getApi().postUnVoteQuestionComment(questionId, commentId);
} else if (!TextUtils.isEmpty(articleId)) {
observable = RetrofitManager.getInstance().getApi().postUnVoteArticleComment(commentId);
+ } else if (!TextUtils.isEmpty(imageArticleId)) {
+ observable = RetrofitManager.getInstance().getApi().postUnVoteImageArticle(commentId);
} else {
observable = RetrofitManager.getInstance().getApi().postUnVoteVideoComment(videoId, commentId);
}
@@ -300,6 +306,25 @@ public class PostCommentUtils {
});
}
+ public static void reportImageArticleComment(String commentId, String reportData, PostCommentListener listener) {
+ RequestBody body = RequestBody.create(MediaType.parse("application/json"), reportData);
+ RetrofitManager.getInstance().getApi()
+ .reportImageArticleComment(commentId, body)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Response() {
+ @Override
+ public void onResponse(ResponseBody response) {
+ listener.postSuccess(null);
+ }
+
+ @Override
+ public void onFailure(HttpException e) {
+ listener.postFailed(e);
+ }
+ });
+ }
+
public interface PostCommentListener {
void postSuccess(JSONObject response);
diff --git a/app/src/main/java/com/gh/common/view/RedBookBannerIndicatorView.kt b/app/src/main/java/com/gh/common/view/RedBookBannerIndicatorView.kt
new file mode 100644
index 0000000000..5e5022be67
--- /dev/null
+++ b/app/src/main/java/com/gh/common/view/RedBookBannerIndicatorView.kt
@@ -0,0 +1,268 @@
+package com.gh.common.view
+
+import android.animation.Animator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.View
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.toColor
+
+class RedBookBannerIndicatorView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+) : View(context, attrs, defStyleAttr) {
+
+ private var defaultColor = R.color.text_instance.toColor(context)
+ private var selectedColor = R.color.primary_theme.toColor(context)
+ private var largeRadius = 2F.dip2px().toFloat()
+ private var smallRadius = 1.2F.dip2px().toFloat()
+ private var distanceBetweenCenters = 8F.dip2px().toFloat()
+ private var maxShowDotCount = 5
+
+ private var lastPosition = 0
+ private var currentPosition = 0
+ private var firstShowPosition = 0
+ private var lastShowPosition = 0
+
+ private var animationProgress = 0F
+
+ private var animationState: AnimationState = AnimationState.No
+
+ private var valueAnimator: ValueAnimator? = null
+
+ private val paint by lazy {
+ Paint().apply {
+ style = Paint.Style.FILL
+ isAntiAlias = true
+ }
+ }
+
+ init {
+ initAttrs(context, attrs)
+ }
+
+ private fun initAttrs(context: Context, attrs: AttributeSet?) {
+ val typedArray = context.obtainStyledAttributes(attrs, R.styleable.RedBookBannerIndicatorView)
+ defaultColor = typedArray.getColor(R.styleable.RedBookBannerIndicatorView_default_color, defaultColor)
+ selectedColor = typedArray.getColor(R.styleable.RedBookBannerIndicatorView_selected_color, selectedColor)
+ largeRadius = typedArray.getDimension(R.styleable.RedBookBannerIndicatorView_large_radius, largeRadius)
+ smallRadius = typedArray.getDimension(R.styleable.RedBookBannerIndicatorView_small_radius, smallRadius)
+ distanceBetweenCenters = typedArray.getDimension(
+ R.styleable.RedBookBannerIndicatorView_distance_between_centers,
+ distanceBetweenCenters
+ )
+ maxShowDotCount =
+ typedArray.getInt(R.styleable.RedBookBannerIndicatorView_max_show_dot_count, maxShowDotCount)
+ typedArray.recycle()
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ val width = if (lastPosition >= maxShowDotCount) {
+ (maxShowDotCount - 1) * distanceBetweenCenters + 2 * largeRadius
+ } else {
+ lastShowPosition * distanceBetweenCenters + 2 * largeRadius
+ }
+ val height = largeRadius * 2
+ setMeasuredDimension(width.toInt(), height.toInt())
+ }
+
+ fun show(total: Int) {
+ valueAnimator?.cancel()
+ lastPosition = total - 1
+ lastShowPosition = if (total > maxShowDotCount) {
+ maxShowDotCount - 1
+ } else {
+ lastPosition
+ }
+ requestLayout()
+ }
+
+ /**
+ * 请注意,selectPosition只能一步一步跳
+ * 比如:当currentPosition ==0 时,selectPosition为2,仍然只会往前跳一步,最终 currentPosition == 1
+ */
+ fun selectPosition(position: Int) {
+ if (currentPosition == position || position < 0 || position > lastPosition) {
+ return
+ }
+ valueAnimator?.cancel()
+ if (position > currentPosition) {
+ currentPosition++
+ } else if (position < currentPosition) {
+ currentPosition--
+ }
+
+ when (currentPosition) {
+ 0 -> {
+ firstShowPosition = 0
+ lastShowPosition = maxShowDotCount - 1
+ if (lastShowPosition > lastPosition) {
+ lastShowPosition = lastPosition
+ }
+ }
+
+ lastPosition -> {
+ lastShowPosition = lastPosition
+ firstShowPosition = lastPosition - maxShowDotCount + 1
+ if (firstShowPosition < 0) {
+ firstShowPosition = (0)
+ }
+ }
+
+ firstShowPosition -> {
+ animationState = AnimationState.RightReady
+ }
+
+ lastShowPosition -> {
+ animationState = AnimationState.LeftReady
+ }
+
+ }
+ invalidate()
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ // 保存 Canvas 的当前状态
+ canvas.save();
+
+ // 限制绘制区域为 View 的边界
+ canvas.clipRect(0, 0, getWidth(), getHeight());
+ if (lastShowPosition > firstShowPosition) {
+ val first = if (firstShowPosition > 0) {
+ firstShowPosition - 1
+ } else {
+ firstShowPosition
+ }
+ val last = if (lastShowPosition < lastPosition) {
+ lastShowPosition + 1
+ } else {
+ lastPosition
+ }
+
+
+ for (position in first..last) {
+ var radius = when (position) {
+ firstShowPosition -> if (firstShowPosition == 0) {
+ largeRadius
+ } else {
+ smallRadius
+ }
+
+ lastShowPosition -> if (lastShowPosition == lastPosition) {
+ largeRadius
+ } else {
+ smallRadius
+ }
+
+ else -> largeRadius
+ }
+
+ val offset = if (animationState == AnimationState.LeftPlaying) {
+ -animationProgress * distanceBetweenCenters
+ } else {
+ animationProgress * distanceBetweenCenters
+ }
+ val centerX = ((position - firstShowPosition) * distanceBetweenCenters + largeRadius) + offset
+ paint.color = if (position == currentPosition) {
+ selectedColor
+ } else {
+ defaultColor
+ }
+
+ radius = getScaleRadius(position, radius)
+ canvas.drawCircle(centerX, largeRadius, radius, paint)
+ }
+
+ if (animationState == AnimationState.LeftReady || animationState == AnimationState.RightReady) {
+ startAnimation()
+ }
+ }
+ // 恢复 Canvas 到之前的状态
+ canvas.restore();
+ }
+
+ private fun getScaleRadius(position: Int, radius: Float): Float {
+ var newRadius = radius
+ if (animationState == AnimationState.LeftPlaying) {
+ if (position - 1 == firstShowPosition) {
+ newRadius = largeRadius - (largeRadius - smallRadius) * animationProgress
+ }
+ if (position == lastShowPosition) {
+ newRadius = smallRadius + (largeRadius - smallRadius) * animationProgress
+ }
+ }
+ if (animationState == AnimationState.RightPlaying) {
+ if (position == firstShowPosition) {
+ newRadius = smallRadius + (largeRadius - smallRadius) * animationProgress
+ }
+ if (position + 1 == lastShowPosition) {
+ newRadius = largeRadius - (largeRadius - smallRadius) * animationProgress
+ }
+ }
+ return newRadius
+ }
+
+ /**
+ * 执行向左的平移动画
+ */
+ private fun startAnimation() {
+ animationState = if (animationState == AnimationState.LeftReady) {
+ AnimationState.LeftPlaying
+ } else {
+ AnimationState.RightPlaying
+ }
+ fun resetShowPosition() {
+ if (animationState == AnimationState.LeftPlaying) {
+ firstShowPosition++
+ lastShowPosition++
+ } else {
+ firstShowPosition--
+ lastShowPosition--
+ }
+ animationState = AnimationState.No
+ animationProgress = 0F
+ invalidate()
+ }
+ valueAnimator = ValueAnimator.ofFloat(0F, 1F).apply {
+ setDuration(ANIMATION_DURATION)
+ addUpdateListener {
+ val progress = it.animatedValue as Float
+ animationProgress = progress
+ invalidate()
+ }
+ addListener(object : Animator.AnimatorListener {
+ override fun onAnimationStart(animation: Animator) = Unit
+
+ override fun onAnimationEnd(animation: Animator) {
+ resetShowPosition()
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ resetShowPosition()
+ }
+
+ override fun onAnimationRepeat(animation: Animator) = Unit
+
+ })
+ start()
+ }
+ }
+
+ companion object {
+ private const val ANIMATION_DURATION = 300L
+ }
+
+ sealed class AnimationState {
+ object No : AnimationState()
+ object LeftReady : AnimationState()
+ object RightReady : AnimationState()
+ object LeftPlaying : AnimationState()
+ object RightPlaying : AnimationState()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/common/view/VoteStateView.kt b/app/src/main/java/com/gh/common/view/VoteStateView.kt
new file mode 100644
index 0000000000..a9c8bca371
--- /dev/null
+++ b/app/src/main/java/com/gh/common/view/VoteStateView.kt
@@ -0,0 +1,107 @@
+package com.gh.common.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.OnClickListener
+import android.widget.LinearLayout
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.retrofit.BiResponse
+import com.gh.gamecenter.common.utils.doOnAnimationEnd
+import com.gh.gamecenter.common.utils.singleToMain
+import com.gh.gamecenter.common.utils.toColor
+import com.gh.gamecenter.common.utils.toResString
+import com.gh.gamecenter.core.utils.ToastUtils
+import com.gh.gamecenter.databinding.LayoutVoteBinding
+import com.gh.gamecenter.entity.VoteEntity
+import com.gh.gamecenter.forum.home.recommend.ImageArticleUseCase.Companion.notifyVoteChanged
+import com.gh.gamecenter.livedata.Event
+
+class VoteStateView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr) {
+
+ private var binding: LayoutVoteBinding = LayoutVoteBinding.inflate(LayoutInflater.from(context), this, true)
+
+ private var status = IMAGE_ARTICLE_PENDING
+
+ private var isAnimatorPlaying = false
+
+ private var outClickListener: OnClickListener? = null
+
+ private val inClickListener = OnClickListener {
+ if (isAnimatorPlaying) {
+ return@OnClickListener
+ }
+ when (status) {
+ IMAGE_ARTICLE_PENDING -> {
+ ToastUtils.showToast(R.string.content_pending_status.toResString())
+ }
+
+ IMAGE_ARTICLE_FAIL -> {
+ ToastUtils.showToast(R.string.fail_status.toResString())
+ }
+
+ IMAGE_ARTICLE_PASS -> {
+ outClickListener?.onClick(it)
+ if (!binding.ivVote.isChecked) {
+ playVoteAnimation()
+ }
+ }
+ }
+
+ }
+
+ fun setVote(isVote: Boolean, count: Int, status: String) {
+ this.status = status
+ binding.ivVote.isChecked = isVote
+ binding.tvVoteCount.text = getVoteCountText(count, context)
+ val textColorResId = if (isVote) {
+ R.color.text_theme
+ } else {
+ R.color.text_secondary
+ }
+ binding.tvVoteCount.setTextColor(textColorResId.toColor(context))
+ }
+
+ override fun setOnClickListener(l: OnClickListener?) {
+ outClickListener = l
+ super.setOnClickListener(inClickListener)
+ }
+
+ private fun getVoteCountText(count: Int, context: Context) =
+ when {
+ count <= 0 -> R.string.like.toResString()
+ count in 1 until VOTE_TEN_THOUSAND -> "$count"
+ else -> context.getString(R.string.ten_thousand, "$count")
+ }
+
+ private fun playVoteAnimation() {
+ with(binding) {
+ isAnimatorPlaying = true
+ tvVoteCount.setTextColor(R.color.text_theme.toColor(context))
+ ivVote.isChecked = true
+ ivVote.visibility = View.INVISIBLE
+ voteAnimation.visibility = View.VISIBLE
+ voteAnimation.setAnimation("lottie/community_vote.json")
+ voteAnimation.playAnimation()
+ voteAnimation.doOnAnimationEnd {
+ voteAnimation.visibility = View.GONE
+ ivVote.visibility = View.VISIBLE
+ isAnimatorPlaying = false
+ }
+ }
+
+ }
+
+ companion object {
+ private const val VOTE_TEN_THOUSAND = 10000
+
+ private const val IMAGE_ARTICLE_PENDING = "pending"
+ private const val IMAGE_ARTICLE_FAIL = "fail"
+ private const val IMAGE_ARTICLE_PASS = "pass"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/CommentDetailActivity.java b/app/src/main/java/com/gh/gamecenter/CommentDetailActivity.java
index cbde880161..7add7c0268 100644
--- a/app/src/main/java/com/gh/gamecenter/CommentDetailActivity.java
+++ b/app/src/main/java/com/gh/gamecenter/CommentDetailActivity.java
@@ -76,6 +76,19 @@ public class CommentDetailActivity extends ToolBarActivity {
return getTargetIntent(context, CommentDetailActivity.class, NewCommentConversationFragment.class, args);
}
+ public static Intent getImageArticleCommentIntent(Context context,
+ String articleId,
+ String articleCommentId,
+ String communityId,
+ LinkEntity linkEntity) {
+ Bundle args = new Bundle();
+ args.putString(CommentActivity.IMAGE_ARTICLE_ID, articleId);
+ args.putString(EntranceConsts.KEY_COMMENTID, articleCommentId);
+ args.putString(CommentActivity.COMMUNITY_ID, communityId);
+ args.putParcelable(EntranceConsts.KEY_LINK, linkEntity);
+ return getTargetIntent(context, CommentDetailActivity.class, NewCommentConversationFragment.class, args);
+ }
+
public static Intent getVideoCommentIntent(Context context,
String commentId,
String videoId,
diff --git a/app/src/main/java/com/gh/gamecenter/entity/ImageArticleEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ImageArticleEntity.kt
new file mode 100644
index 0000000000..7314acf2c5
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/entity/ImageArticleEntity.kt
@@ -0,0 +1,224 @@
+package com.gh.gamecenter.entity
+
+import android.os.Parcelable
+import com.gh.gamecenter.common.entity.CommunityEntity
+import com.gh.gamecenter.feature.entity.*
+import com.gh.gamecenter.feature.entity.TimeEntity
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.IgnoredOnParcel
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class ImageArticleEntity(
+ @SerializedName("_id")
+ private var _id: String? = null,
+ @SerializedName("title")
+ private var _title: String? = null,
+ @SerializedName("content")
+ private val _content: String? = null,
+ @SerializedName("community_id")
+ private var _communityId: String? = null,
+ @SerializedName("community_type")
+ private val _communityType: String? = null,
+ @SerializedName("sections")
+ private val _sections: List? = null,
+ @SerializedName("images")
+ private var _images: List? = null,
+ @SerializedName("images_info")
+ private val _imagesInfos: List? = null,
+ @SerializedName("community")
+ val community: Community? = null,
+ @SerializedName("user")
+ private val _user: UserEntity? = null,
+ @SerializedName("time")
+ private val _time: TimeEntity? = null,
+ @SerializedName("status")
+ private val _status: String? = null,
+ @SerializedName("source")
+ private val _source: SourceEntity? = null,
+ @SerializedName("count")
+ private val _count: Count? = null,
+ @SerializedName("me")
+ private val _me: MeEntity? = null,
+ @SerializedName("_seq")
+ private val _shortId: String? = null,
+ @SerializedName("choiceness_status")
+ private var _choicenessStatus: String? = null,// 精选状态 apply(申请), pass already(已精选) cancel not_yet(未精选)
+ /**
+ * 临时变量,区分是编辑发布还是编辑草稿
+ */
+ var isDraft: Boolean = false
+) : Parcelable {
+
+ val id: String
+ get() = _id ?: ""
+
+ val title: String
+ get() = _title ?: ""
+
+ val content: String
+ get() = _content ?: ""
+
+ val communityId: String
+ get() = _communityId ?: ""
+
+ val communityType: String
+ get() = _communityType ?: ""
+
+ val sections: List
+ get() = _sections ?: emptyList()
+
+ val images: List
+ get() = _images ?: emptyList()
+
+ val imagesInfos: List
+ get() = _imagesInfos ?: emptyList()
+
+ val user: UserEntity
+ get() = _user ?: UserEntity()
+
+ val time: TimeEntity
+ get() = _time ?: TimeEntity()
+
+ val status: String
+ get() = _status ?: "pending"
+
+ val source: SourceEntity
+ get() = _source ?: SourceEntity()
+
+ val count: Count
+ get() = _count ?: Count()
+
+ val me: MeEntity
+ get() = _me ?: MeEntity()
+
+ val shortId: String
+ get() = _shortId ?: ""
+
+ var choicenessStatus: String
+ get() = _choicenessStatus ?: ""
+ set(value) {
+ _choicenessStatus = value
+ }
+
+ fun getSimplifyChoicenessStatus(): String {
+ return when (choicenessStatus) {
+ "already" -> "pass"
+ "not_yet" -> "cancel"
+ else -> choicenessStatus ?: ""
+ }
+ }
+
+ companion object {
+
+ const val IMAGE_ARTICLE_TYPE = "image_article"
+
+ fun getImageRadio(imageInfo: ImageInfo?, minRadio: Float, maxRadio: Float): Float {
+ return if (imageInfo != null && imageInfo.width > 0 && imageInfo.height > 0) {
+ val imageRadio = imageInfo.height.toFloat() / imageInfo.width
+ when {
+ imageRadio < minRadio -> {
+ minRadio
+ }
+
+ imageRadio in 0.5..1.33 -> {
+ imageRadio
+ }
+
+ else -> {
+ maxRadio
+ }
+ }
+ } else {
+ minRadio
+ }
+
+ }
+ }
+
+ @Parcelize
+ data class Community(
+ @SerializedName("_id")
+ private val _id: String? = null,
+ @SerializedName("name")
+ private val _name: String? = null,
+ @SerializedName("icon")
+ private val _icon: String? = null,
+ @SerializedName("hot")
+ private val _hot: Int? = null,
+ @SerializedName("game")
+ val game: GameEntity? = null,
+ @SerializedName("type")
+ private val _type: String? = null,
+ @SerializedName("me")
+ private val _me: MeEntity?
+ ) : Parcelable {
+
+ val id: String
+ get() = _id ?: ""
+
+ val name: String
+ get() = _name ?: ""
+
+ val icon: String
+ get() = _icon ?: ""
+
+ val hot: Int
+ get() = _hot ?: 0
+
+ val type: String
+ get() = _type ?: "game_bbs"
+
+ val typeChinese: String
+ get() = when (type) {
+ "game_bbs" -> "游戏论坛"
+ else -> "综合论坛"
+ }
+
+ var isFollowed: Boolean
+ get() = _me?.isFollower ?: false
+ set(value) {
+ _me?.isFollower = value
+ }
+
+ fun toCommunityEntity() = CommunityEntity(
+ id = id,
+ name = name,
+ icon = icon,
+ type = type,
+ )
+ }
+
+ fun toPersonHistoryEntity() = PersonalHistoryEntity().also {
+ it.id = id
+ it.title = title
+ it.brief = content
+ it.community = community?.toCommunityEntity() ?: CommunityEntity()
+ it.sections = sections
+ it.images = images
+ it.imagesInfo = imagesInfos
+ it.count = count
+ it.user = user
+ it.time = time.update
+ it.status = status
+ it.me = me
+ }
+
+ fun toAnswerEntity() = AnswerEntity().also {
+ it.id = id
+ it.title = title
+ it.brief = content
+ it.content = content
+ it.community = community?.toCommunityEntity() ?: CommunityEntity()
+ it.sections = sections
+ it.images = images
+ it.imagesInfo = imagesInfos
+ it.count = count
+ it.user = user
+ it.time = time.update
+ it.status = status
+ it.me = me
+ it.type = IMAGE_ARTICLE_TYPE
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt
index 82516c3a2d..d2d69edbfb 100644
--- a/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt
+++ b/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt
@@ -36,12 +36,13 @@ data class PersonalHistoryEntity(
var length: Long = 0,
@SerializedName("count")
private var _count: Count = Count(),
- val time: Long = 0,
+ var time: Long = 0,
@SerializedName("title")
+
private var _title: String = "",
var description: String = "",
@SerializedName("community")
- private var _community: CommunityEntity = CommunityEntity(),
+ private var _community: CommunityEntity? = null,
var videos: List = ArrayList(),
@SerializedName("user")
private var _user: UserEntity? = null,
@@ -63,8 +64,17 @@ data class PersonalHistoryEntity(
@SerializedName("sections")
@Ignore
private var _sections: List = ArrayList(),
+
+ // 图文新增字段
+ @SerializedName("content")
+ private val _content: String? = null,
+ @SerializedName("source")
+ private val _source: SourceEntity? = null,
) : Parcelable, CommunityItemData {
+ val content: String
+ get() = _content ?: ""
+
override var id: String
get() = _id
set(value) {
@@ -104,7 +114,7 @@ data class PersonalHistoryEntity(
}
override var community: CommunityEntity
- get() = _community
+ get() = _community ?: CommunityEntity()
set(value) {
_community = value
}
diff --git a/app/src/main/java/com/gh/gamecenter/entity/PublishImageTextRequest.kt b/app/src/main/java/com/gh/gamecenter/entity/PublishImageTextRequest.kt
new file mode 100644
index 0000000000..120e146473
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/entity/PublishImageTextRequest.kt
@@ -0,0 +1,17 @@
+package com.gh.gamecenter.entity
+
+import com.google.gson.annotations.SerializedName
+
+data class PublishImageTextRequest(
+ val title: String,
+ val content: String,
+ @SerializedName("community_type")
+ val communityType: String,
+ @SerializedName("community_id")
+ val communityId: String,
+ @SerializedName("section_id")
+ val sectionId: List,
+ var images: List = listOf(),
+ @SerializedName("draft_id")
+ val draftId: String = ""
+)
diff --git a/app/src/main/java/com/gh/gamecenter/entity/VoteEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/VoteEntity.kt
index 07d175c4d3..bd9a6b0221 100644
--- a/app/src/main/java/com/gh/gamecenter/entity/VoteEntity.kt
+++ b/app/src/main/java/com/gh/gamecenter/entity/VoteEntity.kt
@@ -2,8 +2,15 @@ package com.gh.gamecenter.entity
import com.google.gson.annotations.SerializedName
-class VoteEntity(
- var vote: Int? = 0,
+data class VoteEntity(
+ @SerializedName("vote")
+ private val _vote: Int? = null,
@SerializedName("is_guide_follow")
- var isGuideFollow: Boolean = false
-)
\ No newline at end of file
+ private var _isGuideFollow: Boolean? = null
+) {
+ val vote: Int
+ get() = _vote ?: 0
+
+ val isGuideFollow: Boolean
+ get() = _isGuideFollow ?: false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/eventbus/EBImageArticleChanged.kt b/app/src/main/java/com/gh/gamecenter/eventbus/EBImageArticleChanged.kt
new file mode 100644
index 0000000000..d4c8e6dc8e
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/eventbus/EBImageArticleChanged.kt
@@ -0,0 +1,16 @@
+package com.gh.gamecenter.eventbus
+
+import com.gh.gamecenter.entity.ImageArticleEntity
+
+sealed class EBImageArticleChanged {
+
+ data class VoteChanged(val id: String, val isVoted: Boolean, val count: Int) : EBImageArticleChanged()
+
+ data class CommentChanged(val id: String, val count: Int) : EBImageArticleChanged()
+
+ data class DataChanged(val data: ImageArticleEntity) : EBImageArticleChanged()
+
+ data class DataDeleted(val imageArticleId: String) : EBImageArticleChanged()
+
+ data class DataCreated(val imageArticle: ImageArticleEntity, val draftId: String = "") : EBImageArticleChanged()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListAdapter.kt
index 2731b9ed89..e71f8f633b 100644
--- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListAdapter.kt
+++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListAdapter.kt
@@ -6,6 +6,7 @@ import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
+import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.constant.ItemViewType
import com.gh.gamecenter.common.syncpage.ISyncAdapterHandler
@@ -14,6 +15,8 @@ import com.gh.common.util.NewLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.viewholder.FooterViewHolder
import com.gh.gamecenter.common.baselist.ListAdapter
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
import com.gh.gamecenter.common.entity.CommunityEntity
@@ -21,6 +24,7 @@ import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.databinding.ItemForumArticleHeadBinding
+import com.gh.gamecenter.entity.ImageArticleEntity
import com.gh.gamecenter.forum.home.ForumArticleAskItemViewHolder
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.feature.entity.AnswerEntity
@@ -47,7 +51,7 @@ class ForumArticleAskListAdapter(
}
override fun areItemsTheSame(oldItem: AnswerEntity?, newItem: AnswerEntity?): Boolean {
- return oldItem?.id == newItem?.id
+ return oldItem?.id == newItem?.id && oldItem?.type == newItem?.type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
@@ -55,9 +59,11 @@ class ForumArticleAskListAdapter(
ItemViewType.ITEM_HEADER -> {
ForumArticleHeadViewHolder(parent.toBinding())
}
+
ItemViewType.ITEM_FOOTER -> {
FooterViewHolder(mLayoutInflater.inflate(com.gh.gamecenter.common.R.layout.refresh_footerview, parent, false))
}
+
else -> {
ForumArticleAskItemViewHolder(
CommunityAnswerItemBinding.bind(
@@ -95,7 +101,8 @@ class ForumArticleAskListAdapter(
questions.answerCount = answer.count.answer
answer.questions = questions
}
- if (path == "精华" && answer.type != "answer" && answer.type != "video") answer.type = "community_article"
+ if (path == "精华" && answer.type != "answer" && answer.type != "video") answer.type =
+ "community_article"
if (path == "问答") answer.type = "question"
if (path == "视频") answer.type = "video"
@@ -148,6 +155,7 @@ class ForumArticleAskListAdapter(
)
)
}
+
"video" -> {
NewLogUtils.logForumDetailFeedContentClick(
"click_forum_detail_content",
@@ -166,6 +174,7 @@ class ForumArticleAskListAdapter(
)
mContext.startActivity(ForumVideoDetailActivity.getIntent(mContext, answer.id, bbsId, "论坛详情-信息流"))
}
+
"question" -> {
NewLogUtils.logForumDetailFeedContentClick(
"click_forum_detail_content",
@@ -188,6 +197,7 @@ class ForumArticleAskListAdapter(
)
)
}
+
"answer" -> {
NewLogUtils.logForumDetailFeedContentClick(
"click_forum_detail_content",
@@ -215,9 +225,16 @@ class ForumArticleAskListAdapter(
)
)
}
+
+ ImageArticleEntity.IMAGE_ARTICLE_TYPE -> {
+ ARouter.getInstance().build(RouteConsts.activity.imageArticleDetailActivity)
+ .withString(EntranceConsts.KEY_IMAGE_ARTICLE_ID, answer.id)
+ .navigation()
+ }
}
}
}
+
ItemViewType.ITEM_FOOTER -> {
val footerViewHolder = holder as FooterViewHolder
footerViewHolder.initItemPadding()
@@ -225,6 +242,7 @@ class ForumArticleAskListAdapter(
footerViewHolder.hint.textSize = 12f
footerViewHolder.hint.setTextColor(ContextCompat.getColor(mContext, com.gh.gamecenter.common.R.color.aaaaaa))
}
+
ItemViewType.ITEM_HEADER -> {
if (holder is ForumArticleHeadViewHolder) {
val articleListHead = if (path == "全部") {
diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt
index d7a09bd5a6..d2e171c0a8 100644
--- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt
@@ -10,6 +10,7 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.common.baselist.LazyListFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.utils.safelyGetInRelease
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.tryCatchInRelease
@@ -18,10 +19,13 @@ import com.gh.gamecenter.core.iinterface.IScrollable
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.databinding.FragmentForumArticleAskListBinding
import com.gh.gamecenter.entity.ForumDetailEntity
+import com.gh.gamecenter.entity.ImageArticleEntity
import com.gh.gamecenter.eventbus.EBDeleteDetail
+import com.gh.gamecenter.eventbus.EBImageArticleChanged
import com.gh.gamecenter.eventbus.EBUserFollow
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper
+import com.gh.gamecenter.personalhome.home.UserHistoryAdapter.Companion.USER_HISTORY_PAYLOADS_VOTE_CHANGED
import com.gh.gamecenter.video.detail.CustomManager
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
import org.greenrobot.eventbus.Subscribe
@@ -132,6 +136,73 @@ class ForumArticleAskListFragment : LazyListFragment {
+ updateImageArticleItem(changed.id, true) {
+ val newCount = it.count
+ newCount.vote = changed.count
+ it.count = newCount
+ it.me.isCommunityArticleVote = changed.isVoted
+ }
+ }
+
+ changed is EBImageArticleChanged.CommentChanged -> {
+ updateImageArticleItem(changed.id, true) {
+ val newCount = it.count
+ newCount.comment = changed.count
+ it.count = newCount
+ }
+ }
+
+ changed is EBImageArticleChanged.DataDeleted -> {
+ mViewModel?.deleteImageArticle(changed.imageArticleId)
+ }
+
+ changed is EBImageArticleChanged.DataChanged -> {
+ val imageArticle = changed.data
+ updateImageArticleItem(changed.data.id, false) {
+ it.title = imageArticle.title
+ it.brief = imageArticle.content
+ it.images = imageArticle.images
+ it.imagesInfo = imageArticle.imagesInfos
+ it.community = imageArticle.community?.toCommunityEntity() ?: CommunityEntity()
+ it.sections = imageArticle.sections
+ it.count = imageArticle.count
+ it.user = imageArticle.user
+ it.time = imageArticle.time.update
+ it.status = imageArticle.status
+ }
+ }
+
+ changed is EBImageArticleChanged.DataCreated -> {
+ println("kayn -->DataCreated:${changed.imageArticle}")
+ mViewModel?.addNewestImageArticle(changed.imageArticle.toAnswerEntity())
+ }
+ }
+ }
+
+ private fun updateImageArticleItem(
+ id: String,
+ hasPayload: Boolean,
+ block: (AnswerEntity) -> Unit
+ ) {
+ val dataList = mAdapter?.entityList ?: return
+ val position =
+ dataList.indexOfFirst { it.type == ImageArticleEntity.IMAGE_ARTICLE_TYPE && it.id == id }
+ if (position != -1) {
+ val item = dataList[position]
+ block(item)
+ if (hasPayload) {
+ mAdapter?.notifyItemChanged(position, USER_HISTORY_PAYLOADS_VOTE_CHANGED)
+ } else {
+ mAdapter?.notifyItemChanged(position)
+ }
+
+ }
+ }
+
override fun onLoadRefresh() {
super.onLoadRefresh()
if (::mBinding.isInitialized) {
diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListViewModel.kt
index caa540a02e..682722b5d4 100644
--- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListViewModel.kt
+++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListViewModel.kt
@@ -4,11 +4,13 @@ import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.common.baselist.ListViewModel
+import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.core.utils.PageSwitchDataHelper
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.gamecenter.entity.ForumDetailEntity
+import com.gh.gamecenter.entity.ImageArticleEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.retrofit.RetrofitManager
@@ -58,9 +60,11 @@ class ForumArticleAskListViewModel(application: Application, val bbsId: String =
}
}
+
"精华" -> {
RetrofitManager.getInstance().api.getEssenceForumList(bbsId, page)
}
+
"问答" -> {
RetrofitManager.getInstance().api.getAskForumList(
bbsId,
@@ -68,6 +72,7 @@ class ForumArticleAskListViewModel(application: Application, val bbsId: String =
page
)
}
+
else -> {
RetrofitManager.getInstance().api.getVideoForumList(
bbsId,
@@ -94,6 +99,28 @@ class ForumArticleAskListViewModel(application: Application, val bbsId: String =
}
}
+ fun deleteImageArticle(imageArticleId: String) {
+ val oldData = mResultLiveData.value ?: return
+ val newData = oldData.toMutableList()
+ val hasRemoved =
+ newData.removeAll { it.type == ImageArticleEntity.IMAGE_ARTICLE_TYPE && it.id == imageArticleId }
+ if (hasRemoved) {
+ mResultLiveData.value = newData
+ }
+ }
+
+ fun addNewestImageArticle(item: AnswerEntity) {
+ val oldData = mResultLiveData.value ?: return
+ val newData = oldData.toMutableList()
+ if (newData.isEmpty()) {
+ load(LoadType.REFRESH)
+ } else {
+ newData.add(1, item)
+ mResultLiveData.value = newData
+ }
+
+ }
+
class Factory(private val bbsId: String, val path: String) : ViewModelProvider.NewInstanceFactory() {
override fun create(modelClass: Class): T {
return ForumArticleAskListViewModel(HaloApp.getInstance().application, bbsId, path) as T
diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt
index c98302b75a..72b3802754 100644
--- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt
@@ -35,6 +35,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
import androidx.viewpager.widget.PagerAdapter
+import com.alibaba.android.arouter.launcher.ARouter
import com.ethanhua.skeleton.Skeleton
import com.ethanhua.skeleton.ViewSkeletonScreen
import com.facebook.drawee.drawable.ScalingUtils
@@ -56,6 +57,7 @@ import com.gh.gamecenter.common.base.fragment.BaseLazyTabFragment
import com.gh.gamecenter.common.callback.BiCallback
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.databinding.ItemIconTabBinding
import com.gh.gamecenter.common.databinding.PopupAllTabsBinding
import com.gh.gamecenter.common.entity.CommunityEntity
@@ -79,6 +81,7 @@ import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.UserEntity
import com.gh.gamecenter.forum.home.CommunityHomeFragment
+import com.gh.gamecenter.forum.home.recommend.PublishImageArticleActivity
import com.gh.gamecenter.forum.moderator.ApplyModeratorActivity
import com.gh.gamecenter.forum.moderator.ModeratorListActivity
import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity
@@ -265,12 +268,14 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
if (!mIsFromTabWrapper) {
ViewCompat.setOnApplyWindowInsetsListener(mBinding.forumAppbar) { _, insets ->
- (mBinding.toolbar.layoutParams as MarginLayoutParams).topMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top
+ (mBinding.toolbar.layoutParams as MarginLayoutParams).topMargin =
+ insets.getInsets(WindowInsetsCompat.Type.systemBars()).top
WindowInsetsCompat.CONSUMED
}
}
if (mIsFromMainWrapper) {
- (mBinding.toolbar.layoutParams as MarginLayoutParams).topMargin = DisplayUtils.getStatusBarHeight(requireContext().resources)
+ (mBinding.toolbar.layoutParams as MarginLayoutParams).topMargin =
+ DisplayUtils.getStatusBarHeight(requireContext().resources)
}
mBinding.toolbar.setNavigationOnClickListener {
NewLogUtils.logForumDetailEnterOrClick("click_forum_detail_return", mBbsId, mBbsType)
@@ -881,7 +886,7 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
forumName = mForumDetail?.name ?: "",
bbsType = mForumDetail?.typeChinese ?: "",
buttonName = mBinding.followTv.text.toString(),
- gameForumType = mForumDetail?.game?.categoryChinese?:""
+ gameForumType = mForumDetail?.game?.categoryChinese ?: ""
)
ifLogin(mEntrance) {
val forumEntity = ForumEntity(
@@ -1089,6 +1094,23 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
}
}
+ contentView.findViewById(R.id.community_edit_recommend_container).setOnClickListener {
+ val icon = if ("official_bbs" == mForumDetail?.type) {
+ mForumDetail?.icon ?: ""
+ } else {
+ mForumDetail?.game?.getIcon()
+ }
+ context?.ifLogin("论坛详情-发布-图文动态", action = {
+ ARouter.getInstance().build(RouteConsts.activity.publishImageArticleActivity)
+ .withParcelable(
+ EntranceConsts.KEY_COMMUNITY_DATA,
+ CommunityEntity(mBbsId, name = mForumDetail?.name ?: "", icon = icon)
+ )
+ .navigation()
+ dialog.dismiss()
+ })
+ }
+
contentView.findViewById(R.id.community_edit_article_container).setOnClickListener {
context?.ifLogin("论坛详情-发布-发帖子", action = {
checkStoragePermissionBeforeAction {
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt
index 965c5401b8..4bdeb8b3eb 100644
--- a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt
@@ -17,11 +17,13 @@ import androidx.core.graphics.ColorUtils
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.SimpleColorFilter
import com.airbnb.lottie.model.KeyPath
import com.airbnb.lottie.value.LottieValueCallback
+import com.alibaba.android.arouter.launcher.ARouter
import com.gh.common.browse.BrowseTimer
import com.gh.common.iinterface.ISuperiorChain
import com.gh.common.prioritychain.CommunityHomeGuideHandler
@@ -36,6 +38,7 @@ import com.gh.gamecenter.common.base.fragment.LazyFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.constant.EntranceConsts.IS_DETAIL_PAGE
+import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.retrofit.ApiResponse
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.AvatarBorderView
@@ -52,6 +55,8 @@ import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.forum.home.follow.FollowHomeFilterPopWindow
import com.gh.gamecenter.forum.home.follow.fragment.FollowHomeFragment
+import com.gh.gamecenter.forum.home.recommend.PublishImageArticleActivity
+import com.gh.gamecenter.forum.home.recommend.fragment.ImageArticleHomeFragment
import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity
import com.gh.gamecenter.login.entity.UserInfoEntity
import com.gh.gamecenter.login.user.UserManager
@@ -127,6 +132,7 @@ class CommunityHomeFragment : LazyFragment() {
}
+
override fun getRealLayoutId(): Int {
return R.layout.fragment_community_home
}
@@ -135,7 +141,6 @@ class CommunityHomeFragment : LazyFragment() {
mBinding = FragmentCommunityHomeBinding.bind(inflatedView)
}
-
override fun onFragmentFirstVisible() {
ArticleDetailWebCacheManager.init(requireContext().applicationContext)
@@ -306,14 +311,14 @@ class CommunityHomeFragment : LazyFragment() {
?: FollowHomeFragment()
mFragmentList.add(followFragment)
- val forumArticleListFragment = childFragmentManager.findFragmentByTag("$tag$TAB_RECOMMEND_INDEX")
- ?: ForumArticleListFragment().with(
+ val recommendFragment = childFragmentManager.findFragmentByTag("$tag$TAB_RECOMMEND_INDEX")
+ ?: ImageArticleHomeFragment.newInstance().with(
bundleOf(
EntranceConsts.KEY_ENTRANCE to "社区",
EntranceConsts.KEY_PATH to "推荐"
)
)
- mFragmentList.add(forumArticleListFragment)
+ mFragmentList.add(recommendFragment)
val forumFragment = childFragmentManager.findFragmentByTag("${tag}$TAB_FORUM_INDEX")
?: ForumFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "社区"))
@@ -587,6 +592,16 @@ class CommunityHomeFragment : LazyFragment() {
UserRepository.getInstance().loginUserInfo.removeObserver(observer)
}
+ contentView.findViewById(R.id.community_edit_recommend_container).setOnClickListener {
+ context?.ifLogin("论坛首页-发布-图文动态", action = {
+ showRegulationTestDialogIfNeeded {
+ NewLogUtils.logArticleEditEnter("推荐信息流", "", "")
+ ARouter.getInstance().build(RouteConsts.activity.publishImageArticleActivity).navigation()
+ }
+ dialog.dismiss()
+ })
+ }
+
contentView.findViewById(R.id.community_edit_article_container).setOnClickListener {
context?.ifLogin("论坛首页-发布-发帖子", action = {
showRegulationTestDialogIfNeeded {
@@ -637,6 +652,7 @@ class CommunityHomeFragment : LazyFragment() {
}
}
+
private fun resetFollowTab() {
mBinding?.tabLayout?.run {
if (selectedTabPosition == TAB_FOLLOW_INDEX) {
@@ -675,13 +691,13 @@ class CommunityHomeFragment : LazyFragment() {
}
private fun insertDataToRecommendTab(entity: ArticleEntity) {
- (mFragmentList[TAB_RECOMMEND_INDEX] as? ForumArticleListFragment)?.insertDataToFirstIndex(entity)
+ // 废弃 后续会逐渐将 activityForResult迁移到 launcher
}
override fun onBackPressed(): Boolean {
mBinding?.viewPager?.run {
if (currentItem == 1) {
- return (mFragmentList[1] as ForumArticleListFragment).onBackPressed()
+ return (mFragmentList[1] as ImageArticleHomeFragment).onBackPressed()
}
}
return super.onBackPressed()
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeViewModel.kt
index 126f6e3168..1a5fc1ec12 100644
--- a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeViewModel.kt
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeViewModel.kt
@@ -8,6 +8,7 @@ import androidx.lifecycle.MutableLiveData
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
+import com.gh.gamecenter.entity.ImageArticleEntity
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.TimeEntity
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleAskItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleAskItemViewHolder.kt
index e8131716cb..4f627d61a5 100644
--- a/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleAskItemViewHolder.kt
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumArticleAskItemViewHolder.kt
@@ -6,6 +6,7 @@ import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.text.SpannableStringBuilder
import android.view.View
+import com.alibaba.android.arouter.launcher.ARouter
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.common.util.*
import com.gh.common.util.DialogUtils
@@ -17,6 +18,8 @@ import com.gh.gamecenter.ImageViewerActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.callback.ConfirmListener
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.utils.*
@@ -24,6 +27,7 @@ import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.SpanBuilder
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
+import com.gh.gamecenter.entity.ImageArticleEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.eventbus.EBUserFollow
import com.gh.gamecenter.forum.detail.ForumDetailActivity
@@ -528,6 +532,12 @@ class ForumArticleAskItemViewHolder(
)
)
}
+
+ ImageArticleEntity.IMAGE_ARTICLE_TYPE -> {
+ ARouter.getInstance().build(RouteConsts.activity.imageArticleDetailActivity)
+ .withString(EntranceConsts.KEY_IMAGE_ARTICLE_ID, entity.id)
+ .navigation()
+ }
}
}
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleDetailActivity.kt
new file mode 100644
index 0000000000..4118325e1a
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleDetailActivity.kt
@@ -0,0 +1,33 @@
+package com.gh.gamecenter.forum.home.recommend
+
+import android.graphics.Color
+import android.os.Bundle
+import com.alibaba.android.arouter.facade.annotation.Route
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.base.activity.BaseActivity
+import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.core.utils.DisplayUtils
+import com.gh.gamecenter.forum.home.recommend.fragment.ImageArticleDetailFragment
+
+@Route(path = RouteConsts.activity.imageArticleDetailActivity)
+class ImageArticleDetailActivity : BaseActivity() {
+
+ override fun getLayoutId(): Int {
+ return R.layout.activity_image_article_detail
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ DisplayUtils.setLightStatusBar(this, true)
+ setStatusBarColor(Color.TRANSPARENT)
+
+ val tag = ImageArticleDetailFragment::class.java.toString()
+ val fragment =
+ supportFragmentManager.findFragmentByTag(tag) ?: ImageArticleDetailFragment().apply {
+ arguments = intent.extras
+ }
+ val transaction = supportFragmentManager.beginTransaction()
+ transaction.replace(R.id.layout_activity_content, fragment, tag)
+ transaction.commitAllowingStateLoss()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleFooterWrapperAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleFooterWrapperAdapter.kt
new file mode 100644
index 0000000000..9c9b1a8d8d
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleFooterWrapperAdapter.kt
@@ -0,0 +1,154 @@
+package com.gh.gamecenter.forum.home.recommend
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil.ItemCallback
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.NO_POSITION
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import androidx.recyclerview.widget.StaggeredGridLayoutManager
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.baselist.PageLoader
+import com.gh.gamecenter.common.databinding.RefreshFooterviewBinding
+import com.gh.gamecenter.forum.home.follow.viewholder.FollowFooterViewHolder
+
+abstract class ImageArticleFooterWrapperAdapter(diffCallback: ItemCallback) :
+ ListAdapter(diffCallback) {
+
+ private var pageState: PageLoader.PageState? = null
+
+ private var _recyclerView: RecyclerView? = null
+
+ val dataCount: Int
+ get() = super.getItemCount()
+
+ fun setPageState(pageState: PageLoader.PageState) {
+ this.pageState = pageState
+ _recyclerView?.post {
+ notifyItemChanged(itemCount - 1)
+ }
+
+ }
+
+ override fun getItemCount(): Int {
+ return if (dataCount > 0) dataCount + 1 else dataCount
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return if (position < dataCount) ITEM_TYPE_DATA else ITEM_TYPE_FOOTER
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ return if (viewType == ITEM_TYPE_DATA) {
+ onCreateDataViewHolder(parent)
+ } else {
+ onCreateFooterViewHolder(parent)
+ }
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ if (holder.itemViewType == ITEM_TYPE_FOOTER) {
+ onBindFooterViewHolder(holder)
+ } else {
+ onBindDataViewHolder(holder as VH, position)
+ }
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList) {
+ if (payloads.isEmpty()) {
+ onBindViewHolder(holder, position)
+ } else {
+ if (holder.itemViewType != ITEM_TYPE_FOOTER) {
+ onBindDataViewHolder(holder as VH, position, payloads)
+ }
+ }
+ }
+
+ open fun onBindFooterViewHolder(holder: ViewHolder) {
+ if (holder is FollowFooterViewHolder) {
+ holder.initFooterViewHolder(
+ pageState == PageLoader.PageState.PageLoadMoreLoading,
+ pageState == PageLoader.PageState.PageLoadMoreFailure,
+ pageState == PageLoader.PageState.PageLoadCompleted,
+ R.string.load_over_with_click_hint
+ ) {
+ if (pageState == PageLoader.PageState.PageLoadCompleted) {
+ _recyclerView?.scrollToPosition(0)
+ } else if (pageState == PageLoader.PageState.PageLoadMoreFailure) {
+ pageState = PageLoader.PageState.PageLoadMoreLoading
+ loadMore()
+ notifyItemChanged(itemCount - 1)
+ }
+ }
+ }
+ }
+
+ abstract fun onCreateDataViewHolder(parent: ViewGroup): VH
+
+
+ open fun onCreateFooterViewHolder(parent: ViewGroup): ViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ val binding = RefreshFooterviewBinding.inflate(inflater, parent, false)
+ val viewHolder = FollowFooterViewHolder(binding)
+ val layoutParams = binding.root.layoutParams
+ if (layoutParams is StaggeredGridLayoutManager.LayoutParams) {
+ layoutParams.isFullSpan = true
+ binding.root.layoutParams = layoutParams
+ }
+ return viewHolder
+ }
+
+ abstract fun onBindDataViewHolder(holder: VH, position: Int)
+
+ open fun onBindDataViewHolder(holder: VH, position: Int, payloads: MutableList) {
+ onBindDataViewHolder(holder, position)
+ }
+
+ abstract fun loadMore()
+
+ private val onLoadMoreListener = object : RecyclerView.OnScrollListener() {
+
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ if (dy != 0) {
+ refreshIfNeed(recyclerView)
+ }
+ }
+
+ override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
+ refreshIfNeed(recyclerView)
+ }
+
+ private fun refreshIfNeed(recyclerView: RecyclerView) {
+ val layoutManger = recyclerView.layoutManager
+ if (layoutManger is StaggeredGridLayoutManager) {
+ val lastVisibleItemPositions = IntArray(2)
+ layoutManger.findLastVisibleItemPositions(lastVisibleItemPositions)
+ val lastVisibleItemPosition = lastVisibleItemPositions.maxOrNull() ?: NO_POSITION
+ if (lastVisibleItemPosition >= itemCount - 3 // 提前两个开始加载下一页
+ && pageState is PageLoader.PageState.PageLoadMoreReady
+ ) {
+ pageState = PageLoader.PageState.PageLoadMoreLoading
+ loadMore()
+ }
+ }
+ }
+
+ }
+
+ override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
+ _recyclerView = recyclerView
+ recyclerView.addOnScrollListener(onLoadMoreListener)
+ }
+
+ override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
+ recyclerView.removeOnScrollListener(onLoadMoreListener)
+ _recyclerView = null
+ }
+
+ companion object {
+
+ private const val ITEM_TYPE_DATA = 0
+ const val ITEM_TYPE_FOOTER = 101
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleRepository.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleRepository.kt
new file mode 100644
index 0000000000..f38b2a062d
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleRepository.kt
@@ -0,0 +1,241 @@
+package com.gh.gamecenter.forum.home.recommend
+
+import com.gh.gamecenter.BuildConfig
+import com.gh.gamecenter.common.constant.Constants
+import com.gh.gamecenter.common.utils.UploadImageUtils
+import com.gh.gamecenter.core.utils.SPUtils
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.entity.PersonalHistoryEntity
+import com.gh.gamecenter.entity.PublishImageTextRequest
+import com.gh.gamecenter.entity.VoteEntity
+import com.gh.gamecenter.feature.entity.CommentEntity
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageAndTextAdapter
+import com.gh.gamecenter.login.user.UserManager
+import com.gh.gamecenter.retrofit.RetrofitManager
+import com.gh.gamecenter.retrofit.service.ApiService
+import com.halo.assistant.HaloApp
+import com.zhihu.matisse.internal.utils.PathUtils
+import io.reactivex.Completable
+import io.reactivex.Observable
+import io.reactivex.Single
+import io.reactivex.schedulers.Schedulers
+import okhttp3.MediaType
+import okhttp3.ResponseBody
+
+class ImageArticleRepository private constructor(
+ private val api: ApiService,
+ private val newApi: ApiService
+) {
+
+
+ fun loadRecommends(page: Int): Single> {
+ val version = BuildConfig.VERSION_NAME
+ val channel = HaloApp.getInstance().channel
+ return api.loadImageArticleRecommends(IMAGE_ARTICLE_SORT, page, version, channel)
+ }
+
+
+ fun isShowDragTips() =
+ Single.create {
+ val hasShow = SPUtils.getBoolean(Constants.SP_HAS_SHOW_IMAGE_AND_TEXT_DRAG_TIPS)
+ it.onSuccess(!hasShow)
+ }
+
+ fun saveHasShowDragTips() {
+ SPUtils.setBoolean(Constants.SP_HAS_SHOW_IMAGE_AND_TEXT_DRAG_TIPS, true)
+ }
+
+ fun checkHasSections(bbsId: String) =
+ newApi.getForumSections(bbsId)
+ .map {
+ it.size > 0
+ }
+
+ fun getModeratorsInfo(bbsId: String) =
+ api.getModeratorsInfo(bbsId)
+ .map {
+ it["is_moderators"].asBoolean
+ }
+
+ fun createOrEditDraft(
+ request: PublishImageTextRequest,
+ localImages: List
+ ): Single {
+ // 需要先上传本地图片
+ val userId = UserManager.getInstance().userId
+ return updatePic(localImages)
+ .observeOn(Schedulers.io())
+ .flatMap {
+ request.images = it
+ if (request.draftId.isNotBlank()) {
+ // 编辑草稿
+ api.editImageArticleDrafts(userId, request.draftId, request)
+ .toSingleDefault(ResponseBody.create(MediaType.parse("application/json"), "{}"))
+ } else {
+ api.createImageArticleDrafts(userId, request)
+ }
+
+ }
+ }
+
+ fun publishImageText(
+ request: PublishImageTextRequest,
+ localImages: List
+ ): Single {
+ return updatePic(localImages)
+ .observeOn(Schedulers.io())
+ .flatMap {
+ request.images = it
+ api.publishImageText(request)
+ .flatMap {
+ val view = "detail"
+ val versionName = BuildConfig.VERSION_NAME
+ val channel = HaloApp.getInstance().channel
+ api.loadImageArticleDetail(it["_id"].asString, view, versionName, channel)
+ }
+ }
+
+ }
+
+ fun editImageArticle(
+ id: String,
+ request: PublishImageTextRequest,
+ images: List
+ ): Single {
+ return updatePic(images)
+ .observeOn(Schedulers.io())
+ .flatMap {
+ request.images = it
+ api.editImageArticle(id, request)
+ .toSingleDefault(id)
+ .flatMap {
+ val view = "detail"
+ val versionName = BuildConfig.VERSION_NAME
+ val channel = HaloApp.getInstance().channel
+ api.loadImageArticleDetail(it, view, versionName, channel)
+ }
+ }
+ }
+
+ fun loadDraft(): Single> {
+ val userId = UserManager.getInstance().userId
+ val versionName = BuildConfig.VERSION_NAME
+ val channel = HaloApp.getInstance().channel
+ return api.loadImageTextDrafts(userId, versionName, channel)
+ }
+
+ fun deleteDraft(draftId: String): Completable {
+ val userId = UserManager.getInstance().userId
+ return api.deleteImageArticleDraft(userId, draftId)
+ }
+
+ fun loadImageArticleDetail(imageArticleId: String): Single {
+ val view = "detail"
+ return api.loadImageArticleDetail(imageArticleId, view, BuildConfig.VERSION_NAME, HaloApp.getInstance().channel)
+
+ }
+
+ fun loadMyImageArticleList(page: Int): Observable> {
+ val userId = UserManager.getInstance().userId
+ val version = BuildConfig.VERSION_NAME
+ val channel = HaloApp.getInstance().channel
+ return api.getPersonalHistory(userId, page, channel, IMAGE_ARTICLE_FILTER)
+ }
+
+ private fun updatePic(localImages: List): Single> =
+ Single.create {
+ // 已上传的图片
+ val urls = localImages.mapNotNull { image -> image.url }
+ val uris = localImages.mapNotNull { image -> image.uri }
+ if (uris.isEmpty()) {
+ it.onSuccess(urls)
+ return@create
+ }
+ // 需要上传的图片
+ val localPaths = uris.map { uri -> PathUtils.getPath(HaloApp.getInstance(), uri) }
+ UploadImageUtils.compressAndUploadImageList(
+ UploadImageUtils.UploadType.community_article,
+ localPaths,
+ false,
+ object : UploadImageUtils.OnUploadImageListListener {
+ override fun onSuccess(
+ imageUrlMap: LinkedHashMap,
+ errorMap: Map
+ ) {
+ val finalUrls = urls + imageUrlMap.values
+ it.onSuccess(finalUrls)
+ }
+
+ override fun onSingleSuccess(imageUrlMap: Map) = Unit
+
+ override fun onError(errorMap: Map) {
+ // 图片上传失败,忽略
+
+ }
+
+ override fun onProgress(total: Long, progress: Long) = Unit
+
+ })
+ }
+
+ fun getSingleCommentById(commentId: String): Single {
+ return api.getImageArticleCommentDetail(
+ commentId,
+ BuildConfig.VERSION_NAME,
+ HaloApp.getInstance().channel,
+ System.currentTimeMillis()
+ )
+ }
+
+ fun followBbs(communityId: String, isFollow: Boolean) =
+ if (isFollow) {
+ api.followForum(communityId)
+ } else {
+ api.unFollowForum(communityId)
+ }
+
+ fun collect(imageArticleId: String, isCollected: Boolean) =
+ if (isCollected) {
+ api.collectImageArticle(imageArticleId)
+ .flatMapCompletable {
+ Completable.complete()
+ }
+ } else {
+ api.cancelImageArticleCollection(imageArticleId)
+ }
+
+ fun voteImageArticle(imageArticleId: String, vote: Boolean): Single = if (vote) {
+ api.voteArticleImage(imageArticleId)
+ } else {
+ api.unVoteArticleImage(imageArticleId)
+ }
+
+ fun applyHighlightForImageArticle(imageArticleId: String): Completable =
+ api.applyHighlightForImageArticle(imageArticleId)
+
+ fun addHighlight(isAdd: Boolean, imageArticleId: String) =
+ if (isAdd) {
+ api.addHighlightForImageArticle(imageArticleId)
+ } else {
+ api.cancelHighlightForImageArticle(imageArticleId)
+ }
+
+ fun deleteOrHideImageArticle(imageArticleId: String) =
+ api.deleteOrHideImageArticle(imageArticleId)
+
+ fun followUser(follow: Boolean, userId: String) =
+ if (follow) {
+ api.postFollowing(userId)
+ } else {
+ api.deleteFollowing(userId)
+ }
+
+ companion object {
+
+ private const val IMAGE_ARTICLE_FILTER = "scene:question_answer,type:image_article"
+ private const val IMAGE_ARTICLE_SORT = "time.comment:-1"
+
+ fun newInstance() =
+ ImageArticleRepository(RetrofitManager.getInstance().api, RetrofitManager.getInstance().newApi)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleRequestPermissionFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleRequestPermissionFragment.kt
new file mode 100644
index 0000000000..dbb2749e3e
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleRequestPermissionFragment.kt
@@ -0,0 +1,11 @@
+package com.gh.gamecenter.forum.home.recommend
+
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.base.fragment.ToolbarFragment
+
+class ImageArticleRequestPermissionFragment : ToolbarFragment() {
+
+ override fun getLayoutId(): Int {
+ return R.layout.fragment_image_article_request_permission
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleUseCase.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleUseCase.kt
new file mode 100644
index 0000000000..1e90c2e0ed
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/ImageArticleUseCase.kt
@@ -0,0 +1,56 @@
+package com.gh.gamecenter.forum.home.recommend
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.retrofit.BiResponse
+import com.gh.gamecenter.common.utils.singleToMain
+import com.gh.gamecenter.core.utils.ToastUtils
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.entity.VoteEntity
+import com.gh.gamecenter.eventbus.EBImageArticleChanged
+import com.gh.gamecenter.livedata.Event
+import io.reactivex.disposables.CompositeDisposable
+import org.greenrobot.eventbus.EventBus
+
+class ImageArticleUseCase(
+ private val repository: ImageArticleRepository,
+ private val compositeDisposable: CompositeDisposable
+) {
+
+ fun voteImageArticle(imageArticleId: String, isVote: Boolean) {
+ repository.voteImageArticle(imageArticleId, isVote)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: VoteEntity) {
+ notifyVoteChanged(imageArticleId, isVote, data.vote)
+ }
+
+ }).let(compositeDisposable::add)
+ }
+
+ companion object {
+
+
+ fun notifyVoteChanged(id: String, isVoted: Boolean, count: Int) {
+ EventBus.getDefault().post(EBImageArticleChanged.VoteChanged(id, isVoted, count))
+ }
+
+ fun notifyCommentChanged(id: String, count: Int) {
+ EventBus.getDefault().post(EBImageArticleChanged.CommentChanged(id, count))
+ }
+
+ fun notifyDataDeleted(id: String) {
+ EventBus.getDefault().post(EBImageArticleChanged.DataDeleted(id))
+ }
+
+ fun notifyDataCreated(data: ImageArticleEntity, draftId: String = "") {
+ EventBus.getDefault().post(EBImageArticleChanged.DataCreated(data, draftId))
+ }
+
+ fun notifyDataEdited(data: ImageArticleEntity) {
+ EventBus.getDefault().post(EBImageArticleChanged.DataChanged(data))
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/PublishImageArticleActivity.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/PublishImageArticleActivity.kt
new file mode 100644
index 0000000000..ce15862fe9
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/PublishImageArticleActivity.kt
@@ -0,0 +1,178 @@
+package com.gh.gamecenter.forum.home.recommend
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.os.Bundle
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.activity.result.ActivityResultCallback
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.activity.viewModels
+import androidx.fragment.app.Fragment
+import com.alibaba.android.arouter.facade.annotation.Autowired
+import com.alibaba.android.arouter.facade.annotation.Route
+import com.alibaba.android.arouter.launcher.ARouter
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.base.activity.BaseActivity
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.common.entity.CommunityEntity
+import com.gh.gamecenter.common.utils.PermissionHelper
+import com.gh.gamecenter.core.utils.DisplayUtils
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.forum.home.recommend.fragment.ImageArticleDraftFragment
+import com.gh.gamecenter.forum.home.recommend.fragment.PublishImageArticleFragment
+import com.gh.gamecenter.forum.home.recommend.viewmodel.PublishImageArticleActivityViewModel
+import com.gh.gamecenter.livedata.EventObserver
+import com.gh.gamecenter.qa.editor.LocalMediaActivity
+import com.zhihu.matisse.Matisse
+
+@Route(path = RouteConsts.activity.publishImageArticleActivity)
+class PublishImageArticleActivity : BaseActivity() {
+
+ private val viewModel by viewModels()
+
+ private lateinit var chooseImageLauncher: ActivityResultLauncher
+
+ private var isFirstEnter = true
+
+ @JvmField
+ @Autowired(name = EntranceConsts.KEY_IMAGE_ARTICLE_ENTITY)
+ var imageArticleEntity: ImageArticleEntity? = null
+
+ @JvmField
+ @Autowired(name = EntranceConsts.KEY_COMMUNITY_DATA)
+ var communityEntity: CommunityEntity? = null
+
+ private lateinit var tvTitle: TextView
+
+ override fun getLayoutId(): Int {
+ return R.layout.activity_publish_image_article
+ }
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ ARouter.getInstance().inject(this)
+
+ DisplayUtils.setLightStatusBar(this, true)
+ setStatusBarColor(Color.TRANSPARENT)
+
+ tvTitle = findViewById(R.id.tv_title)
+ val ivBack = findViewById(R.id.iv_back)
+ ivBack.setOnClickListener {
+ onBackPressed()
+ }
+
+ supportFragmentManager.addOnBackStackChangedListener {
+ val fragment = supportFragmentManager.fragments.lastOrNull()
+ val titleResId = if (fragment is ImageArticleDraftFragment) {
+ R.string.image_article_draft
+ } else {
+ R.string.post_image_and_text
+ }
+ tvTitle.setText(titleResId)
+ }
+
+ chooseImageLauncher = registerForActivityResult(object : ActivityResultContract() {
+ override fun createIntent(context: Context, input: Int): Intent {
+ return LocalMediaActivity.getIntent(
+ context,
+ LocalMediaActivity.ChooseType.IMAGE,
+ input,
+ "图文动态"
+ )
+ }
+
+ override fun parseResult(resultCode: Int, intent: Intent?): Intent? {
+ return intent
+ }
+ }, ActivityResultCallback {
+ if (it != null) {
+ val uris = Matisse.obtainResult(it)
+ if (uris.isEmpty()) {
+ if (isFirstEnter) {
+ finish()
+ }
+
+ } else {
+ viewModel.addImages(uris)
+ // 打开发布页
+ if (isFirstEnter) {
+ replaceFragment(true) {
+ PublishImageArticleFragment.newInstance()
+ }
+ }
+ }
+ } else {
+ if (isFirstEnter) {
+ finish()
+ }
+ }
+
+ })
+
+ if (imageArticleEntity == null) {
+ // 新建图文,优先进入图库选择页
+ replaceFragment(false, ::ImageArticleRequestPermissionFragment)
+ PermissionHelper.checkStoragePermissionBeforeActionForResult(this) { grant ->
+ if (grant) {
+ chooseImage(9)
+ } else {
+ finish()
+ }
+ }
+ } else {
+ // 编辑图文草稿,不用弹出授权页,直接进入发布页
+ replaceFragment(true) {
+ PublishImageArticleFragment.newInstance()
+ }
+ }
+
+
+
+ with(viewModel) {
+ val lifecycleOwner = this@PublishImageArticleActivity
+ chooseImageAction.observe(lifecycleOwner, EventObserver {
+ isFirstEnter = false
+ PermissionHelper.checkStoragePermissionBeforeActionForResult(this@PublishImageArticleActivity) { grant ->
+ if (grant) {
+ this@PublishImageArticleActivity.chooseImage(it)
+ }
+ }
+ })
+
+ draftBoxDestination.observe(lifecycleOwner, EventObserver {
+ replaceFragment(true) {
+ ImageArticleDraftFragment.newInstance(true)
+ }
+ })
+
+ setEditImageArticle(imageArticleEntity)
+ communityEntity?.let(::setCommunity)
+ }
+ }
+
+
+ private fun chooseImage(limit: Int) {
+ try {
+ chooseImageLauncher.launch(limit)
+ } catch (e: Exception) {
+ toast(R.string.media_image_hint)
+ e.printStackTrace()
+ }
+ }
+
+ private inline fun replaceFragment(addBackStack: Boolean = true, creator: () -> T) {
+ val tag = T::class.java.toString()
+ val fragment = supportFragmentManager.findFragmentByTag(tag) ?: creator()
+ val transaction = supportFragmentManager.beginTransaction()
+ transaction.replace(R.id.layout_activity_content, fragment, tag)
+ if (addBackStack) {
+ transaction.addToBackStack(tag)
+ }
+ transaction.commitAllowingStateLoss()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageAndTextAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageAndTextAdapter.kt
new file mode 100644
index 0000000000..d3018888a6
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageAndTextAdapter.kt
@@ -0,0 +1,107 @@
+package com.gh.gamecenter.forum.home.recommend.adapter
+
+import android.net.Uri
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil.ItemCallback
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.utils.display
+import com.gh.gamecenter.common.utils.toBinding
+import com.gh.gamecenter.common.utils.visibleIf
+import com.gh.gamecenter.core.utils.ToastUtils
+import com.gh.gamecenter.databinding.RecyclerImageAndTextBinding
+import com.gh.gamecenter.forum.home.recommend.viewmodel.PublishImageArticleViewModel
+
+open class ImageAndTextAdapter(
+ private val viewModel: PublishImageArticleViewModel,
+ private val startDrag: (ViewHolder) -> Unit
+) :
+ ListAdapter(
+ createItemDiffCallback()
+ ) {
+
+ override fun submitList(list: List?) {
+ val size = list?.size ?: 0
+ if (size < 9 && list != null) {
+ super.submitList(list + LocalImage(INVALID_ID, false, showClose = false))
+ } else {
+ super.submitList(list)
+ }
+ }
+
+ public override fun getItem(position: Int): LocalImage {
+ return super.getItem(position)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
+ return ImageViewHolder(parent.toBinding())
+ }
+
+ override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
+ val item = getItem(position)
+ with(holder.binding) {
+ tvCover.visibleIf(item.isCover)
+ ivClose.visibleIf(item.showClose) {
+ ivClose.setOnClickListener {
+ if (itemCount <= 2) {
+ ToastUtils.showToast(root.context.getString(R.string.post_image_limit))
+ return@setOnClickListener
+ }
+ viewModel.removeImage(holder.bindingAdapterPosition)
+ }
+ }
+ if (item.id != INVALID_ID) {
+ if (item.uri != null) {
+ ivIcon.setImageURI(item.uri)
+ } else {
+ ivIcon.display(item.url)
+ }
+
+ ivIcon.setOnClickListener(null)
+ ivIcon.setOnLongClickListener {
+ startDrag(holder)
+ return@setOnLongClickListener true
+ }
+ } else {
+ ivIcon.setImageResource(R.drawable.ic_image_and_text_add)
+ ivIcon.setOnClickListener {
+ viewModel.chooseImage()
+ }
+ root.setOnLongClickListener(null)
+ }
+
+ }
+
+ }
+
+ companion object {
+
+ const val INVALID_ID = -1
+
+ private fun createItemDiffCallback() = object : ItemCallback() {
+
+ override fun areItemsTheSame(oldItem: LocalImage, newItem: LocalImage): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(oldItem: LocalImage, newItem: LocalImage): Boolean {
+ return oldItem == newItem
+ }
+
+ }
+ }
+
+ data class LocalImage(
+ val id: Int,
+ var isCover: Boolean,
+ var showClose: Boolean,
+ val uri: Uri? = null, // 本地图片
+ val url: String? = null // 线上图片
+ )
+
+ data class ImageViewHolder(
+ val binding: RecyclerImageAndTextBinding
+ ) : RecyclerView.ViewHolder(binding.root)
+}
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageArticleDetailBannerAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageArticleDetailBannerAdapter.kt
new file mode 100644
index 0000000000..719e4aaae9
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageArticleDetailBannerAdapter.kt
@@ -0,0 +1,42 @@
+package com.gh.gamecenter.forum.home.recommend.adapter
+
+import android.view.ViewGroup
+import androidx.core.view.updateLayoutParams
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import com.gh.gamecenter.common.utils.ImageUtils
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.toBinding
+import com.gh.gamecenter.databinding.RecyclerImageArticleDetailBannerBinding
+
+class ImageArticleDetailBannerAdapter : ListAdapter(
+ createDiffCallback()
+) {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BannerViewHolder {
+ return BannerViewHolder(parent.toBinding())
+ }
+
+ override fun onBindViewHolder(holder: BannerViewHolder, position: Int) {
+ with(holder.binding) {
+ ImageUtils.display(ivBanner, getItem(position))
+ }
+ }
+
+ companion object {
+
+ fun createDiffCallback() = object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
+ return oldItem == newItem
+ }
+
+ override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
+ return oldItem == newItem
+ }
+
+ }
+ }
+
+ class BannerViewHolder(val binding: RecyclerImageArticleDetailBannerBinding) : ViewHolder(binding.root)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageArticleDetailCommentAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageArticleDetailCommentAdapter.kt
new file mode 100644
index 0000000000..dcd88cacfc
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageArticleDetailCommentAdapter.kt
@@ -0,0 +1,15 @@
+package com.gh.gamecenter.forum.home.recommend.adapter
+
+import android.content.Context
+import com.gh.gamecenter.feature.entity.CommentEntity
+import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter
+import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter.AdapterType
+import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel
+
+class ImageArticleDetailCommentAdapter(
+ context: Context,
+ private val viewModel: BaseCommentViewModel,
+ type: AdapterType,
+ entrance: String
+) : BaseCommentAdapter(context, viewModel, type, entrance) {
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageArticleDraftAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageArticleDraftAdapter.kt
new file mode 100644
index 0000000000..a045f5dbad
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/ImageArticleDraftAdapter.kt
@@ -0,0 +1,79 @@
+package com.gh.gamecenter.forum.home.recommend.adapter
+
+import android.content.Context
+import android.view.ViewGroup
+import androidx.core.view.updateLayoutParams
+import androidx.recyclerview.widget.DiffUtil.ItemCallback
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import com.gh.common.util.NewsUtils
+import com.gh.gamecenter.common.utils.ImageUtils
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.goneIf
+import com.gh.gamecenter.common.utils.toBinding
+import com.gh.gamecenter.databinding.RecyclerImageArticleDraftBinding
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.forum.home.recommend.viewmodel.ImageArticleDraftViewModel
+import com.lzf.easyfloat.utils.DisplayUtils
+
+class ImageArticleDraftAdapter(
+ private val context: Context,
+ private val viewModel: ImageArticleDraftViewModel
+) :
+ ListAdapter(createDiffCallback()) {
+
+ private val itemWidth by lazy {
+ (DisplayUtils.getScreenWidth(context) - 20F.dip2px()) / 2F
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DraftViewHolder {
+ return DraftViewHolder(parent.toBinding())
+ }
+
+ override fun onBindViewHolder(holder: DraftViewHolder, position: Int) {
+ val item = getItem(position)
+ with(holder.binding) {
+ val title = item.title.ifBlank { item.content }
+ tvTitle.goneIf(title.isBlank()) {
+ tvTitle.text = title
+ }
+
+ val imageInfo = item.imagesInfos.firstOrNull()
+ val imageRadio = ImageArticleEntity.getImageRadio(imageInfo, DRAFT_RADIO_MIN, DRAFT_RADIO_MAX)
+ ivCover.updateLayoutParams {
+ height = (itemWidth * imageRadio).toInt()
+ }
+ ImageUtils.display(ivCover, item.images.firstOrNull() ?: "")
+ tvTime.text = NewsUtils.getFormattedTime(item.time.update)
+ vDelete.setOnClickListener {
+ viewModel.showDeleteDraftDialog(item.id)
+ }
+
+ root.setOnClickListener {
+ viewModel.editDraft(item)
+ }
+ }
+ }
+
+ companion object {
+
+ const val DRAFT_RADIO_MIN = 0.75F
+ const val DRAFT_RADIO_MAX = 1.33F
+
+ private fun createDiffCallback() = object : ItemCallback() {
+ override fun areItemsTheSame(oldItem: ImageArticleEntity, newItem: ImageArticleEntity): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(
+ oldItem: ImageArticleEntity,
+ newItem: ImageArticleEntity
+ ): Boolean {
+ return oldItem == newItem
+ }
+
+ }
+ }
+
+ data class DraftViewHolder(val binding: RecyclerImageArticleDraftBinding) : ViewHolder(binding.root)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/MyImageArticleAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/MyImageArticleAdapter.kt
new file mode 100644
index 0000000000..53336b905d
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/MyImageArticleAdapter.kt
@@ -0,0 +1,187 @@
+package com.gh.gamecenter.forum.home.recommend.adapter
+
+import android.content.Context
+import android.view.ViewGroup
+import androidx.core.view.updateLayoutParams
+import androidx.recyclerview.widget.DiffUtil.ItemCallback
+import androidx.recyclerview.widget.RecyclerView
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.display
+import com.gh.gamecenter.common.utils.goneIf
+import com.gh.gamecenter.common.utils.toBinding
+import com.gh.gamecenter.databinding.RecyclerRecommendHomeBinding
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.entity.PersonalHistoryEntity
+import com.gh.gamecenter.forum.home.recommend.ImageArticleFooterWrapperAdapter
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageArticleDraftAdapter.Companion.DRAFT_RADIO_MAX
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageArticleDraftAdapter.Companion.DRAFT_RADIO_MIN
+import com.gh.gamecenter.forum.home.recommend.adapter.RecommendHomeAdapter.Companion.PAYLOADS_CHANGED_COVER
+import com.gh.gamecenter.forum.home.recommend.adapter.RecommendHomeAdapter.Companion.PAYLOADS_CHANGED_TITLE
+import com.gh.gamecenter.forum.home.recommend.adapter.RecommendHomeAdapter.Companion.PAYLOADS_CHANGED_VOTE
+import com.gh.gamecenter.forum.home.recommend.viewmodel.MyImageArticleListViewModel
+import com.lzf.easyfloat.utils.DisplayUtils
+
+class MyImageArticleAdapter(
+ private val context: Context,
+ private val viewModel: MyImageArticleListViewModel
+) :
+ ImageArticleFooterWrapperAdapter(
+ createDiffCallback()
+ ) {
+
+ override fun onCreateDataViewHolder(parent: ViewGroup): MyImageArticleViewHolder {
+ return MyImageArticleViewHolder(parent.toBinding(), object : MyImageArticleViewHolder.OnMyArticleImageListener {
+ override fun navigateToImageArticleDetailPage(id: String) {
+ viewModel.navigateToImageArticleDetailPage(id)
+ }
+
+ override fun voteImageArticle(id: String, vote: Boolean) {
+ viewModel.useCase.voteImageArticle(id, vote)
+ }
+
+ })
+ }
+
+ override fun onBindDataViewHolder(holder: MyImageArticleViewHolder, position: Int) {
+ val item = getItem(position)
+ holder.bind(item)
+
+ }
+
+ override fun onBindDataViewHolder(holder: MyImageArticleViewHolder, position: Int, payloads: MutableList) {
+ val item = getItem(position)
+ holder.bind(item, payloads)
+ }
+
+ override fun loadMore() {
+ viewModel.loadMore()
+ }
+
+ companion object {
+
+ fun createDiffCallback() = object : ItemCallback() {
+ override fun areItemsTheSame(oldItem: PersonalHistoryEntity, newItem: PersonalHistoryEntity): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(oldItem: PersonalHistoryEntity, newItem: PersonalHistoryEntity): Boolean {
+ return oldItem.title == newItem.title
+ && oldItem.brief == newItem.brief
+ && oldItem.user.id == newItem.user.id
+ && oldItem.user.icon == newItem.user.icon
+ && oldItem.user.name == newItem.user.name
+ && oldItem.count.vote == newItem.count.vote
+ && oldItem.status == newItem.status
+ }
+
+ override fun getChangePayload(oldItem: PersonalHistoryEntity, newItem: PersonalHistoryEntity): Any {
+ val payloads = mutableListOf()
+ if (oldItem.images.firstOrNull() != newItem.images.firstOrNull()
+ || oldItem.imagesInfo.firstOrNull() != newItem.imagesInfo.firstOrNull()
+ ) {
+ payloads.add(PAYLOADS_CHANGED_COVER)
+ }
+
+ val oldTitle = oldItem.title.ifBlank { oldItem.content }
+ val newTitle = newItem.title.ifBlank { newItem.content }
+ if (oldTitle != newTitle) {
+ payloads.add(PAYLOADS_CHANGED_TITLE)
+ }
+
+ if (oldItem.me.isCommunityArticleVote != newItem.me.isCommunityArticleVote
+ || oldItem.count.vote != newItem.count.vote
+ ) {
+ payloads.add(PAYLOADS_CHANGED_VOTE)
+ }
+ return payloads
+ }
+
+ }
+ }
+
+ class MyImageArticleViewHolder(
+ val binding: RecyclerRecommendHomeBinding,
+ private val listener: OnMyArticleImageListener
+ ) : RecyclerView.ViewHolder(binding.root) {
+
+ private val itemWidth by lazy {
+ (DisplayUtils.getScreenWidth(itemView.context) - 20F.dip2px()) / 2F
+ }
+
+ fun bind(item: PersonalHistoryEntity, payloads: List? = null) {
+ println("kayn -->bind:$payloads")
+ fun setCover() {
+ val imageInfo = item.imagesInfo.firstOrNull()
+ val imageRadio = ImageArticleEntity.getImageRadio(imageInfo, DRAFT_RADIO_MIN, DRAFT_RADIO_MAX)
+ binding.ivCover.updateLayoutParams {
+ height = (itemWidth * imageRadio).toInt()
+ }
+ binding.ivCover.display(item.images.firstOrNull())
+ }
+
+ fun setTitle() {
+ val title = item.title.ifBlank { item.content }
+ binding.tvTitle.goneIf(title.isBlank()) {
+ binding.tvTitle.text = title
+ }
+ }
+
+ fun setVote() {
+ binding.voteState.setVote(item.me.isCommunityArticleVote, item.count.vote, item.status)
+ }
+
+ with(binding) {
+ if (payloads.isNullOrEmpty()) {
+ ivIcon.displayGameIcon(item.user.icon, null)
+ tvAuthorName.text = item.user.name
+
+ setTitle()
+
+ setCover()
+
+ setVote()
+
+ } else {
+ payloads.filterIsInstance>()
+ .flatten()
+ .forEach { change ->
+ when (change) {
+ PAYLOADS_CHANGED_COVER -> {
+ setCover()
+ }
+
+ PAYLOADS_CHANGED_TITLE -> {
+ setTitle()
+ }
+
+ PAYLOADS_CHANGED_VOTE -> {
+ binding.voteState.setVote(
+ item.me.isCommunityArticleVote,
+ item.count.vote,
+ item.status
+ )
+ }
+ }
+ }
+ }
+
+ root.setOnClickListener {
+ listener.navigateToImageArticleDetailPage(item.id)
+ }
+
+ setVote()
+ voteState.setOnClickListener {
+ val vote = !item.me.isCommunityArticleVote
+ listener.voteImageArticle(item.id, vote)
+ }
+ }
+ }
+
+ interface OnMyArticleImageListener {
+
+ fun navigateToImageArticleDetailPage(id: String)
+
+ fun voteImageArticle(id: String, vote: Boolean)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/RecommendHomeAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/RecommendHomeAdapter.kt
new file mode 100644
index 0000000000..c56b950200
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/adapter/RecommendHomeAdapter.kt
@@ -0,0 +1,196 @@
+package com.gh.gamecenter.forum.home.recommend.adapter
+
+import android.content.Context
+import android.view.ViewGroup
+import androidx.core.view.updateLayoutParams
+import androidx.recyclerview.widget.DiffUtil.ItemCallback
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.display
+import com.gh.gamecenter.common.utils.goneIf
+import com.gh.gamecenter.common.utils.toBinding
+import com.gh.gamecenter.databinding.RecyclerRecommendHomeBinding
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.forum.home.recommend.ImageArticleFooterWrapperAdapter
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageArticleDraftAdapter.Companion.DRAFT_RADIO_MAX
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageArticleDraftAdapter.Companion.DRAFT_RADIO_MIN
+import com.gh.gamecenter.forum.home.recommend.viewmodel.ImageArticleHomeViewModel
+import com.lzf.easyfloat.utils.DisplayUtils
+
+class RecommendHomeAdapter(
+ private val context: Context,
+ private val viewModel: ImageArticleHomeViewModel
+) :
+ ImageArticleFooterWrapperAdapter(
+ createDiffCallback()
+ ) {
+
+ override fun onCreateDataViewHolder(parent: ViewGroup): RecommendHomeViewHolder {
+ return RecommendHomeViewHolder(parent.toBinding(), object : OnImageArticleListener {
+ override fun navigateToImageArticleDetailPage(id: String) {
+ viewModel.navigateToImageArticleDetailPage(id)
+ }
+
+ override fun voteImageArticle(id: String, vote: Boolean) {
+ viewModel.useCase.voteImageArticle(id, vote)
+ }
+
+ })
+ }
+
+
+ override fun onBindDataViewHolder(holder: RecommendHomeViewHolder, position: Int) {
+ val item = getItem(position)
+ holder.bind(item, position)
+ }
+
+ override fun onBindDataViewHolder(holder: RecommendHomeViewHolder, position: Int, payloads: MutableList) {
+ holder.bind(getItem(position), position, payloads)
+ }
+
+ override fun loadMore() {
+ viewModel.loadMore()
+ }
+
+ fun notifyItemChanged(result: ImageArticleEntity) {
+ val position = currentList.indexOfFirst { it.id == result.id }
+ if (position != -1) {
+ val newData = currentList.toMutableList()
+ newData[position] = result
+ submitList(newData)
+ }
+ }
+
+ companion object {
+
+ const val PAYLOADS_CHANGED_COVER = "payloads_changed_cover"
+ const val PAYLOADS_CHANGED_TITLE = "payloads_changed_title"
+ const val PAYLOADS_CHANGED_VOTE = "payloads_changed_vote"
+
+
+ fun createDiffCallback() = object : ItemCallback() {
+ override fun areItemsTheSame(oldItem: ImageArticleEntity, newItem: ImageArticleEntity): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(oldItem: ImageArticleEntity, newItem: ImageArticleEntity): Boolean {
+ return oldItem.title == newItem.title
+ && oldItem.content == newItem.content
+ && oldItem.user.id == newItem.user.id
+ && oldItem.user.icon == newItem.user.icon
+ && oldItem.user.name == newItem.user.name
+ && oldItem.count.vote == newItem.count.vote
+
+ }
+
+ override fun getChangePayload(oldItem: ImageArticleEntity, newItem: ImageArticleEntity): Any? {
+ val payloads = mutableListOf()
+ if (oldItem.images.firstOrNull() != newItem.images.firstOrNull()
+ || oldItem.imagesInfos.firstOrNull() != newItem.imagesInfos.firstOrNull()
+ ) {
+ payloads.add(PAYLOADS_CHANGED_COVER)
+ }
+
+ val oldTitle = oldItem.title.ifBlank { oldItem.content }
+ val newTitle = newItem.title.ifBlank { newItem.content }
+ if (oldTitle != newTitle) {
+ payloads.add(PAYLOADS_CHANGED_TITLE)
+ }
+
+ if (oldItem.me.isCommunityArticleVote != newItem.me.isCommunityArticleVote
+ || oldItem.count.vote != newItem.count.vote
+ ) {
+ payloads.add(PAYLOADS_CHANGED_VOTE)
+ }
+ return payloads
+ }
+
+ }
+ }
+
+ class RecommendHomeViewHolder(
+ val binding: RecyclerRecommendHomeBinding,
+ private val listener: OnImageArticleListener
+ ) : ViewHolder(binding.root) {
+
+ private val itemWidth by lazy {
+ (DisplayUtils.getScreenWidth(itemView.context) - 20F.dip2px()) / 2F
+ }
+
+ fun bind(item: ImageArticleEntity, position: Int, payloads: MutableList = mutableListOf()) {
+ fun setCover() {
+ val imageInfo = item.imagesInfos.firstOrNull()
+ val imageRadio = ImageArticleEntity.getImageRadio(imageInfo, DRAFT_RADIO_MIN, DRAFT_RADIO_MAX)
+ binding.ivCover.updateLayoutParams {
+ height = (itemWidth * imageRadio).toInt()
+ }
+ binding.ivCover.display(item.images.firstOrNull())
+ }
+
+ fun setTitle() {
+ val title = item.title.ifBlank { item.content }
+ binding.tvTitle.goneIf(title.isBlank()){
+ binding.tvTitle.text = title
+ }
+ }
+
+ fun setVote() {
+ binding.voteState.setVote(item.me.isCommunityArticleVote, item.count.vote, item.status)
+ }
+
+ with(binding) {
+ if (payloads.isEmpty()) {
+ ivIcon.displayGameIcon(item.user.icon, null)
+ tvAuthorName.text = item.user.name
+
+ setTitle()
+
+ setCover()
+
+ setVote()
+
+ } else {
+ payloads.filterIsInstance>()
+ .flatten()
+ .forEach { change ->
+ when (change) {
+ PAYLOADS_CHANGED_COVER -> {
+ setCover()
+ }
+
+ PAYLOADS_CHANGED_TITLE -> {
+ setTitle()
+ }
+
+ PAYLOADS_CHANGED_VOTE -> {
+ setVote()
+ }
+ }
+ }
+ }
+
+ root.setOnClickListener {
+ listener.navigateToImageArticleDetailPage(item.id)
+
+ }
+ voteState.setOnClickListener {
+ val vote = !item.me.isCommunityArticleVote
+ listener.voteImageArticle(item.id, vote)
+ }
+
+ }
+ }
+ }
+
+ interface OnImageArticleListener {
+
+
+
+
+
+
+ fun navigateToImageArticleDetailPage(id: String)
+
+ fun voteImageArticle(id: String, vote: Boolean)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleCommentFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleCommentFragment.kt
new file mode 100644
index 0000000000..247e234dd4
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleCommentFragment.kt
@@ -0,0 +1,136 @@
+package com.gh.gamecenter.forum.home.recommend.fragment
+
+import android.os.Bundle
+import android.view.View
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.RecyclerView
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.baselist.ListAdapter
+import com.gh.gamecenter.common.baselist.ListFragment
+import com.gh.gamecenter.common.constant.Constants
+import com.gh.gamecenter.common.eventbus.EBReuse
+import com.gh.gamecenter.common.utils.goneIf
+import com.gh.gamecenter.common.utils.viewModelProvider
+import com.gh.gamecenter.common.view.CustomDividerItemDecoration
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageArticleDetailCommentAdapter
+import com.gh.gamecenter.forum.home.recommend.viewmodel.ImageArticleCommentViewModel
+import com.gh.gamecenter.forum.home.recommend.viewmodel.ImageArticleDetailViewModel
+import com.gh.gamecenter.livedata.EventObserver
+import com.gh.gamecenter.qa.article.detail.CommentItemData
+import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter
+import com.halo.assistant.HaloApp
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class ImageArticleCommentFragment : ListFragment() {
+
+ private val parentViewModel by viewModels(ownerProducer = { parentFragment ?: this })
+
+ private val adapter by lazy {
+ ImageArticleDetailCommentAdapter(
+ requireContext(),
+ mListViewModel,
+ BaseCommentAdapter.AdapterType.COMMENT,
+ mEntrance
+ )
+ }
+
+ override fun provideListAdapter(): ListAdapter<*> = adapter
+
+ override fun provideListViewModel(): ImageArticleCommentViewModel {
+ return viewModelProvider(
+ ImageArticleCommentViewModel.Factory(
+ HaloApp.getInstance().application,
+ arguments?.getString(KEY_IMAGE_ARTICLE_ID) ?: ""
+ )
+ )
+ }
+
+ override fun getItemDecoration(): RecyclerView.ItemDecoration {
+
+ val drawable = ContextCompat.getDrawable(requireContext(), R.drawable.divider_article_detail_comment)
+ val itemDecoration = CustomDividerItemDecoration(
+ requireContext(),
+ notDecorateTheFirstItem = true,
+ notDecorateTheLastItem = true
+ )
+
+ itemDecoration.setDrawable(drawable!!)
+ mItemDecoration = itemDecoration
+ return itemDecoration
+ }
+
+ override fun addSyncPageObserver() = true
+
+ override fun provideSyncAdapter() = adapter
+
+ override fun getLayoutId(): Int {
+ return R.layout.fragment_list_article_detail_comment
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ with(parentViewModel) {
+ sortType.observe(this@ImageArticleCommentFragment, EventObserver {
+ mListViewModel.changeSort(it)
+ })
+
+ insertNewestCommentAction.observe(this@ImageArticleCommentFragment, EventObserver {
+ mListViewModel.insertNewestComment(it)
+ })
+
+ commentCount.observe(this@ImageArticleCommentFragment) {
+ mListViewModel.commentCount = it
+ }
+ }
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ mListRefresh?.isEnabled = false
+
+ with(mListViewModel) {
+ updateCommentCountAction.observe(viewLifecycleOwner, EventObserver {
+ parentViewModel.updateComment("", commentCount)
+ })
+ }
+ }
+
+ override fun onLoadError() {
+ super.onLoadError()
+ mListRv.goneIf(false)
+ }
+
+ override fun onLoadNotFound() {
+ super.onLoadNotFound()
+ mListRv.goneIf(false)
+ }
+
+ override fun onLoadEmpty() {
+ super.onLoadEmpty()
+ mListRv.goneIf(false)
+ }
+
+
+ override fun onDarkModeChanged() {
+ super.onDarkModeChanged()
+ if (mListRv != null) {
+ mListRv.removeItemDecoration(mItemDecoration)
+ mListRv.addItemDecoration(itemDecoration)
+ }
+ }
+
+ companion object {
+ private const val KEY_IMAGE_ARTICLE_ID = "key_image_article_id"
+
+ fun newInstance(imageArticleId: String): Fragment =
+ ImageArticleCommentFragment().apply {
+ arguments = Bundle().apply {
+ putString(KEY_IMAGE_ARTICLE_ID, imageArticleId)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleDetailFragment.kt
new file mode 100644
index 0000000000..f74901e50a
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleDetailFragment.kt
@@ -0,0 +1,790 @@
+package com.gh.gamecenter.forum.home.recommend.fragment
+
+import android.app.Activity
+import android.content.Intent
+import android.graphics.Path
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import androidx.core.view.updateLayoutParams
+import androidx.core.widget.TextViewCompat
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.Lifecycle
+import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
+import com.alibaba.android.arouter.facade.annotation.Autowired
+import com.alibaba.android.arouter.launcher.ARouter
+import com.gh.common.databind.BindingAdapters
+import com.gh.common.util.BbsReportHelper
+import com.gh.common.util.DirectUtils
+import com.gh.common.util.DownloadItemUtils
+import com.gh.common.util.NewsUtils
+import com.gh.download.DownloadManager
+import com.gh.gamecenter.GameDetailActivity
+import com.gh.gamecenter.R
+import com.gh.gamecenter.adapter.viewholder.GameViewHolder
+import com.gh.gamecenter.common.base.fragment.BaseFragment
+import com.gh.gamecenter.common.baselist.LoadStatus
+import com.gh.gamecenter.common.constant.Constants
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.common.entity.AdditionalParamsEntity
+import com.gh.gamecenter.common.entity.NormalShareEntity
+import com.gh.gamecenter.common.eventbus.EBReuse
+import com.gh.gamecenter.common.exposure.ExposureSource
+import com.gh.gamecenter.common.utils.*
+import com.gh.gamecenter.core.utils.ToastUtils
+import com.gh.gamecenter.databinding.FragmentImageArticleDetailBinding
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.entity.MenuItemEntity
+import com.gh.gamecenter.eventbus.EBDownloadStatus
+import com.gh.gamecenter.eventbus.EBImageArticleChanged
+import com.gh.gamecenter.eventbus.EBPackage
+import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.feature.entity.ImageInfo
+import com.gh.gamecenter.feature.entity.Permissions
+import com.gh.gamecenter.feature.exposure.ExposureEvent
+import com.gh.gamecenter.forum.home.recommend.PublishImageArticleActivity
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageArticleDetailBannerAdapter
+import com.gh.gamecenter.forum.home.recommend.viewmodel.ImageArticleDetailViewModel
+import com.gh.gamecenter.livedata.EventObserver
+import com.gh.gamecenter.login.user.UserManager
+import com.gh.gamecenter.qa.comment.CommentActivity
+import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel
+import com.gh.gamecenter.qa.dialog.MoreFunctionPanelDialog
+import com.gh.gamecenter.qa.entity.ArticleDetailEntity
+import com.lightgame.download.DataWatcher
+import com.lightgame.download.DownloadEntity
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class ImageArticleDetailFragment : BaseFragment() {
+
+ private val viewModel by viewModels()
+
+ @JvmField
+ @Autowired(name = EntranceConsts.KEY_IMAGE_ARTICLE_ID)
+ var imageArticleId = ""
+
+ @JvmField
+ @Autowired(name = EntranceConsts.KEY_TOP_COMMENT_ID)
+ var topCommentId: String? = null
+
+ private var showIndicator = false
+
+ private val binding: FragmentImageArticleDetailBinding by lazy {
+ FragmentImageArticleDetailBinding.inflate(layoutInflater)
+ }
+
+ private val adapter by lazy {
+ ImageArticleDetailBannerAdapter()
+ }
+
+ private val handler = object : Handler(Looper.getMainLooper()) {
+ override fun handleMessage(msg: Message) {
+ super.handleMessage(msg)
+ if (msg.what == HIDE_BANNER_NUMBER_INDICATOR) {
+ binding.llNumberIndicator.goneIf(true)
+ }
+ }
+ }
+
+ private val dataWatcher = object : DataWatcher() {
+ override fun onDataChanged(downloadEntity: DownloadEntity) {
+ setDownloadButton(viewModel.imageArticleDetailEntity.value?.community?.game)
+ }
+
+ override fun onDataInit(downloadEntity: DownloadEntity) {
+ onDataChanged(downloadEntity)
+ }
+ }
+
+ override fun getInflatedLayout() = binding.root
+
+ override fun getLayoutId() = 0
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ ARouter.getInstance().inject(this)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ val tag = ImageArticleDetailFragment::class.java.name
+ val fragment =
+ childFragmentManager.findFragmentByTag(tag) ?: ImageArticleCommentFragment.newInstance(imageArticleId)
+ childFragmentManager.beginTransaction()
+ .replace(R.id.fcv_comment_container, fragment, tag)
+ .commitNowAllowingStateLoss()
+
+ with(viewModel) {
+ pageStatus.observe(viewLifecycleOwner) {
+ binding.reuseNoConnection.root.goneIf(it != LoadStatus.INIT_FAILED)
+ binding.clContent.goneIf(it != LoadStatus.INIT_LOADED)
+ binding.layoutLoading.root.goneIf(it != LoadStatus.INIT_LOADING)
+ }
+
+ imageArticleDetailEntity.observe(viewLifecycleOwner) {
+ fillUi(it)
+ }
+
+ isUserFollowed.observe(viewLifecycleOwner) {
+ binding.ctvFollow.isChecked = it
+ binding.ctvFollow.setText(
+ if (it) {
+ R.string.concerned
+ } else {
+ R.string.follow
+ }
+ )
+ }
+
+ commentCount.observe(viewLifecycleOwner) {
+ binding.inputContainer.bottomCommentTv.text = if (it == 0) {
+ R.string.comment.toResString()
+ } else {
+ "$it"
+ }
+ binding.tvCommentCount.text = "$it"
+ }
+
+ vote.observe(viewLifecycleOwner) {
+ val (isVoted, count) = it
+ val (imageDrawableResId, textColorResId) = if (isVoted) {
+ R.drawable.ic_article_detail_liked_bottom_bar to R.color.text_theme
+ } else {
+ R.drawable.ic_article_detail_like_bottom_bar to R.color.text_secondary
+ }
+ binding.inputContainer.bottomLikeIv.setImageResource(imageDrawableResId)
+ binding.inputContainer.bottomLikeTv.setTextColor(textColorResId.toColor(requireContext()))
+ binding.inputContainer.bottomLikeTv.text = if (count == 0) {
+ R.string.agree.toResString()
+ } else {
+ "$count"
+ }
+
+ }
+
+ isBbsFollowed.observe(viewLifecycleOwner) {
+ binding.ctvFollowBbs.isChecked = it
+ binding.ctvFollowBbs.setText(
+ if (it) {
+ R.string.concerned
+ } else {
+ R.string.follow
+ }
+ )
+ }
+
+ isFavorite.observe(viewLifecycleOwner) {
+ val (drawableResId, textColorResId) = if (it) {
+ R.drawable.ic_article_detail_stared_bottom_bar to R.color.text_theme
+ } else {
+ R.drawable.ic_article_detail_star_bottom_bar to R.color.text_secondary
+ }
+ binding.inputContainer.bottomStarIv.setImageResource(drawableResId)
+ binding.inputContainer.bottomStarTv.setTextColor(textColorResId.toColor(requireContext()))
+ binding.inputContainer.bottomStarTv.setText(
+ if (it) {
+ R.string.already_saved
+ } else {
+ R.string.menu_collect
+ }
+ )
+ }
+
+ applyHighlightAction.observe(viewLifecycleOwner, EventObserver {
+ val toastResId = if (it) {
+ R.string.apply_successfully
+ } else {
+ R.string.apply_failure
+ }
+ toast(toastResId)
+ })
+
+ addHighlightAction.observe(viewLifecycleOwner, EventObserver {
+ toast(it)
+ })
+
+ deleteOrHideImageArticleSuccessfully.observe(viewLifecycleOwner, EventObserver {
+ val (isSuccess, toastResId) = it
+ toast(toastResId)
+ if (isSuccess) {
+ EventBus.getDefault().post(EBImageArticleChanged.DataDeleted(imageArticleId))
+ requireActivity().finish()
+ }
+ })
+
+ loadImageArticleDetail(this@ImageArticleDetailFragment.imageArticleId)
+ }
+ }
+
+ override fun initView(view: View?) {
+ super.initView(view)
+ binding.vpBanner.adapter = adapter
+
+ binding.vMore.setOnClickListener {
+ showMoreItemDialog()
+ }
+ binding.sfvOrder.setItemList(
+ listOf(R.string.positive_older.toResString(), R.string.reverse_order.toResString()), 0
+ )
+
+ binding.inputContainer.replyTv.setText(R.string.message_detail_comment_hint2)
+ binding.inputContainer.replyTv.setRoundedColorBackground(R.color.ui_container_2, 19F)
+ binding.inputContainer.replyTv.setDebouncedClickListener {
+ clickToastByStatus(viewModel.imageArticleDetailEntity.value?.status ?: "") {
+ val imageArticle = viewModel.imageArticleDetailEntity.value ?: return@clickToastByStatus
+ val intent = CommentActivity.getImageArticleCommentIntent(
+ requireContext(),
+ imageArticle.id,
+ imageArticle.communityId,
+ viewModel.commentCount.value
+ )
+ startActivityForResult(intent, CommentActivity.REQUEST_CODE)
+ }
+ }
+ binding.inputContainer.bottomCommentIv.setOnClickListener {
+ binding.appBar.setExpanded(false, true)
+ }
+ binding.inputContainer.bottomCommentTv.setOnClickListener { binding.inputContainer.bottomCommentIv.performClick() }
+
+ binding.inputContainer.bottomStarIv.setOnClickListener {
+ requireContext().ifLogin(entrance = "帖子详情-收藏") {
+ clickToastByStatus(viewModel.imageArticleDetailEntity.value?.status ?: "") {
+ viewModel.collect()
+ }
+ }
+ }
+ binding.inputContainer.bottomStarTv.setOnClickListener { binding.inputContainer.bottomStarIv.performClick() }
+
+ binding.inputContainer.bottomLikeIv.setOnClickListener {
+ requireContext().ifLogin("帖子详情-赞同") {
+ clickToastByStatus(viewModel.imageArticleDetailEntity.value?.status ?: "") {
+ viewModel.vote()
+ }
+ }
+ }
+ binding.inputContainer.bottomLikeTv.setOnClickListener { binding.inputContainer.bottomLikeIv.performClick() }
+
+ binding.sfvOrder.setOnCheckedCallback {
+ val newSort = if (it == 0) {
+ BaseCommentViewModel.SortType.OLDEST
+ } else {
+ BaseCommentViewModel.SortType.LATEST
+ }
+ viewModel.changeSort(newSort)
+ }
+ binding.ctvFollow.setOnClickListener {
+ ifLogin("图文详情-关注") {
+ viewModel.followUser()
+ }
+ }
+ binding.vBack.setOnClickListener {
+ activity?.finish()
+ }
+ binding.ivAvatar.setOnClickListener {
+ DirectUtils.directToHomeActivity(
+ requireContext(),
+ viewModel.imageArticleDetailEntity.value?.user?.id,
+ 1,
+ mEntrance,
+ "图文详情"
+ )
+ }
+
+ binding.vpBanner.registerOnPageChangeCallback(object : OnPageChangeCallback() {
+ override fun onPageSelected(position: Int) {
+ showNumberIndicator(position)
+ }
+ })
+ }
+
+ private fun fillUi(imageArticle: ImageArticleEntity) {
+ binding.tvTitle.goneIf(imageArticle.title.isBlank()) {
+ binding.tvTitle.text = imageArticle.title
+ }
+ binding.tvContent.goneIf(imageArticle.content.isBlank()) {
+ binding.tvContent.text = imageArticle.content
+ }
+
+ binding.ivAvatar.display(imageArticle.user.border, imageArticle.user.icon, imageArticle.user.auth?.icon)
+ binding.tvName.text = imageArticle.user.name
+ if (imageArticle.user.auth == null) {
+ binding.tvAuth.goneIf(true)
+ TextViewCompat.setTextAppearance(binding.tvName, R.style.TextHeadline)
+ } else {
+ binding.tvAuth.goneIf(false)
+ binding.tvAuth.text = imageArticle.user.auth?.text
+ TextViewCompat.setTextAppearance(binding.tvName, R.style.TextBody2)
+ }
+
+ binding.ctvFollow.goneIf(imageArticle.me.isContentOwner)
+ binding.tvTime.text = NewsUtils.getFormattedTime(imageArticle.time.edit)
+
+ val imagesSize = imageArticle.images.size
+ showIndicator = imagesSize >= SHOW_DOT_INDICATOR_MIN
+ binding.dorIndicator.goneIf(!showIndicator) {
+ binding.dorIndicator.show(imagesSize)
+ }
+ binding.llNumberIndicator.goneIf(!showIndicator) {
+ binding.tvIndicatorTotal.text = "/${imagesSize}"
+ showNumberIndicator(0)
+ }
+
+
+ setBannerSize(imageArticle.imagesInfos.firstOrNull())
+ adapter.submitList(imageArticle.images)
+
+ val community = imageArticle.community
+ val game = community?.game
+ when {
+ game != null -> {
+ // 显示游戏
+ showGame(game, imageArticle.community.id)
+ }
+
+ community != null -> {
+ showCommunity(community)
+ }
+
+ else -> {
+ binding.clBbsContainer.goneIf(true)
+ binding.clGameContainer.goneIf(true)
+ }
+ }
+ }
+
+ private fun showCommunity(community: ImageArticleEntity.Community) {
+ binding.clBbsContainer.goneIf(false)
+ binding.clGameContainer.goneIf(true)
+ binding.ivBbsIcon.display(community.icon)
+ binding.tvBbsName.text = community.name
+ binding.tvHeat.text = "${community.hot}"
+ binding.clBbsContainer.setOnClickListener {
+ DirectUtils.directForumDetail(requireContext(), community.id, "帖子详情")
+ }
+ binding.ctvFollowBbs.setOnClickListener {
+ viewModel.followBbs(community.id)
+ }
+ }
+
+ private fun showGame(game: GameEntity, communityId: String) {
+ game.exposureEvent = ExposureEvent.createEventWithSourceConcat(
+ game,
+ listOf(),
+ listOf(ExposureSource("其他"), ExposureSource("图文详情"))
+ )
+ binding.clBbsContainer.goneIf(true)
+ binding.clGameContainer.goneIf(false)
+ binding.ivGameIcon.displayGameIcon(game)
+ binding.tvGameName.text = game.name
+ binding.tvGameRating.goneIf(!(game.commentCount > 3 && game.star >= 7)) {
+ binding.tvGameRating.text = game.star.toString()
+ }
+ BindingAdapters.setGameTags(binding.gtcvGameTags, game)
+
+ setDownloadButton(game)
+ binding.clGameContainer.setOnClickListener {
+ GameDetailActivity.startGameDetailActivity(
+ requireContext(),
+ game,
+ "图文详情",
+ traceEvent = game.exposureEvent
+ )
+ }
+
+ binding.tvGoBbs.setOnClickListener {
+ DirectUtils.directForumDetail(requireContext(), communityId, "帖子详情")
+ }
+ }
+
+ private fun setDownloadButton(gameEntity: GameEntity?) {
+ if (gameEntity == null) return
+ DownloadItemUtils.setOnClickListener(
+ requireContext(), binding.btnDownload,
+ gameEntity, 0, null,
+ mEntrance,
+ location = "视频详情",
+ traceEvent = gameEntity.exposureEvent,
+ clickCallback = {
+
+ },
+ refreshCallback = { setDownloadButton(gameEntity) },
+ allStateClickCallback = null
+ )
+ DownloadItemUtils.updateItem(
+ requireContext(),
+ gameEntity,
+ GameViewHolder(binding.clGameContainer).apply {
+ gameDownloadBtn = binding.btnDownload
+ gameDownloadTips = binding.downloadTipsLottie
+ multiVersionDownloadTv = binding.multiVersionDownloadTv
+ }
+ )
+ }
+
+ private fun setBannerSize(imageInfo: ImageInfo?) {
+ binding.vpBanner.goneIf(imageInfo == null) {
+ val bannerRadio = ImageArticleEntity.getImageRadio(imageInfo, BANNER_RADIO_MIN, BANNER_RADIO_MAX)
+ binding.vpBanner.updateLayoutParams {
+ height = (resources.displayMetrics.widthPixels * bannerRadio).toInt()
+ }
+ }
+
+ }
+
+ private fun showNumberIndicator(position: Int) {
+ if (showIndicator) {
+ binding.llNumberIndicator.goneIf(false)
+ binding.tvIndicatorPosition.text = "${position + 1}"
+ handler.removeMessages(HIDE_BANNER_NUMBER_INDICATOR)
+ handler.sendEmptyMessageDelayed(HIDE_BANNER_NUMBER_INDICATOR, SHOW_NUMBER_INDICATOR_DURATION)
+ binding.dorIndicator.selectPosition(position)
+ }
+ }
+
+ private fun showMoreItemDialog() {
+ val imageArticle = viewModel.imageArticleDetailEntity.value
+ if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) && imageArticle != null) {
+ val entities = ArrayList()
+ // 申请加精
+ val simplifyChoicenessStatus = imageArticle.getSimplifyChoicenessStatus()
+ if (imageArticle.user.id == UserManager.getInstance().userId && !imageArticle.me.isModerator && imageArticle.status == "pass") {
+ val isEnable =
+ simplifyChoicenessStatus != ArticleDetailEntity.STATUS_PASS
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_apply_select_title),
+ if (isEnable)
+ R.drawable.icon_more_panel_essence else R.drawable.icon_more_panel_essence_unenable,
+ isEnable = isEnable
+ )
+ )
+ }
+
+ // 修改
+ if (imageArticle.me.isContentOwner
+ && imageArticle.status == ArticleDetailEntity.STATUS_PASS
+ ) {
+ entities.add(
+ MenuItemEntity(getString(R.string.article_detail_more_edit_title), R.drawable.icon_more_panel_edit)
+ )
+ }
+
+ // 举报
+ if (!imageArticle.me.isContentOwner) {
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_complaint_title),
+ R.drawable.icon_gamedetail_copyright
+ )
+ )
+ }
+
+ val moderatorPermissions = imageArticle.me.moderatorPermissions
+ // 取消精选
+ if (imageArticle.me.isModerator
+ && imageArticle.status == ArticleDetailEntity.STATUS_PASS
+ ) {
+ if (simplifyChoicenessStatus == ArticleDetailEntity.STATUS_PASS) {
+ if ((moderatorPermissions.cancelHighlightCommunityArticle) > Permissions.GUEST
+ ) {
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_unselect_title),
+ R.drawable.icon_more_panel_essence_cancel
+ )
+ )
+ }
+ } else {
+ if ((moderatorPermissions.highlightCommunityArticle) > Permissions.GUEST
+ ) {
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_select_title),
+ R.drawable.icon_more_panel_essence
+ )
+ )
+ }
+ }
+ }
+
+ // 修改活动标签
+ if (imageArticle.me.isModerator &&
+ (moderatorPermissions.updateArticleActivityTag) > Permissions.GUEST &&
+ imageArticle.status == ArticleDetailEntity.STATUS_PASS
+ ) {
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_edit_activity_tag_title),
+ R.drawable.icon_more_panel_modify_label
+ )
+ )
+ }
+
+ // 隐藏/删除
+ if (imageArticle.me.isModerator && moderatorPermissions.hideCommunityArticle > Permissions.GUEST
+ ) {
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_hide_title),
+ R.drawable.icon_more_panel_delete
+ )
+ )
+ } else {
+ if (imageArticle.me.isContentOwner) {
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_delete_title),
+ R.drawable.icon_more_panel_delete
+ )
+ )
+ }
+ }
+
+ // 置顶/取消置顶
+ if (imageArticle.me.isModerator
+ && !imageArticle.me.isCommunityTop
+ && moderatorPermissions.topCommunityArticle > Permissions.GUEST
+ ) {
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_top_title),
+ R.drawable.icon_more_panel_top
+ )
+ )
+ } else if (imageArticle.me.isModerator
+ && imageArticle.me.isCommunityTop
+ && moderatorPermissions.cancelTopCommunityArticle > Permissions.GUEST
+ ) {
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_cancel_top_title),
+ R.drawable.icon_more_panel_top_cancel
+ )
+ )
+ }
+
+ MoreFunctionPanelDialog.showMoreDialog(
+ requireActivity() as AppCompatActivity,
+ entities,
+ imageArticle.title.ifBlank { imageArticle.content },
+ getShareEntity(imageArticle),
+ imageArticle.status,
+ tag ?: ""
+ )
+ }
+ }
+
+ private fun getShareEntity(imageArticle: ImageArticleEntity): NormalShareEntity {
+ val params = AdditionalParamsEntity().apply {
+ contentType = "帖子"
+ contentId = imageArticle.id
+ bbsId = imageArticle.communityId
+ bbsType = imageArticle.community?.typeChinese ?: "综合论坛"
+ customerType = imageArticle.user.auth?.text ?: ""
+ activityTagName = ""
+ gameForumType = imageArticle.community?.game?.categoryChinese ?: ""
+ refUserId = UserManager.getInstance().userId
+ }
+ return NormalShareEntity(
+ id = imageArticle.id,
+ shareUrl = requireContext().getString(
+ R.string.share_community_image_article_url,
+ imageArticle.shortId
+ ),
+ shareIcon = if (imageArticle.images.isNotEmpty()) {
+ imageArticle.images.first()
+ } else {
+ requireContext().getString(R.string.share_ghzs_logo)
+ },
+ shareTitle = imageArticle.title.ifBlank { imageArticle.content }
+ .ifBlank { getString(R.string.image_article) },
+ shareSummary = imageArticle.content,
+ shareEntrance = ShareUtils.ShareEntrance.communityArticle,
+ additionalParams = params
+ )
+ }
+
+ override fun onResume() {
+ super.onResume()
+ DownloadManager.getInstance().addObserver(dataWatcher)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ DownloadManager.getInstance().removeObserver(dataWatcher)
+ }
+
+ //下载被删除事件
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onEventMainThread(status: EBDownloadStatus) {
+ if ("delete" == status.status) {
+ setDownloadButton(viewModel.imageArticleDetailEntity.value?.community?.game)
+ }
+ }
+
+ //安装、卸载事件
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onEventMainThread(busFour: EBPackage) {
+ setDownloadButton(viewModel.imageArticleDetailEntity.value?.community?.game)
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onEventMainThread(reuse: EBReuse) {
+ if (reuse.type == Constants.LOGIN_TAG) { // 登入
+ viewModel.loadImageArticleDetail(imageArticleId)
+
+ }
+ val path = Path()
+ path.reset()
+ }
+
+ //
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onEventMainThread(changed: EBImageArticleChanged) {
+ viewModel.imageArticleChanged(changed)
+ }
+
+ override fun onDarkModeChanged() {
+ super.onDarkModeChanged()
+ setDownloadButton(viewModel.imageArticleDetailEntity.value?.community?.game)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ when {
+ requestCode == CommentActivity.REQUEST_CODE && resultCode == Activity.RESULT_OK -> {
+ val commentCount = data?.getIntExtra(CommentActivity.COMMENT_COUNT, 0) ?: 0
+ val commentId = data?.getStringExtra(EntranceConsts.KEY_COMMENT_ID) ?: ""
+ viewModel.updateComment(commentId, commentCount)
+ }
+
+ requestCode == MoreFunctionPanelDialog.REQUEST_CODE && resultCode == Activity.RESULT_OK -> {
+ getItemClickCallback().invoke(data?.getParcelableExtra(EntranceConsts.KEY_DATA))
+ }
+ }
+ }
+
+ private fun getItemClickCallback(): (menuItem: MenuItemEntity?) -> Unit {
+ return {
+ when (it?.text) {
+ getString(R.string.article_detail_more_edit_title) -> {
+ ARouter.getInstance().build(RouteConsts.activity.publishImageArticleActivity)
+ .withParcelable(
+ EntranceConsts.KEY_IMAGE_ARTICLE_ENTITY,
+ viewModel.imageArticleDetailEntity.value
+ )
+ .navigation()
+ }
+
+ getString(R.string.article_detail_more_complaint_title) -> {
+ ifLogin("图文详情") {
+ BbsReportHelper.showReportDialog(BbsReportHelper.ImageArticleReporter(imageArticleId))
+ }
+
+ }
+
+ getString(R.string.article_detail_more_apply_select_title) -> {
+ if (viewModel.imageArticleDetailEntity.value?.getSimplifyChoicenessStatus() == "apply") {
+ ToastUtils.showToast("申请加精审核中")
+ } else {
+ viewModel.applyHighlightForImageArticle()
+ }
+ }
+
+ getString(R.string.article_detail_more_select_title) -> {
+ if (viewModel.imageArticleDetailEntity.value?.getSimplifyChoicenessStatus() == "apply") {
+ ToastUtils.showToast("加精审核中")
+ } else {
+ showHighlightDialog(true)
+ }
+ }
+
+ getString(R.string.article_detail_more_unselect_title) -> {
+ showHighlightDialog(false)
+ }
+
+ getString(R.string.article_detail_more_delete_title),
+ getString(R.string.article_detail_more_hide_title) -> {
+ DialogHelper.showDialog(
+ requireContext(),
+ "提示",
+ "${it.text}帖子后,其中的所有评论及回复都将被${it.text}",
+ it.text,
+ "取消",
+ confirmClickCallback = {
+ viewModel.deleteOrHideImageArticle()
+ }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
+ )
+ }
+
+ getString(R.string.article_detail_more_top_title) -> {
+// TopCommunityCategoryDialog.show(
+// childFragmentManager
+// ) { category ->
+// mViewModel.topCommunityArticle(category.id)
+// }
+ }
+
+ getString(R.string.article_detail_more_cancel_top_title) -> {
+ DialogHelper.showDialog(
+ requireContext(),
+ getString(R.string.article_detail_cancel_top_dialog_title),
+ getString(R.string.article_detail_cancel_top_dialog_hint),
+ getString(R.string.article_detail_cancel_top_dialog_confirm),
+ getString(R.string.article_detail_cancel_top_dialog_cancel),
+ confirmClickCallback = {
+// mViewModel.cancelTopCommunityArticle()
+ },
+ extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
+ )
+ }
+ }
+ }
+ }
+
+ private fun showHighlightDialog(isHighlight: Boolean) {
+ val imageArticle = viewModel.imageArticleDetailEntity.value ?: return
+ var highlightDialogHintContent = ""
+ val permissions = imageArticle.me.moderatorPermissions
+ if ((isHighlight && permissions.highlightCommunityArticle > Permissions.GUEST) ||
+ (!isHighlight && permissions.cancelHighlightCommunityArticle > Permissions.GUEST)
+ ) {
+ highlightDialogHintContent =
+ if ((isHighlight && permissions.highlightCommunityArticle == Permissions.REPORTER) ||
+ (!isHighlight && permissions.cancelHighlightCommunityArticle == Permissions.REPORTER)
+ ) {
+ "你的操作将提交给小编审核,确定提交吗?"
+ } else {
+ "你的操作将立即生效,确定提交吗?"
+ }
+ }
+ val title = if (isHighlight) "加精帖子" else "取消精选"
+ DialogHelper.showDialog(
+ requireContext(),
+ title,
+ highlightDialogHintContent,
+ "确定",
+ "取消",
+ {
+ viewModel.addHighlight(isHighlight)
+ }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
+ )
+ }
+
+ companion object {
+ private const val HIDE_BANNER_NUMBER_INDICATOR = 1
+ private const val SHOW_NUMBER_INDICATOR_DURATION = 5000L
+ private const val SHOW_DOT_INDICATOR_MIN = 2
+ private const val BANNER_RADIO_MIN = 0.5F
+ private const val BANNER_RADIO_MAX = 1.33F
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleDraftFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleDraftFragment.kt
new file mode 100644
index 0000000000..50390bb017
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleDraftFragment.kt
@@ -0,0 +1,161 @@
+package com.gh.gamecenter.forum.home.recommend.fragment
+
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.activityViewModels
+import androidx.fragment.app.viewModels
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.StaggeredGridLayoutManager
+import com.alibaba.android.arouter.launcher.ARouter
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.base.fragment.LazyFragment
+import com.gh.gamecenter.common.baselist.PageLoader
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.common.utils.DialogHelper
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.goneIf
+import com.gh.gamecenter.common.utils.toResString
+import com.gh.gamecenter.databinding.FragmentImageArticleDraftBinding
+import com.gh.gamecenter.eventbus.EBImageArticleChanged
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageArticleDraftAdapter
+import com.gh.gamecenter.forum.home.recommend.viewmodel.ImageArticleDraftViewModel
+import com.gh.gamecenter.forum.home.recommend.viewmodel.PublishImageArticleActivityViewModel
+import com.gh.gamecenter.livedata.EventObserver
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class ImageArticleDraftFragment : LazyFragment() {
+
+ private val viewModel by viewModels()
+ private val activityViewModel by activityViewModels()
+
+ private lateinit var binding: FragmentImageArticleDraftBinding
+
+ private var isFromPublishPage = false
+
+ private val adapter by lazy {
+ ImageArticleDraftAdapter(requireContext(), viewModel)
+ }
+
+ override fun getRealLayoutId(): Int {
+ return R.layout.fragment_image_article_draft
+ }
+
+ override fun onRealLayoutInflated(inflatedView: View) {
+ binding = FragmentImageArticleDraftBinding.bind(inflatedView)
+ }
+
+ override fun initRealView() {
+ super.initRealView()
+ isFromPublishPage = arguments?.getBoolean(KEY_IS_FROM_PUBLISH_PAGE) ?: false
+ binding.rvDrafts.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
+ binding.rvDrafts.addItemDecoration(MyItemDecoration())
+ binding.rvDrafts.adapter = adapter
+
+ binding.srlRefresh.setOnRefreshListener {
+ viewModel.loadDraft(true)
+ }
+
+ with(viewModel) {
+ pageState.observe(viewLifecycleOwner) {
+
+ binding.srlRefresh.goneIf(
+ it == PageLoader.PageState.PageNoData || it == PageLoader.PageState.PageInitFailure
+ ) {
+ binding.srlRefresh.isRefreshing = false
+ }
+ binding.reuseLlLoading.root.goneIf(it != PageLoader.PageState.PageInitLoading)
+ binding.reuseNoneData.root.goneIf(it != PageLoader.PageState.PageNoData)
+ binding.reuseDataException.root.goneIf(it != PageLoader.PageState.PageInitFailure)
+
+ }
+
+ drafts.observe(viewLifecycleOwner) {
+ adapter.submitList(it)
+ }
+
+ showDeleteDraftDialogAction.observe(viewLifecycleOwner, EventObserver {
+ DialogHelper.showDialog(
+ requireContext(),
+ R.string.warning.toResString(),
+ R.string.delete_image_article_warning.toResString(),
+ R.string.confirm.toResString(),
+ R.string.cancel.toResString(),
+ confirmClickCallback = {
+ viewModel.deleteDraft(it)
+ }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
+ )
+ })
+
+ publishImageArticleDestination.observe(viewLifecycleOwner, EventObserver {
+ it.isDraft = true
+ if (isFromPublishPage) {
+ // 如果是从发布页进入草稿箱,则回到发布页,并将草稿数据回传
+ activityViewModel.setEditImageArticle(it)
+ activity?.onBackPressedDispatcher?.onBackPressed()
+ } else {
+ // 否则,直接打开新的发布页
+ ARouter.getInstance().build(RouteConsts.activity.publishImageArticleActivity)
+ .withParcelable(EntranceConsts.KEY_IMAGE_ARTICLE_ENTITY, it)
+ .navigation()
+ }
+ })
+
+ loadDraft(false)
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onEventMainThread(changed: EBImageArticleChanged) {
+ if (changed is EBImageArticleChanged.DataCreated
+ && changed.draftId.isNotBlank()
+ ) {
+ // 如果时选择的草稿发布,则删除当前选中草稿
+ val draftList = viewModel.drafts.value?.toMutableList()
+ if (draftList != null) {
+ val hasRemoved = draftList.removeAll { it.id == changed.draftId }
+ if (hasRemoved) {
+ if (draftList.isEmpty()) {
+ viewModel.loadDraft(true)
+ } else {
+ adapter.submitList(null)
+ adapter.notifyDataSetChanged()
+ viewModel.updateDrafts(draftList)
+ }
+ }
+ }
+ }
+ }
+
+ private class MyItemDecoration : RecyclerView.ItemDecoration() {
+
+ override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
+ super.getItemOffsets(outRect, view, parent, state)
+ val layoutParams = view.layoutParams
+ if (layoutParams is StaggeredGridLayoutManager.LayoutParams) {
+ val spanIndex = layoutParams.spanIndex
+ val (left, right) = if (spanIndex == 0) {
+ 8F.dip2px() to 2F.dip2px()
+ } else {
+ 2F.dip2px() to 8F.dip2px()
+ }
+ outRect.left = left
+ outRect.right = right
+ }
+ }
+ }
+
+ companion object {
+
+ private const val KEY_IS_FROM_PUBLISH_PAGE = "key_is_from_publish_page"
+
+ fun newInstance(isFromPublishPage: Boolean) =
+ ImageArticleDraftFragment().apply {
+ arguments = Bundle().apply {
+ putBoolean(KEY_IS_FROM_PUBLISH_PAGE, isFromPublishPage)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleHomeFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleHomeFragment.kt
new file mode 100644
index 0000000000..f4cad8749c
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/ImageArticleHomeFragment.kt
@@ -0,0 +1,233 @@
+package com.gh.gamecenter.forum.home.recommend.fragment
+
+import android.graphics.Rect
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.view.View
+import androidx.fragment.app.viewModels
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.StaggeredGridLayoutManager
+import com.alibaba.android.arouter.launcher.ARouter
+import com.ethanhua.skeleton.Skeleton
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.base.fragment.LazyFragment
+import com.gh.gamecenter.common.baselist.PageLoader
+import com.gh.gamecenter.common.constant.Constants
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.goneIf
+import com.gh.gamecenter.common.utils.toJson
+import com.gh.gamecenter.databinding.FragmentImageArticleHomeBinding
+import com.gh.gamecenter.eventbus.EBImageArticleChanged
+import com.gh.gamecenter.forum.home.CommunityHomeViewModel
+import com.gh.gamecenter.forum.home.recommend.ImageArticleDetailActivity
+import com.gh.gamecenter.forum.home.recommend.ImageArticleFooterWrapperAdapter
+import com.gh.gamecenter.forum.home.recommend.adapter.RecommendHomeAdapter
+import com.gh.gamecenter.forum.home.recommend.viewmodel.ImageArticleHomeViewModel
+import com.gh.gamecenter.livedata.EventObserver
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class ImageArticleHomeFragment : LazyFragment() {
+
+ private val viewModel: ImageArticleHomeViewModel by viewModels()
+
+ private lateinit var binding: FragmentImageArticleHomeBinding
+
+ private val handler = Handler(Looper.getMainLooper())
+
+ private val skeletonScreen by lazy {
+ Skeleton.bind(binding.flSkeleton)
+ .shimmer(true)
+ .angle(Constants.SHIMMER_ANGLE)
+ .color(R.color.ui_skeleton_highlight)
+ .duration(Constants.SHIMMER_DURATION)
+ .maskWidth(Constants.MASK_WIDTH)
+ .gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
+ .load(R.layout.fragment_image_article_home_skeleton)
+ .show()
+ }
+
+ private val adapter by lazy {
+ RecommendHomeAdapter(requireContext(), viewModel)
+ }
+
+ override fun getRealLayoutId(): Int = R.layout.fragment_image_article_home
+
+ override fun onRealLayoutInflated(inflatedView: View) {
+ binding = FragmentImageArticleHomeBinding.bind(inflatedView)
+ }
+
+ override fun initRealView() {
+ super.initRealView()
+ binding.rvRecommends.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL).also {
+ it.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_NONE
+ }
+ binding.rvRecommends.adapter = adapter
+ binding.rvRecommends.addItemDecoration(MyItemDecoration())
+
+ binding.srlRefresh.setOnRefreshListener {
+ viewModel.pageLoader.pullToRefresh()
+ }
+
+ binding.reuseNoConnection.connectionReloadTv.setOnClickListener {
+ viewModel.initLoad()
+ }
+
+ with(viewModel) {
+ pageLoader.pageState.observe(viewLifecycleOwner) {
+ if (it == PageLoader.PageState.PageInitLoading) {
+ skeletonScreen
+ } else {
+ skeletonScreen.hide()
+ }
+
+ if (it is PageLoader.PageState.PagePullToRefreshLoading) {
+ // 正在下拉刷新,页面保持不变
+ binding.srlRefresh.isRefreshing = true
+ } else {
+ binding.reuseNoneData.root.goneIf(it != PageLoader.PageState.PageNoData)
+ binding.reuseNoConnection.root.goneIf(it != PageLoader.PageState.PageInitFailure)
+
+ binding.srlRefresh.isEnabled = it != PageLoader.PageState.PageInitLoading
+ && it != PageLoader.PageState.PageInitFailure
+ binding.srlRefresh.isRefreshing = false
+ }
+
+ if (it is PageLoader.PageState.PageLoadMoreReady
+ && (it.previousState is PageLoader.PageState.PagePullToRefreshLoading
+ || it.previousState is PageLoader.PageState.PageInitLoading)
+ ) {
+ // 加载第一页完成
+ if (it.previousState is PageLoader.PageState.PagePullToRefreshLoading) {
+ // 下拉刷新成功,先清空上一次内容
+ adapter.submitList(null)
+ adapter.notifyDataSetChanged()
+ }
+ // 延时一下,检查第一页数据是否已铺满第一页,如果未铺满,则直接请求下一页
+ handler.removeCallbacksAndMessages(null)
+ handler.postDelayed({
+ checkIfLoadNextPage(binding.rvRecommends, viewModel::loadMore)
+ }, 16)
+ }
+ adapter.setPageState(it)
+
+ }
+
+ pageLoader.dataList.observe(viewLifecycleOwner) {
+ adapter.submitList(it)
+ }
+
+ imageDetailDestination.observe(viewLifecycleOwner, EventObserver {
+ ARouter.getInstance().build(RouteConsts.activity.imageArticleDetailActivity)
+ .withString(EntranceConsts.KEY_IMAGE_ARTICLE_ID, it)
+ .navigation()
+ })
+
+ initLoad()
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onEventMainThread(changed: EBImageArticleChanged) {
+ when (changed) {
+ is EBImageArticleChanged.VoteChanged -> {
+ viewModel.voteChanged(changed)
+ }
+
+ is EBImageArticleChanged.DataChanged -> {
+ viewModel.itemChanged(changed)
+ }
+
+ is EBImageArticleChanged.DataDeleted -> {
+ itemDeleted(changed)
+ }
+
+ is EBImageArticleChanged.DataCreated -> {
+ itemCreated(changed)
+ }
+
+ else -> Unit
+
+ }
+ }
+
+ private fun itemCreated(changed: EBImageArticleChanged.DataCreated) {
+ val newData = viewModel.pageLoader.dataList.value?.toMutableList() ?: mutableListOf()
+ if (newData.isEmpty()) {
+ viewModel.initLoad()
+ } else {
+ // 瀑布流布局,在中间添加或者移除item时,会出现错位的现象,这里需要先将原来的数据清空,在重新添加可以避免这个问题
+ adapter.submitList(null)
+ newData.add(0, changed.imageArticle)
+ viewModel.pageLoader.updateData(newData)
+ }
+ }
+
+ private fun itemDeleted(changed: EBImageArticleChanged.DataDeleted) {
+ val oldData = viewModel.pageLoader.dataList.value ?: return
+ val newData = oldData.toMutableList()
+ val hasRemoved = newData.removeAll {
+ it.id == changed.imageArticleId
+ }
+ if (hasRemoved) {
+ if (newData.isEmpty()) {
+ viewModel.initLoad()
+ } else {
+ adapter.submitList(null)
+ viewModel.pageLoader.updateData(newData)
+ }
+ }
+
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ handler.removeCallbacksAndMessages(null)
+ }
+
+ companion object {
+
+ private const val KEY_IS_IN_MY_PUBLISH = "key_is_in_my_publish"
+
+ fun newInstance() = ImageArticleHomeFragment()
+
+ fun checkIfLoadNextPage(recyclerView: RecyclerView, loadNext: () -> Unit) {
+ val layoutManager = recyclerView.layoutManager
+ if (layoutManager is StaggeredGridLayoutManager) {
+ val itemPositions = layoutManager.findLastCompletelyVisibleItemPositions(null)
+ val lastPosition = itemPositions.maxOrNull() ?: 0
+ val itemType = recyclerView.adapter?.getItemViewType(lastPosition) ?: 0
+ if (itemType == ImageArticleFooterWrapperAdapter.ITEM_TYPE_FOOTER) {
+ // 底部加载更多的itemView完全显示在屏幕内,说明数据未铺满一屏:直接加载下一页
+ loadNext()
+ }
+ }
+ }
+ }
+
+ class MyItemDecoration : RecyclerView.ItemDecoration() {
+
+ override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
+ val layoutParams = view.layoutParams
+ if (layoutParams is StaggeredGridLayoutManager.LayoutParams) {
+ if (parent.getChildAdapterPosition(view) == state.itemCount - 1) {
+ layoutParams.isFullSpan = true
+ } else {
+ val spanIndex = layoutParams.spanIndex
+ val (left, right) = if (spanIndex % 2 == 0) {
+ 8F.dip2px() to 2F.dip2px()
+ } else {
+ 2F.dip2px() to 8F.dip2px()
+ }
+ outRect.left = left
+ outRect.right = right
+ }
+ }
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/MyImageArticleListFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/MyImageArticleListFragment.kt
new file mode 100644
index 0000000000..cd9f8466d1
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/MyImageArticleListFragment.kt
@@ -0,0 +1,213 @@
+package com.gh.gamecenter.forum.home.recommend.fragment
+
+import android.annotation.SuppressLint
+import android.graphics.Rect
+import android.os.Handler
+import android.os.Looper
+import android.view.View
+import androidx.fragment.app.viewModels
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.StaggeredGridLayoutManager
+import com.alibaba.android.arouter.launcher.ARouter
+import com.ethanhua.skeleton.Skeleton
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.base.fragment.LazyFragment
+import com.gh.gamecenter.common.baselist.PageLoader
+import com.gh.gamecenter.common.constant.Constants
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.goneIf
+import com.gh.gamecenter.databinding.FragmentImageArticleHomeBinding
+import com.gh.gamecenter.eventbus.EBImageArticleChanged
+import com.gh.gamecenter.forum.home.recommend.adapter.MyImageArticleAdapter
+import com.gh.gamecenter.forum.home.recommend.fragment.ImageArticleHomeFragment.Companion.checkIfLoadNextPage
+import com.gh.gamecenter.forum.home.recommend.viewmodel.MyImageArticleListViewModel
+import com.gh.gamecenter.livedata.EventObserver
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class MyImageArticleListFragment : LazyFragment() {
+
+ private val handler = Handler(Looper.getMainLooper())
+
+ private val viewModel by viewModels()
+
+ private lateinit var binding: FragmentImageArticleHomeBinding
+
+ private val skeletonScreen by lazy {
+ Skeleton.bind(binding.flSkeleton)
+ .shimmer(true)
+ .angle(Constants.SHIMMER_ANGLE)
+ .color(R.color.ui_skeleton_highlight)
+ .duration(Constants.SHIMMER_DURATION)
+ .maskWidth(Constants.MASK_WIDTH)
+ .gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
+ .load(R.layout.fragment_image_article_home_skeleton)
+ .show()
+ }
+
+ private val adapter by lazy {
+ MyImageArticleAdapter(requireContext(), viewModel)
+ }
+
+ override fun getRealLayoutId(): Int = R.layout.fragment_image_article_home
+
+ override fun onRealLayoutInflated(inflatedView: View) {
+ binding = FragmentImageArticleHomeBinding.bind(inflatedView)
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ override fun initRealView() {
+ super.initRealView()
+ binding.rvRecommends.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
+ binding.rvRecommends.adapter = adapter
+ binding.rvRecommends.addItemDecoration(MyItemDecoration())
+
+ binding.srlRefresh.setOnRefreshListener {
+ viewModel.pageLoader.pullToRefresh()
+ }
+
+ binding.reuseNoConnection.connectionReloadTv.setOnClickListener {
+ viewModel.initLoad()
+ }
+
+ with(viewModel) {
+ pageLoader.pageState.observe(viewLifecycleOwner) {
+ if (it == PageLoader.PageState.PageInitLoading) {
+ skeletonScreen
+ } else {
+ skeletonScreen.hide()
+ }
+
+ if (it is PageLoader.PageState.PagePullToRefreshLoading) {
+ // 正在下拉刷新,页面保持不变
+ binding.srlRefresh.isRefreshing = true
+ } else {
+ binding.reuseNoneData.root.goneIf(it != PageLoader.PageState.PageNoData)
+ binding.reuseNoConnection.root.goneIf(it != PageLoader.PageState.PageInitFailure)
+
+ binding.srlRefresh.isEnabled = it != PageLoader.PageState.PageInitLoading
+ && it != PageLoader.PageState.PageInitFailure
+ binding.srlRefresh.isRefreshing = false
+ }
+
+ if (it is PageLoader.PageState.PageLoadMoreReady
+ && (it.previousState is PageLoader.PageState.PagePullToRefreshLoading
+ || it.previousState is PageLoader.PageState.PageInitLoading)
+ ) {
+ if (it.previousState is PageLoader.PageState.PagePullToRefreshLoading) {
+ adapter.submitList(null)
+ adapter.notifyDataSetChanged()
+ }
+ // 延时一下,检查第一页数据是否已铺满第一页,如果未铺满,则直接请求下一页
+ handler.removeCallbacksAndMessages(null)
+ handler.postDelayed({
+ checkIfLoadNextPage(binding.rvRecommends, viewModel::loadMore)
+ }, 16)
+ }
+ adapter.setPageState(it)
+ }
+
+ pageLoader.dataList.observe(viewLifecycleOwner) {
+ adapter.submitList(it)
+ }
+
+ imageArticleDetailDestination.observe(viewLifecycleOwner, EventObserver {
+ ARouter.getInstance().build(RouteConsts.activity.imageArticleDetailActivity)
+ .withString(EntranceConsts.KEY_IMAGE_ARTICLE_ID, it)
+ .navigation()
+ })
+
+ initLoad()
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onEventMainThread(changed: EBImageArticleChanged) {
+ when (changed) {
+ is EBImageArticleChanged.VoteChanged -> {
+ viewModel.voteChanged(changed)
+ }
+
+ is EBImageArticleChanged.DataChanged -> {
+ viewModel.itemChanged(changed)
+ }
+
+ is EBImageArticleChanged.DataDeleted -> {
+ itemDeleted(changed)
+ }
+
+ is EBImageArticleChanged.DataCreated -> {
+ itemCreated(changed)
+ }
+
+ else -> Unit
+ }
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private fun itemCreated(changed: EBImageArticleChanged.DataCreated) {
+ val newData = viewModel.pageLoader.dataList.value?.toMutableList() ?: mutableListOf()
+ if (newData.isEmpty()) {
+ viewModel.initLoad()
+ } else {
+ adapter.submitList(null)
+ // 请注意:这一行代码不能省
+ adapter.notifyDataSetChanged()
+ newData.add(0, changed.imageArticle.toPersonHistoryEntity())
+ viewModel.pageLoader.updateData(newData)
+ }
+
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private fun itemDeleted(changed: EBImageArticleChanged.DataDeleted) {
+ val oldData = viewModel.pageLoader.dataList.value ?: return
+ val newData = oldData.toMutableList()
+ val hasRemoved = newData.removeAll {
+ it.id == changed.imageArticleId
+ }
+ if (hasRemoved) {
+ adapter.submitList(null)
+ adapter.notifyDataSetChanged()
+ if (newData.isEmpty()) {
+ viewModel.initLoad()
+ } else {
+ viewModel.pageLoader.updateData(newData)
+ }
+
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ handler.removeCallbacksAndMessages(null)
+ }
+
+ companion object {
+
+ fun newInstance() = MyImageArticleListFragment()
+ }
+
+ private class MyItemDecoration : RecyclerView.ItemDecoration() {
+
+ override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
+ val layoutParams = view.layoutParams
+ if (layoutParams is StaggeredGridLayoutManager.LayoutParams) {
+ if (parent.getChildAdapterPosition(view) == state.itemCount - 1) {
+ layoutParams.isFullSpan = true
+ } else {
+ val spanIndex = layoutParams.spanIndex
+ val (left, right) = if (spanIndex % 2 == 0) {
+ 8F.dip2px() to 2F.dip2px()
+ } else {
+ 2F.dip2px() to 8F.dip2px()
+ }
+ outRect.left = left
+ outRect.right = right
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/PublishImageArticleFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/PublishImageArticleFragment.kt
new file mode 100644
index 0000000000..c4598d746d
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/fragment/PublishImageArticleFragment.kt
@@ -0,0 +1,519 @@
+package com.gh.gamecenter.forum.home.recommend.fragment
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.Rect
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.text.Editable
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver.OnGlobalLayoutListener
+import android.widget.EditText
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.updateLayoutParams
+import androidx.core.widget.addTextChangedListener
+import androidx.fragment.app.activityViewModels
+import androidx.fragment.app.viewModels
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_IDLE
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.base.fragment.BaseFragment
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.entity.CommunityEntity
+import com.gh.gamecenter.common.utils.*
+import com.gh.gamecenter.core.AppExecutor
+import com.gh.gamecenter.core.utils.ToastUtils
+import com.gh.gamecenter.databinding.FragmentPublishImageArticleBinding
+import com.gh.gamecenter.entity.ForumDetailEntity
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageAndTextAdapter
+import com.gh.gamecenter.forum.home.recommend.viewmodel.PublishImageArticleActivityViewModel
+import com.gh.gamecenter.forum.home.recommend.viewmodel.PublishImageArticleViewModel
+import com.gh.gamecenter.livedata.EventObserver
+import com.gh.gamecenter.qa.dialog.ChooseForumActivity
+import com.gh.gamecenter.qa.dialog.ChooseSectionDialogFragment
+import com.lightgame.listeners.OnBackPressedListener
+import com.lightgame.utils.Util_System_Keyboard
+
+class PublishImageArticleFragment : BaseFragment(), OnBackPressedListener {
+
+ private val activityViewModel by activityViewModels()
+ private val viewModel by viewModels()
+
+ private lateinit var binding: FragmentPublishImageArticleBinding
+
+ private val rootView: View?
+ get() = activity?.findViewById(android.R.id.content)
+
+ private val itemTouchHelper by lazy {
+ ItemTouchHelper(MyItemTouchCallback(activityViewModel))
+ }
+
+ private val adapter by lazy {
+ ImageAndTextAdapter(viewModel) {
+ itemTouchHelper.startDrag(it)
+ }
+ }
+
+ private val savingDraftLoading by lazy {
+ DialogHelper.getProgressDialog(requireContext(), R.string.draft_saving.toResString())
+ }
+
+ private val publishingLoading by lazy {
+ DialogHelper.getProgressDialog(requireContext(), R.string.image_article_publishing.toResString())
+ }
+
+ override fun getInflatedLayout(): View {
+ return View(requireContext())
+ }
+
+ private val onGlobalLayoutListener = OnGlobalLayoutListener {
+ onKeyboardChanged()
+ }
+
+ private val handler = Handler(Looper.getMainLooper())
+
+ private var isKeyBoardShow = false
+
+ private lateinit var chooseForumLauncher: ActivityResultLauncher
+
+ override fun getLayoutId(): Int = 0
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ chooseForumLauncher = registerForActivityResult(object :
+ ActivityResultContract() {
+
+ override fun createIntent(context: Context, input: String): Intent {
+ val intent = Intent(context, ChooseForumActivity::class.java)
+ intent.putExtra(EntranceConsts.KEY_SOURCE_ENTRANCE, input)
+ return intent
+ }
+
+ override fun parseResult(resultCode: Int, intent: Intent?): CommunityEntity? {
+ return intent?.getParcelableExtra(EntranceConsts.KEY_COMMUNITY_DATA)
+ }
+ }
+ ) { result -> result?.let(viewModel::changeLinkBbs) }
+
+ activityViewModel.editImageArticle.observe(this, EventObserver {
+ viewModel.setEditImageArticle(it)
+ })
+
+ activityViewModel.community.observe(this, EventObserver {
+ viewModel.changeLinkBbs(it)
+ })
+ }
+
+ override fun initView(view: View?) = Unit
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return FragmentPublishImageArticleBinding.inflate(inflater, container, false)
+ .also {
+ binding = it
+ }.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ view.post {
+ setContentHeight(0)
+ }
+
+
+ binding.rvImages.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
+ binding.rvImages.addItemDecoration(MyItemDecoration())
+ binding.rvImages.adapter = adapter
+ itemTouchHelper.attachToRecyclerView(binding.rvImages)
+
+ binding.etTitle.addTextChangedListener { text ->
+ if (text != null) {
+ checkMaxLimitReached(binding.etTitle, text, TITLE_MAX_LENGTH, R.string.image_and_text_title_limit)
+ var remainingSize = TITLE_MAX_LENGTH - text.length
+ if (remainingSize < 0) {
+ remainingSize = 0
+ }
+ binding.tvRemainingWords.text = "$remainingSize"
+ }
+ binding.ivClear.visibleIf(!text.isNullOrBlank() && binding.etTitle.hasFocus())
+ }
+
+ binding.etTitle.setOnFocusChangeListener { _, hasFocus ->
+ binding.ivClear.visibleIf(hasFocus && binding.etTitle.text.isNotBlank())
+ binding.tvRemainingWords.visibleIf(hasFocus)
+ }
+
+ binding.tvSelectBbs.setOnClickListener {
+ showSelectBbsDialog()
+ }
+
+ binding.ivClear.setOnClickListener {
+ binding.etTitle.text = null
+ }
+
+ binding.vCloseDragTips.setOnClickListener {
+ handler.removeCallbacksAndMessages(null)
+ binding.gDragTips.goneIf(true)
+ }
+
+ binding.etContent.addTextChangedListener { text ->
+ if (text != null) {
+ checkMaxLimitReached(binding.etContent, text, CONTENT_MAX_LENGTH, R.string.image_and_text_content_limit)
+ }
+ }
+
+ binding.tvBbsName.setOnClickListener {
+ showSelectBbsDialog()
+ }
+
+ binding.tvSubSection.setOnClickListener {
+ showSelectSubSectionDialog()
+ }
+
+ binding.vClearBbs.setOnClickListener {
+ if (viewModel.isCreating) {
+ viewModel.changeLinkBbs(null)
+ } else {
+ viewModel.changeLinkSection(null)
+ toast(R.string.cannot_be_modified_after_publishing)
+ }
+
+ }
+
+ binding.tvDraftBox.setOnClickListener {
+ activityViewModel.navigateToDraftBox()
+ }
+
+ binding.tvPublish.setOnClickListener {
+ publish()
+ }
+
+ activityViewModel.localImages.observe(viewLifecycleOwner) {
+ adapter.submitList(it)
+ }
+
+ activityViewModel.showDragTipsAction.observe(viewLifecycleOwner, EventObserver {
+ // 倒计时开始,5s 后消失
+ binding.gDragTips.visibleIf(true)
+ handler.removeCallbacksAndMessages(null)
+ handler.postDelayed({
+ binding.gDragTips.goneIf(true)
+ }, DRAG_TIPS_SHOW_DURATION)
+
+ })
+
+ with(viewModel) {
+
+ chooseImageAction.observe(viewLifecycleOwner, EventObserver {
+ activityViewModel.chooseImage()
+ })
+
+ removeImageAction.observe(viewLifecycleOwner, EventObserver {
+ activityViewModel.removeImage(it)
+ })
+
+ linkBbs.observe(viewLifecycleOwner) {
+ updateLinkBbs(it)
+ }
+
+ draftCreated.observe(viewLifecycleOwner, EventObserver {
+ if (savingDraftLoading.isShowing) {
+ savingDraftLoading.dismiss()
+ }
+ val toastText = if (it) {
+ R.string.save_draft_successfully.toResString()
+ } else {
+ R.string.save_draft_failure.toResString()
+ }
+ ToastUtils.showToast(toastText)
+ if (it) {
+ activity?.finish()
+ }
+ })
+
+ imageTextPublished.observe(viewLifecycleOwner, EventObserver {
+ if (publishingLoading.isShowing) {
+ publishingLoading.dismiss()
+ }
+ val toastText = if (it) {
+ // 发布成功
+ R.string.publish_image_article_successfully.toResString()
+ } else {
+ // 发布失败
+ R.string.publish_image_article_failure.toResString()
+ }
+ ToastUtils.showToast(toastText)
+ if (it) {
+ activity?.finish()
+ }
+ })
+
+ editImageArticle.observe(viewLifecycleOwner, EventObserver {
+ fillUiByDraft(it)
+ })
+ }
+ }
+
+ private fun fillUiByDraft(imageArticle: ImageArticleEntity) {
+ binding.etTitle.setText(imageArticle.title)
+ binding.etContent.setText(imageArticle.content)
+ }
+
+ /**
+ * 设置内容输入框的高度
+ */
+ private fun setContentHeight(keyboardHeight: Int) {
+ val rootHeight = binding.root.height
+ val remainHeight =
+ if (keyboardHeight > 0) {
+ rootHeight - 116F.dip2px() - 50F.dip2px() - 50F.dip2px() - keyboardHeight
+ } else {
+ rootHeight - 116F.dip2px() - 50F.dip2px() - 60F.dip2px() - 50F.dip2px()
+ }
+
+ val contentHeight = if (remainHeight > 360F.dip2px()) {
+ 360F.dip2px()
+ } else {
+ remainHeight
+ }
+ binding.etContent.updateLayoutParams {
+ height = contentHeight
+ }
+ }
+
+ private fun createDraft() {
+ if (!savingDraftLoading.isShowing) {
+ savingDraftLoading.show()
+ }
+ val title = binding.etTitle.text?.toString() ?: ""
+ val content = binding.etContent.text?.toString() ?: ""
+ val images = activityViewModel.localImages.value ?: emptyList()
+ viewModel.createOrEditDraft(title, content, images)
+ }
+
+ private fun publish() {
+ if (!publishingLoading.isShowing) {
+ publishingLoading.show()
+ }
+ val title = binding.etTitle.text?.toString() ?: ""
+ val content = binding.etContent.text?.toString() ?: ""
+ val images = activityViewModel.localImages.value ?: emptyList()
+ viewModel.publishImageText(title, content, images)
+ }
+
+ private fun updateLinkBbs(linkBbs: PublishImageArticleViewModel.LinkBbs) {
+ if (linkBbs.communityEntity == null) {
+ // 还没有关联论坛
+ binding.tvSelectBbs.visibleIf(true)
+ binding.gBbsSelected.visibleIf(false)
+ } else {
+ // 已关联论坛
+ binding.tvSelectBbs.visibleIf(false)
+ binding.gBbsSelected.visibleIf(true)
+ binding.ivBbsIcon.displayGameIcon(linkBbs.communityEntity.icon, null, null)
+ binding.tvBbsName.text = linkBbs.communityEntity.name
+ if (linkBbs.selectedSection == null) {
+ // 还没有选择子模块
+
+ if (linkBbs.hasSection) {
+ // 有子模块
+ binding.tvSubSection.visibleIf(true)
+ binding.ivBbsArrRight.visibleIf(true)
+ binding.tvSubSection.text = ""
+ } else {
+ // 没有子模块
+ binding.tvSubSection.visibleIf(false)
+ binding.ivBbsArrRight.visibleIf(false)
+ }
+ } else {
+ binding.tvSubSection.text = linkBbs.selectedSection.name
+ }
+ }
+ }
+
+ private fun checkMaxLimitReached(editText: EditText, text: Editable, maxLength: Int, toastResId: Int) {
+ if (text.length > maxLength) {
+ editText.setText(text.subSequence(0, TITLE_MAX_LENGTH))
+ editText.setSelection(TITLE_MAX_LENGTH) // 将光标移动到文本末尾
+ ToastUtils.showToast(toastResId.toResString())
+ }
+ }
+
+ private fun onKeyboardChanged() {
+ val rect = Rect()
+ rootView?.let {
+ it.getWindowVisibleDisplayFrame(rect)
+ val screenHeight = it.height
+ val keyboardHeight = screenHeight - rect.bottom
+ if (keyboardHeight > screenHeight * 0.15) {
+ // 键盘弹起
+ if (!isKeyBoardShow) {
+ isKeyBoardShow = true
+
+ // 当软键盘弹起时,必须要保证EditText的光标和内容不被挡住
+ setContentHeight(keyboardHeight)
+ }
+
+ } else {
+ // 键盘隐藏
+ if (isKeyBoardShow) {
+ isKeyBoardShow = false
+ setContentHeight(0)
+ }
+
+ }
+ }
+
+ }
+
+ private fun showSelectBbsDialog() {
+ if (viewModel.isCreating) {
+ val delayTime = if (isKeyBoardShow) {
+ Util_System_Keyboard.hideSoftKeyboard(requireActivity())
+ 200L
+ } else 0L
+ AppExecutor.uiExecutor.executeWithDelay({
+ chooseForumLauncher.launch("社区")
+ }, delayTime)
+ } else {
+ toast(R.string.cannot_be_modified_after_publishing)
+ }
+ }
+
+ private fun showSelectSubSectionDialog() {
+ val delayTime = if (isKeyBoardShow) {
+ Util_System_Keyboard.hideSoftKeyboard(requireActivity())
+ 200L
+ } else 0L
+ AppExecutor.uiExecutor.executeWithDelay({
+ ChooseSectionDialogFragment.show(
+ requireActivity() as AppCompatActivity,
+ viewModel.linkBbs.value?.communityEntity?.id ?: "",
+ viewModel.linkBbs.value?.selectedSection?.id ?: "",
+ viewModel.isModerator,
+ tag ?: ""
+ )
+ }, delayTime)
+ }
+
+
+ override fun onResume() {
+ super.onResume()
+ requireActivity().findViewById(android.R.id.content)?.viewTreeObserver?.addOnGlobalLayoutListener(
+ onGlobalLayoutListener
+ )
+ }
+
+ override fun onPause() {
+ super.onPause()
+ requireActivity().findViewById(android.R.id.content)?.viewTreeObserver?.removeOnGlobalLayoutListener(
+ onGlobalLayoutListener
+ )
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ handler.removeCallbacksAndMessages(null)
+ }
+
+ override fun onHandleBackPressed(): Boolean {
+ showSaveDraftDialog()
+ return true
+ }
+
+ private fun showSaveDraftDialog() {
+ DialogHelper.showDialog(
+ requireContext(),
+ R.string.miui_optimization_warning_dialog_title.toResString(),
+ R.string.save_to_draft_box_and_exit.toResString(),
+ confirmText = R.string.save_and_exit.toResString(),
+ cancelText = R.string.do_not_save.toResString(),
+ confirmClickCallback = {
+ createDraft()
+ },
+ cancelClickCallback = {
+ activity?.finish()
+ },
+ extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
+
+ )
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ if (requestCode == ChooseSectionDialogFragment.REQUEST_CODE) {
+ val section = data?.getParcelableExtra(EntranceConsts.KEY_DATA)
+ if (section != null) {
+ viewModel.changeLinkSection(section)
+ }
+ }
+ }
+
+ companion object {
+
+ private const val TITLE_MAX_LENGTH = 20
+ private const val CONTENT_MAX_LENGTH = 1000
+ private const val DRAG_TIPS_SHOW_DURATION = 5000L
+
+ fun newInstance() = PublishImageArticleFragment()
+ }
+
+ private class MyItemTouchCallback(
+ private val viewModel: PublishImageArticleActivityViewModel
+ ) : ItemTouchHelper.Callback() {
+ override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
+ return makeMovementFlags(ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT, 1)
+ }
+
+ override fun onMove(
+ recyclerView: RecyclerView,
+ viewHolder: RecyclerView.ViewHolder,
+ target: RecyclerView.ViewHolder
+ ): Boolean {
+ val adapter = recyclerView.adapter as? ImageAndTextAdapter
+ if (adapter != null) {
+ val position = viewHolder.bindingAdapterPosition
+ val targetPosition = target.bindingAdapterPosition
+ val targetItem = adapter.getItem(targetPosition)
+ if (targetItem.id != ImageAndTextAdapter.INVALID_ID) {
+ viewModel.swap(position, targetPosition)
+ return true
+ }
+
+ }
+ return false
+ }
+
+ override fun isLongPressDragEnabled(): Boolean = true
+
+ override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
+
+ override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
+ super.onSelectedChanged(viewHolder, actionState)
+ if (actionState == ACTION_STATE_IDLE) {
+ // 拖拽完毕,刷新数据:将第一位的图片设为封面
+ viewModel.refreshImages()
+ }
+ }
+ }
+
+ class MyItemDecoration : RecyclerView.ItemDecoration() {
+
+ override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
+ outRect.left = if (parent.getChildAdapterPosition(view) == 0) {
+ 0
+ } else {
+ 4F.dip2px()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleCommentViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleCommentViewModel.kt
new file mode 100644
index 0000000000..fdf2fea5d6
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleCommentViewModel.kt
@@ -0,0 +1,116 @@
+package com.gh.gamecenter.forum.home.recommend.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.gh.gamecenter.common.baselist.LoadStatus
+import com.gh.gamecenter.common.baselist.LoadType
+import com.gh.gamecenter.common.baselist.PageLoader
+import com.gh.gamecenter.common.utils.toArrayList
+import com.gh.gamecenter.common.utils.toJson
+import com.gh.gamecenter.core.utils.GsonUtils
+import com.gh.gamecenter.feature.entity.CommentEntity
+import com.gh.gamecenter.feature.entity.MeEntity
+import com.gh.gamecenter.forum.home.recommend.ImageArticleRepository
+import com.gh.gamecenter.qa.article.detail.CommentItemData
+import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel
+import com.gh.gamecenter.retrofit.RetrofitManager
+import com.google.gson.reflect.TypeToken
+import io.reactivex.Observable
+import io.reactivex.disposables.CompositeDisposable
+
+class ImageArticleCommentViewModel(
+ application: Application,
+ imageArticleId: String
+) :
+ BaseCommentViewModel(application, "", "", "", "", "", "", imageArticleId) {
+
+ private val repository = ImageArticleRepository.newInstance()
+
+ override fun provideDataObservable(page: Int): Observable> {
+ val map = hashMapOf()
+ if (!isHandleTopComment && topCommentId.isNotEmpty()) {
+ map["top_comment_id"] = topCommentId
+ }
+ return RetrofitManager.getInstance().api.getImageArticleComments(
+ imageArticleId,
+ currentSortType.value,
+ page,
+ map
+ ).map {
+ mTotal = it.headers().get("total")?.toInt() ?: 0
+ val type = object : TypeToken>() {}.type
+ GsonUtils.gson.fromJson(it.body()?.toJson() ?: "", type)
+ }
+ }
+
+ override fun mergeResultLiveData() {
+ mResultLiveData.addSource(mListLiveData) {
+ mergeListData(mListLiveData.value)
+ }
+ }
+
+ override fun mergeListData(commentList: List?, hasFilter: Boolean) {
+ val mergedList = arrayListOf().apply {
+ if (commentList.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_EMPTY) {
+ add(CommentItemData(errorEmpty = true))
+ } else if (commentList.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_FAILED) {
+ add(CommentItemData(errorConnection = true))
+ } else {
+ commentList?.forEachIndexed { index, entity ->
+ // 没有 me 会导致不能跨页面更新点赞
+ if (entity.me == null) {
+ entity.me = MeEntity()
+ }
+ handleTopComment(index, entity)
+ add(CommentItemData(commentNormal = entity))
+ }
+ add(CommentItemData(footer = true))
+ }
+ }
+ mResultLiveData.postValue(mergedList)
+ }
+
+ fun insertNewestComment(newComment: CommentEntity) {
+ val data = (mResultLiveData.value ?: emptyList()).toArrayList()
+ val count = data.count { it.commentNormal != null }
+ if (count == 0) {
+ load(LoadType.REFRESH)
+ return
+ }
+ when (currentSortType) {
+ SortType.LATEST -> {
+ val index = data.indexOfFirst { it.commentNormal != null }
+ if (index >= 0) {
+ mTotal++
+ data.add(index, CommentItemData(commentNormal = newComment))
+ mResultLiveData.postValue(data)
+ } else {
+ load(LoadType.REFRESH)
+ }
+ }
+
+ SortType.OLDEST -> {
+ //根据total判断是否插入到最后
+ if (count >= mTotal) {
+ mTotal++
+ val index = data.indexOfLast { it.commentNormal != null }
+ data.add(index + 1, CommentItemData(commentNormal = newComment))
+ mResultLiveData.postValue(data)
+ } else {
+ load(LoadType.REFRESH)
+ }
+ }
+ }
+ }
+
+ class Factory(
+ private val application: Application,
+ private val imageArticleId: String
+ ) : ViewModelProvider.NewInstanceFactory() {
+
+ override fun create(modelClass: Class): T {
+ return ImageArticleCommentViewModel(application, imageArticleId) as T
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleDetailViewModel.kt
new file mode 100644
index 0000000000..de6a88de0d
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleDetailViewModel.kt
@@ -0,0 +1,293 @@
+package com.gh.gamecenter.forum.home.recommend.viewmodel
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Transformations
+import androidx.lifecycle.ViewModel
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.baselist.LoadStatus
+import com.gh.gamecenter.common.retrofit.BiResponse
+import com.gh.gamecenter.common.retrofit.Response
+import com.gh.gamecenter.common.utils.observableToMain
+import com.gh.gamecenter.common.utils.singleToMain
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.eventbus.EBImageArticleChanged
+import com.gh.gamecenter.feature.entity.CommentEntity
+import com.gh.gamecenter.feature.entity.Permissions
+import com.gh.gamecenter.forum.home.recommend.ImageArticleRepository
+import com.gh.gamecenter.forum.home.recommend.ImageArticleUseCase
+import com.gh.gamecenter.livedata.Event
+import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel.SortType
+import io.reactivex.CompletableObserver
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+import okhttp3.ResponseBody
+
+class ImageArticleDetailViewModel : ViewModel() {
+
+ private val repository = ImageArticleRepository.newInstance()
+
+ private val compositeDisposable = CompositeDisposable()
+
+ var imageArticleId: String = ""
+
+ private val useCase = ImageArticleUseCase(repository, compositeDisposable)
+
+ private val _imageArticleDetailEntity = MutableLiveData()
+ val imageArticleDetailEntity: LiveData = _imageArticleDetailEntity
+
+ val isUserFollowed: LiveData =
+ Transformations.distinctUntilChanged(Transformations.map(imageArticleDetailEntity) {
+ it.me.isFollower
+ })
+
+ fun followUser() {
+ val isFollowed = isUserFollowed.value ?: false
+ val userId = imageArticleDetailEntity.value?.user?.id ?: ""
+ repository.followUser(!isFollowed, userId)
+ .compose(observableToMain())
+ .subscribe(object : Response() {
+ override fun onSubscribe(d: Disposable) {
+ compositeDisposable.add(d)
+ }
+
+ override fun onResponse(response: ResponseBody?) {
+ updateImageArticle {
+ it.me.isFollower = !isFollowed
+ }
+ }
+ })
+ }
+
+ val commentCount: LiveData =
+ Transformations.distinctUntilChanged(Transformations.map(imageArticleDetailEntity) {
+ it.count.comment
+ })
+
+ fun updateComment(commentId: String, commentCount: Int) {
+ updateImageArticle {
+ it.count.comment = commentCount
+ }
+ ImageArticleUseCase.notifyCommentChanged(imageArticleId, commentCount)
+ if (commentId.isNotEmpty()) {
+ insertNewestComment(commentId)
+ }
+ }
+
+ private val _insertNewestCommentAction = MutableLiveData>()
+ val insertNewestCommentAction: LiveData> = _insertNewestCommentAction
+ private fun insertNewestComment(commentId: String) {
+ // 将刚刚评论的内容插入到评论列表中
+ repository.getSingleCommentById(commentId)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: CommentEntity) {
+ _insertNewestCommentAction.value = Event(data)
+ }
+ }).let(compositeDisposable::add)
+ }
+
+ val vote: LiveData> =
+ Transformations.distinctUntilChanged(Transformations.map(imageArticleDetailEntity) {
+ it.me.isCommunityArticleVote to it.count.vote
+ })
+
+ fun vote() {
+ val isVoted = vote.value?.first ?: false
+ useCase.voteImageArticle(imageArticleId, !isVoted)
+ }
+
+ private val _pageStatus = MutableLiveData()
+ val pageStatus: LiveData = _pageStatus
+
+ fun loadImageArticleDetail(imageArticleId: String) {
+ _pageStatus.value = LoadStatus.INIT_LOADING
+ this.imageArticleId = imageArticleId
+ repository.loadImageArticleDetail(imageArticleId)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: ImageArticleEntity) {
+ _pageStatus.value = LoadStatus.INIT_LOADED
+ setImageArticle(data)
+ }
+
+ override fun onFailure(exception: Exception) {
+ _pageStatus.value = LoadStatus.INIT_FAILED
+ }
+
+ }).let(compositeDisposable::add)
+ }
+
+ private val _sortType = MutableLiveData>()
+ val sortType: LiveData> = _sortType
+ fun changeSort(newSort: SortType) {
+ _sortType.value = Event(newSort)
+ }
+
+ val isBbsFollowed: LiveData =
+ Transformations.distinctUntilChanged(Transformations.map(imageArticleDetailEntity) {
+ it.community?.isFollowed ?: false
+ })
+
+ fun followBbs(communityId: String) {
+ val isFollowed = isBbsFollowed.value ?: false
+ repository.followBbs(communityId, !isFollowed)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: ResponseBody) {
+ updateImageArticle {
+ it.community?.isFollowed = !isFollowed
+ }
+ }
+
+ }).let(compositeDisposable::add)
+ }
+
+ val isFavorite: LiveData =
+ Transformations.distinctUntilChanged(Transformations.map(imageArticleDetailEntity) {
+ it.me.isFavorite
+ })
+
+ fun collect() {
+ val imageArticleId = imageArticleId
+ val isCollected = isFavorite.value ?: false
+ repository.collect(imageArticleId, !isCollected)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : CompletableObserver {
+ override fun onSubscribe(d: Disposable) {
+ compositeDisposable.add(d)
+ }
+
+ override fun onComplete() {
+ updateImageArticle {
+ it.me.isFavorite = !isCollected
+ }
+ }
+
+ override fun onError(e: Throwable) = Unit
+
+ })
+ }
+
+ fun setImageArticle(newData: ImageArticleEntity) {
+ _imageArticleDetailEntity.value = newData
+ }
+
+ fun updateImageArticle(block: (ImageArticleEntity) -> Unit) {
+ val data = imageArticleDetailEntity.value ?: return
+ block(data)
+ _imageArticleDetailEntity.value = data
+ }
+
+ private val _applyHighlightAction = MutableLiveData>()
+ val applyHighlightAction: LiveData> = _applyHighlightAction
+ fun applyHighlightForImageArticle() {
+ repository.applyHighlightForImageArticle(imageArticleId)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : CompletableObserver {
+ override fun onSubscribe(d: Disposable) {
+ compositeDisposable.add(d)
+ }
+
+ override fun onComplete() {
+ imageArticleDetailEntity.value?.choicenessStatus = "apply"
+ _applyHighlightAction.value = Event(true)
+ }
+
+ override fun onError(e: Throwable) {
+ _applyHighlightAction.value = Event(false)
+ }
+ })
+ }
+
+ private val _addHighlightAction = MutableLiveData>()
+ val addHighlightAction: LiveData> = _addHighlightAction
+ fun addHighlight(isAdd: Boolean) {
+ repository.addHighlight(isAdd, imageArticleId)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: ResponseBody) {
+ val imageArticle = imageArticleDetailEntity.value ?: return
+ if (isAdd) {
+ if (imageArticle.me.moderatorPermissions.choicenessImageArticle == Permissions.REPORTER) {
+ _addHighlightAction.value = Event(R.string.apply_successfully)
+ updateImageArticle {
+ it.choicenessStatus = "apply"
+ }
+ } else {
+ _addHighlightAction.value = Event(R.string.operation_successful)
+ updateImageArticle {
+ it.choicenessStatus = "pass"
+ }
+
+ }
+ } else {
+ if (imageArticle.me.moderatorPermissions.cancelChoicenessImageArticle == Permissions.REPORTER) {
+ _addHighlightAction.value = Event(R.string.apply_successfully)
+ } else {
+ _addHighlightAction.value = Event(R.string.operation_successful)
+ updateImageArticle {
+ it.choicenessStatus = "cancel"
+ }
+ }
+ }
+ }
+
+ override fun onFailure(exception: Exception) {
+ _addHighlightAction.value = Event(R.string.permission_error)
+ }
+ }).let(compositeDisposable::add)
+ }
+
+ private val _deleteOrHideImageArticleSuccessfully = MutableLiveData>>()
+ val deleteOrHideImageArticleSuccessfully: LiveData>> =
+ _deleteOrHideImageArticleSuccessfully
+
+ fun deleteOrHideImageArticle() {
+ repository.deleteOrHideImageArticle(imageArticleId)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: ResponseBody) {
+ val imageArticle = imageArticleDetailEntity.value ?: return
+ val toastResId = if (imageArticle.me.isModerator) {
+ if (imageArticle.me.moderatorPermissions.hideImageArticle == Permissions.REPORTER) {
+ R.string.apply_successfully
+ } else {
+ R.string.hidden
+ }
+ } else {
+ R.string.deleted
+ }
+ ImageArticleUseCase.notifyDataDeleted(imageArticleId)
+ _deleteOrHideImageArticleSuccessfully.value = Event(true to toastResId)
+
+ }
+
+ override fun onFailure(exception: Exception) {
+ super.onFailure(exception)
+ _deleteOrHideImageArticleSuccessfully.value = Event(false to R.string.permission_error_and_retry)
+ }
+
+ }).let(compositeDisposable::add)
+ }
+
+ fun imageArticleChanged(changed: EBImageArticleChanged) {
+ when {
+ changed is EBImageArticleChanged.VoteChanged -> {
+ updateImageArticle {
+ it.me.isCommunityArticleVote = changed.isVoted
+ it.count.vote = changed.count
+ }
+ }
+
+ changed is EBImageArticleChanged.DataChanged -> {
+ setImageArticle(changed.data)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleDraftViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleDraftViewModel.kt
new file mode 100644
index 0000000000..934d6e1677
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleDraftViewModel.kt
@@ -0,0 +1,101 @@
+package com.gh.gamecenter.forum.home.recommend.viewmodel
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.gh.gamecenter.common.baselist.PageLoader
+import com.gh.gamecenter.common.retrofit.BiResponse
+import com.gh.gamecenter.common.utils.singleToMain
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.forum.home.recommend.ImageArticleRepository
+import com.gh.gamecenter.livedata.Event
+import io.reactivex.CompletableObserver
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+
+class ImageArticleDraftViewModel : ViewModel() {
+
+ private val repository = ImageArticleRepository.newInstance()
+
+ private val composeDisposable = CompositeDisposable()
+
+ private val _pageState = MutableLiveData()
+ val pageState: LiveData = _pageState
+
+ private val _drafts = MutableLiveData>()
+ val drafts: LiveData> = _drafts
+
+ fun loadDraft(isPullToRefresh: Boolean) {
+ _pageState.value = if (isPullToRefresh) {
+ PageLoader.PageState.PagePullToRefreshLoading
+ } else {
+ PageLoader.PageState.PageInitLoading
+ }
+
+ repository.loadDraft()
+ .compose(singleToMain())
+ .subscribe(object : BiResponse>() {
+ override fun onSuccess(data: List) {
+ _pageState.value = if (data.isEmpty()) {
+ PageLoader.PageState.PageNoData
+ } else {
+ PageLoader.PageState.PageLoadCompleted
+ }
+ _drafts.value = data
+ }
+
+ override fun onFailure(exception: Exception) {
+ super.onFailure(exception)
+ _pageState.value = if (isPullToRefresh) {
+ PageLoader.PageState.PagePullToRefreshFailure
+ } else {
+ PageLoader.PageState.PageInitFailure
+ }
+
+ }
+
+ }).let(composeDisposable::add)
+ }
+
+ private val _showDeleteDraftDialogAction = MutableLiveData>()
+ val showDeleteDraftDialogAction: LiveData> = _showDeleteDraftDialogAction
+ fun showDeleteDraftDialog(draftId: String) {
+ _showDeleteDraftDialogAction.value = Event(draftId)
+ }
+
+ fun deleteDraft(draftId: String) {
+ repository.deleteDraft(draftId)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : CompletableObserver {
+ override fun onSubscribe(d: Disposable) {
+ composeDisposable.add(d)
+ }
+
+ override fun onComplete() {
+ val oldData = drafts.value ?: emptyList()
+ val newList = oldData.toMutableList()
+ newList.removeAll { it.id == draftId }
+ if (newList.isEmpty()) {
+ _pageState.value = PageLoader.PageState.PageNoData
+ } else {
+ _drafts.value = newList
+ }
+ }
+
+ override fun onError(e: Throwable) = Unit
+ })
+ }
+
+ private val _publishImageArticleDestination = MutableLiveData>()
+ val publishImageArticleDestination: LiveData> = _publishImageArticleDestination
+ fun editDraft(draftEntity: ImageArticleEntity) {
+ _publishImageArticleDestination.value = Event(draftEntity)
+ }
+
+ fun updateDrafts(draftList: MutableList) {
+ _drafts.value = draftList
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleHomeViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleHomeViewModel.kt
new file mode 100644
index 0000000000..7fff7c3511
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/ImageArticleHomeViewModel.kt
@@ -0,0 +1,78 @@
+package com.gh.gamecenter.forum.home.recommend.viewmodel
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.gh.gamecenter.common.baselist.PageLoader
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.eventbus.EBImageArticleChanged
+import com.gh.gamecenter.forum.home.recommend.ImageArticleRepository
+import com.gh.gamecenter.forum.home.recommend.ImageArticleUseCase
+import com.gh.gamecenter.livedata.Event
+import io.reactivex.disposables.CompositeDisposable
+
+class ImageArticleHomeViewModel : ViewModel() {
+
+ private val repository = ImageArticleRepository.newInstance()
+
+ private val compositeDisposable = CompositeDisposable()
+
+ val useCase = ImageArticleUseCase(repository, compositeDisposable)
+
+ val pageLoader = PageLoader(compositeDisposable = compositeDisposable) { pageNo, _ ->
+ repository.loadRecommends(pageNo)
+ }
+
+ fun initLoad() {
+ pageLoader.loadInit()
+ }
+
+ private val _imageDetailDestination = MutableLiveData>()
+ val imageDetailDestination: LiveData> = _imageDetailDestination
+ fun navigateToImageArticleDetailPage(imageArticleId: String) {
+ _imageDetailDestination.value = Event(imageArticleId)
+ }
+
+ fun loadMore() {
+ pageLoader.loadMore()
+ }
+
+ override fun onCleared() {
+ compositeDisposable.clear()
+ }
+
+ fun voteChanged(changed: EBImageArticleChanged.VoteChanged) {
+ val oldData = pageLoader.dataList.value ?: return
+ val newData = oldData.toMutableList()
+ val position = newData.indexOfFirst { it.id == changed.id }
+ if (position != -1) {
+ val oldItem = newData[position]
+ val oldCount = oldItem.count.vote
+ val newCount = if (changed.isVoted) {
+ oldCount + 1
+ } else {
+ oldCount - 1
+ }
+ val newItem = oldItem.copy(
+ _me = oldItem.me.copy(isCommunityArticleVote = changed.isVoted),
+ _count = oldItem.count.copy(vote = newCount)
+ )
+ newData[position] = newItem
+ pageLoader.updateData(newData)
+ }
+ }
+
+ fun itemChanged(changed: EBImageArticleChanged.DataChanged) {
+ val oldData = pageLoader.dataList.value ?: return
+ val newData = oldData.toMutableList()
+ val position = newData.indexOfFirst { it.id == changed.data.id }
+ if (position != -1) {
+ newData[position] = changed.data
+ pageLoader.updateData(newData)
+ }
+
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/MyImageArticleListViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/MyImageArticleListViewModel.kt
new file mode 100644
index 0000000000..edc77b0807
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/MyImageArticleListViewModel.kt
@@ -0,0 +1,81 @@
+package com.gh.gamecenter.forum.home.recommend.viewmodel
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.gh.gamecenter.common.baselist.PageLoader
+import com.gh.gamecenter.entity.PersonalHistoryEntity.Count
+import com.gh.gamecenter.eventbus.EBImageArticleChanged
+import com.gh.gamecenter.forum.home.recommend.ImageArticleRepository
+import com.gh.gamecenter.forum.home.recommend.ImageArticleUseCase
+import com.gh.gamecenter.livedata.Event
+import io.reactivex.disposables.CompositeDisposable
+
+class MyImageArticleListViewModel : ViewModel() {
+
+ private val repository = ImageArticleRepository.newInstance()
+
+ private val compositeDisposable = CompositeDisposable()
+
+ val useCase = ImageArticleUseCase(repository, compositeDisposable)
+
+ val pageLoader = PageLoader(compositeDisposable = compositeDisposable) { pageSize, _ ->
+ repository.loadMyImageArticleList(pageSize)
+ .singleOrError()
+ }
+
+ fun initLoad() {
+ pageLoader.loadInit()
+ }
+
+ private val _imageArticleDetailDestination = MutableLiveData>()
+ val imageArticleDetailDestination: LiveData> = _imageArticleDetailDestination
+ fun navigateToImageArticleDetailPage(imageArticleId: String) {
+ _imageArticleDetailDestination.value = Event(imageArticleId)
+ }
+
+ fun loadMore() {
+ pageLoader.loadMore()
+ }
+
+ override fun onCleared() {
+ compositeDisposable.clear()
+ }
+
+ fun voteChanged(changed: EBImageArticleChanged.VoteChanged) {
+ val oldData = pageLoader.dataList.value ?: return
+ val newData = oldData.toMutableList()
+ val position = newData.indexOfFirst { it.id == changed.id }
+ if (position != -1) {
+ val oldItem = newData[position]
+ val oldCount = oldItem.count.vote
+ val newCount = if (changed.isVoted) {
+ oldCount + 1
+ } else {
+ oldCount - 1
+ }
+ val newItem = oldItem.copy(
+ _me = oldItem.me.copy(isCommunityArticleVote = changed.isVoted),
+ _count = Count(
+ comment = oldItem.count.comment,
+ vote = newCount,
+ reply = oldItem.count.reply,
+ )
+ )
+
+ newData[position] = newItem
+ pageLoader.updateData(newData)
+ }
+ }
+
+ fun itemChanged(changed: EBImageArticleChanged.DataChanged) {
+ val oldData = pageLoader.dataList.value ?: return
+ val newData = oldData.toMutableList()
+ val position = newData.indexOfFirst { it.id == changed.data.id }
+ if (position != -1) {
+ newData[position] = changed.data.toPersonHistoryEntity()
+ pageLoader.updateData(newData)
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/PublishImageArticleActivityViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/PublishImageArticleActivityViewModel.kt
new file mode 100644
index 0000000000..37c9f5821e
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/PublishImageArticleActivityViewModel.kt
@@ -0,0 +1,129 @@
+package com.gh.gamecenter.forum.home.recommend.viewmodel
+
+import android.net.Uri
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.gh.gamecenter.common.entity.CommunityEntity
+import com.gh.gamecenter.common.retrofit.BiResponse
+import com.gh.gamecenter.common.utils.singleToMain
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.forum.home.recommend.ImageArticleRepository
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageAndTextAdapter
+import com.gh.gamecenter.livedata.Event
+import io.reactivex.disposables.CompositeDisposable
+import java.util.*
+
+class PublishImageArticleActivityViewModel : ViewModel() {
+
+ private val repository = ImageArticleRepository.newInstance()
+
+ private val compositeDisposable = CompositeDisposable()
+
+ private var imageId: Int = 0
+
+ private val _showDragTipsAction = MutableLiveData>()
+ val showDragTipsAction: LiveData> = _showDragTipsAction
+
+ private val _localImages = MutableLiveData>()
+ val localImages: LiveData> = _localImages
+
+ fun addImages(uris: List) {
+ val oldData = localImages.value?.toMutableList() ?: mutableListOf()
+
+ val newData = (oldData + uris.map {
+ ImageAndTextAdapter.LocalImage(id = imageId++, isCover = false, showClose = true, uri = it)
+ }).toMutableList()
+
+ resetFirstItem(newData)
+
+ repository.isShowDragTips()
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: Boolean) {
+ if (data && newData.size > 1) {
+ repository.saveHasShowDragTips()
+ _showDragTipsAction.value = Event(Unit)
+ }
+ }
+
+ }).let(compositeDisposable::add)
+ }
+
+ private val _chooseImageAction = MutableLiveData>()
+ val chooseImageAction: LiveData> = _chooseImageAction
+ fun chooseImage() {
+ val currentImageSize = localImages.value?.size ?: 0
+ val remainingSize = 9 - currentImageSize
+ _chooseImageAction.value = Event(remainingSize)
+ }
+
+ fun removeImage(position: Int) {
+ val images = localImages.value?.toMutableList() ?: mutableListOf()
+ images.removeAt(position)
+ resetFirstItem(images)
+ }
+
+ private fun resetFirstItem(data: MutableList) {
+ if (data.isEmpty()) {
+ _localImages.value = mutableListOf()
+ } else {
+ // 给第一个设置为封面
+ val firstItem = data[0].copy(isCover = true)
+ data[0] = firstItem
+ _localImages.value = data
+ }
+ }
+
+ fun swap(dataPosition: Int, targetDataPosition: Int) {
+ val oldList = localImages.value ?: arrayListOf()
+ val newList = oldList.toMutableList()
+ Collections.swap(newList, dataPosition, targetDataPosition)
+ _localImages.value = newList
+
+ }
+
+ fun refreshImages() {
+ val oldList = localImages.value ?: arrayListOf()
+ val newList = oldList.toMutableList()
+ for (index in newList.indices) {
+ val item = newList[index]
+ if (index == 0 && !item.isCover) {
+ newList[0] = item.copy(isCover = true)
+ }
+
+ if (index != 0 && item.isCover) {
+ newList[index] = item.copy(isCover = false)
+ }
+ }
+ _localImages.value = newList
+ }
+
+ private val _draftBoxDestination = MutableLiveData>()
+ val draftBoxDestination: LiveData> = _draftBoxDestination
+ fun navigateToDraftBox() {
+ _draftBoxDestination.value = Event(Unit)
+ }
+
+ private val _editImageArticle = MutableLiveData>()
+ val editImageArticle: LiveData> = _editImageArticle
+ fun setEditImageArticle(imageArticleEntity: ImageArticleEntity?) {
+ imageArticleEntity ?: return
+ addImagesFromEdit(imageArticleEntity.images)
+ _editImageArticle.value = Event(imageArticleEntity)
+ }
+
+ private fun addImagesFromEdit(urls: List) {
+ val newImages = urls.map {
+ ImageAndTextAdapter.LocalImage(id = imageId++, isCover = false, showClose = true, url = it)
+ }.toMutableList()
+
+ resetFirstItem(newImages)
+ }
+
+ private val _community = MutableLiveData>()
+ val community: LiveData> = _community
+ fun setCommunity(communityEntity: CommunityEntity) {
+ _community.value = Event(communityEntity)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/PublishImageArticleViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/PublishImageArticleViewModel.kt
new file mode 100644
index 0000000000..37dd2e786f
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/recommend/viewmodel/PublishImageArticleViewModel.kt
@@ -0,0 +1,198 @@
+package com.gh.gamecenter.forum.home.recommend.viewmodel
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.gh.gamecenter.common.entity.CommunityEntity
+import com.gh.gamecenter.common.retrofit.BiResponse
+import com.gh.gamecenter.common.retrofit.Response
+import com.gh.gamecenter.common.utils.observableToMain
+import com.gh.gamecenter.common.utils.singleToMain
+import com.gh.gamecenter.entity.ForumDetailEntity
+import com.gh.gamecenter.entity.ImageArticleEntity
+import com.gh.gamecenter.entity.PublishImageTextRequest
+import com.gh.gamecenter.forum.home.recommend.ImageArticleRepository
+import com.gh.gamecenter.forum.home.recommend.ImageArticleUseCase
+import com.gh.gamecenter.forum.home.recommend.adapter.ImageAndTextAdapter
+import com.gh.gamecenter.livedata.Event
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.disposables.Disposable
+import okhttp3.ResponseBody
+
+class PublishImageArticleViewModel : ViewModel() {
+
+ private val repository = ImageArticleRepository.newInstance()
+
+ private val compositeDisposable = CompositeDisposable()
+
+ var isModerator: Boolean = false
+
+ private var imageArticle: ImageArticleEntity? = null
+
+ val isCreating: Boolean
+ get() = imageArticle == null || imageArticle!!.isDraft
+
+ private val _chooseImageAction = MutableLiveData>()
+ val chooseImageAction: LiveData> = _chooseImageAction
+ fun chooseImage() {
+ _chooseImageAction.value = Event(Unit)
+ }
+
+ private val _removeImageAction = MutableLiveData>()
+ val removeImageAction: LiveData> = _removeImageAction
+ fun removeImage(position: Int) {
+ _removeImageAction.value = Event(position)
+ }
+
+ private val _linkBbs = MutableLiveData()
+ val linkBbs: LiveData = _linkBbs
+ fun changeLinkBbs(newCommunity: CommunityEntity?) {
+ if (newCommunity == null) {
+ _linkBbs.value = LinkBbs(null, false, null)
+ return
+ }
+ val oldCommunity = linkBbs.value?.communityEntity
+ if (newCommunity != oldCommunity) {
+ // 用户重新选择了关联论坛
+ _linkBbs.value = LinkBbs(newCommunity, false, null)
+ getForumSections(newCommunity)
+ getModeratorsInfo(newCommunity.id)
+ }
+ }
+
+ private fun getForumSections(newCommunity: CommunityEntity) {
+ repository.checkHasSections(newCommunity.id)
+ .compose(observableToMain())
+ .subscribe(object : Response() {
+ override fun onSubscribe(d: Disposable) {
+ compositeDisposable.add(d)
+ }
+
+ override fun onResponse(response: Boolean?) {
+ if (response == true) {
+ // 此论坛有子板块,更新关联论坛ui状态
+ _linkBbs.value = LinkBbs(newCommunity, true, null)
+ }
+ }
+
+ })
+ }
+
+ private fun getModeratorsInfo(bbsId: String) {
+ repository.getModeratorsInfo(bbsId)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: Boolean) {
+ isModerator = data
+ }
+
+ }).let(compositeDisposable::add)
+ }
+
+ fun changeLinkSection(section: ForumDetailEntity.Section?) {
+ val oldLink = linkBbs.value
+ if (oldLink != null) {
+ _linkBbs.value = oldLink.copy(selectedSection = section)
+ }
+ }
+
+ private val _draftCreated = MutableLiveData>()
+ val draftCreated: LiveData> = _draftCreated
+ fun createOrEditDraft(title: String, content: String, localImages: List) {
+ val request = createPublishRequest(title, content)
+ repository.createOrEditDraft(request, localImages)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: ResponseBody) {
+ _draftCreated.value = Event(true)
+ }
+
+ override fun onFailure(exception: Exception) {
+ _draftCreated.value = Event(false)
+ }
+
+ }).let(compositeDisposable::add)
+ }
+
+ private val _imageTextPublished = MutableLiveData>()
+ val imageTextPublished: LiveData> = _imageTextPublished
+ fun publishImageText(title: String, content: String, images: List) {
+ val request = createPublishRequest(title, content)
+ if (isCreating) {
+ repository.publishImageText(request, images)
+ } else {
+ repository.editImageArticle(imageArticle!!.id, request, images)
+ }
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: ImageArticleEntity) {
+ if (isCreating) {
+ ImageArticleUseCase.notifyDataCreated(data, request.draftId)
+ } else {
+ ImageArticleUseCase.notifyDataEdited(data)
+ }
+ _imageTextPublished.value = Event(true)
+ }
+
+ override fun onFailure(exception: Exception) {
+ _imageTextPublished.value = Event(false)
+ }
+
+ }).let(compositeDisposable::add)
+ }
+
+ private fun createPublishRequest(title: String, content: String): PublishImageTextRequest {
+ val draftId = if (imageArticle?.isDraft == true) {
+ // 发布或者编辑草稿
+ imageArticle?.id ?: ""
+ } else {
+ ""
+ }
+ val selectedBbs = linkBbs.value
+ val communityId = selectedBbs?.communityEntity?.id ?: ""
+ val communityType = selectedBbs?.communityEntity?.type ?: ""
+ val sectionId = selectedBbs?.selectedSection?.id ?: ""
+ val sectionIds = if (sectionId.isNotEmpty()) listOf(sectionId) else listOf()
+ return PublishImageTextRequest(title, content, communityType, communityId, sectionIds, emptyList(), draftId)
+ }
+
+ private val _editImageArticle = MutableLiveData>()
+ val editImageArticle: LiveData> = _editImageArticle
+ fun setEditImageArticle(imageArticleEntity: ImageArticleEntity?) {
+ imageArticleEntity ?: return
+ imageArticle = imageArticleEntity
+ val community = imageArticleEntity.community?.toCommunityEntity()
+ val section = imageArticleEntity.sections.firstOrNull()
+ val game = imageArticleEntity.community?.game
+ val icon = if (game == null) {
+ community?.icon ?: ""
+ } else {
+ game.rawIcon ?: game.icon
+ }
+ community?.icon = icon
+ when {
+ community != null && section != null -> { // 绑定了论坛和子板块
+ val formSection = ForumDetailEntity.Section(section.id, name = section.name)
+ _linkBbs.value = LinkBbs(community, hasSection = true, formSection)
+ }
+
+ community != null -> { // 只绑定了论坛未绑定子板块
+ changeLinkBbs(community)
+ }
+
+ else -> { // 未绑定论坛
+ changeLinkBbs(null)
+ }
+ }
+
+ changeLinkBbs(community)
+ _editImageArticle.value = Event(imageArticleEntity)
+ }
+
+
+ data class LinkBbs(
+ val communityEntity: CommunityEntity?,
+ val hasSection: Boolean,
+ val selectedSection: ForumDetailEntity.Section? = null
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/mypost/MyPostWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/mypost/MyPostWrapperFragment.kt
index 762389f237..3c57905dd8 100644
--- a/app/src/main/java/com/gh/gamecenter/mypost/MyPostWrapperFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/mypost/MyPostWrapperFragment.kt
@@ -9,6 +9,9 @@ import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout
import com.gh.gamecenter.common.utils.goneIf
+import com.gh.gamecenter.common.utils.toResString
+import com.gh.gamecenter.forum.home.recommend.fragment.ImageArticleHomeFragment
+import com.gh.gamecenter.forum.home.recommend.fragment.MyImageArticleListFragment
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.personalhome.home.UserHistoryViewModel
import com.gh.gamecenter.personalhome.home.game.UserCommentHistoryFragment
@@ -25,6 +28,7 @@ class MyPostWrapperFragment : BaseFragment_TabLayout() {
override fun initTabTitleList(tabTitleList: MutableList) {
tabTitleList.add("评价")
+ tabTitleList.add(R.string.image_article.toResString())
tabTitleList.add(getString(R.string.collection_article))
tabTitleList.add(getString(R.string.ask_search_questions))
tabTitleList.add(getString(R.string.video))
@@ -50,10 +54,11 @@ class MyPostWrapperFragment : BaseFragment_TabLayout() {
menuItem?.run {
if (itemId == R.id.menu_draft && mLastPosition != 0) {
val tabName = when (mLastPosition) {
- 1 -> getString(R.string.collection_article)
- 2 -> getString(R.string.ask_search_questions)
- 3 -> getString(R.string.video)
- 4 -> getString(R.string.answer)
+ 1 -> getString(R.string.image_article)
+ 2 -> getString(R.string.collection_article)
+ 3 -> getString(R.string.ask_search_questions)
+ 4 -> getString(R.string.video)
+ 5 -> getString(R.string.answer)
else -> ""
}
NewFlatLogUtils.logHaloSelfPublishContent(tabName, "草稿箱")
@@ -64,6 +69,7 @@ class MyPostWrapperFragment : BaseFragment_TabLayout() {
override fun initFragmentList(fragments: MutableList) {
fragments.add(UserCommentHistoryFragment.getInstance("我的发布", UserManager.getInstance().userId))
+ fragments.add(MyImageArticleListFragment.newInstance())
fragments.add(MyPostFragment.getInstance(UserHistoryViewModel.TYPE.COMMUNITY_ARTICLE))
fragments.add(MyPostFragment.getInstance(UserHistoryViewModel.TYPE.QUESTION))
fragments.add(MyPostFragment.getInstance(UserHistoryViewModel.TYPE.VIDEO))
@@ -75,10 +81,11 @@ class MyPostWrapperFragment : BaseFragment_TabLayout() {
NewFlatLogUtils.logHaloSelfPublishTab(
when (position) {
0 -> "评价"
- 1 -> getString(R.string.collection_article)
- 2 -> getString(R.string.ask_search_questions)
- 3 -> getString(R.string.video)
- 4 -> getString(R.string.answer)
+ 1 -> "图文"
+ 2 -> getString(R.string.collection_article)
+ 3 -> getString(R.string.ask_search_questions)
+ 4 -> getString(R.string.video)
+ 5 -> getString(R.string.answer)
else -> ""
}
)
diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/PersonalItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/personalhome/PersonalItemViewHolder.kt
index b90bde4be4..67f236ac18 100644
--- a/app/src/main/java/com/gh/gamecenter/personalhome/PersonalItemViewHolder.kt
+++ b/app/src/main/java/com/gh/gamecenter/personalhome/PersonalItemViewHolder.kt
@@ -36,7 +36,18 @@ class PersonalItemViewHolder(
) :
BaseAnswerOrArticleItemViewHolder(binding.root) {
- fun bindPersonalItem(historyEntity: PersonalHistoryEntity, entrance: String, position: Int) {
+ fun bindPersonalItem(
+ historyEntity: PersonalHistoryEntity,
+ entrance: String,
+ position: Int,
+ payloads: List? = null
+ ) {
+ if(!payloads.isNullOrEmpty()){
+ // 局部更新
+ commentCount.isClickable = true
+ bindCommendAndVote(historyEntity, entrance)
+ return
+ }
commentCount.isClickable = true
bindCommendAndVote(historyEntity, entrance)
diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryAdapter.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryAdapter.kt
index 260724596e..70113158fa 100644
--- a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryAdapter.kt
+++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryAdapter.kt
@@ -1,42 +1,34 @@
package com.gh.gamecenter.personalhome.home
-import android.app.Activity
import android.content.Context
-import android.text.SpannableStringBuilder
import android.util.SparseBooleanArray
+import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
-import com.facebook.drawee.view.SimpleDraweeView
-import com.gh.common.util.DialogUtils
-import com.gh.common.util.DirectUtils
-import com.gh.common.util.NewsUtils
-import com.gh.common.view.ImageContainerView
-import com.gh.common.view.ImageContainerView.Companion.toImageContainerData
-import com.gh.gamecenter.ImageViewerActivity
+import androidx.recyclerview.widget.StaggeredGridLayoutManager
+import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.PersonalHomeRatingViewHolder
import com.gh.gamecenter.common.baselist.ListAdapter
-import com.gh.gamecenter.common.callback.ConfirmListener
+import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.constant.ItemViewType
-import com.gh.gamecenter.common.utils.*
+import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.common.databinding.RefreshFooterviewBinding
+import com.gh.gamecenter.common.utils.TextHelper
+import com.gh.gamecenter.common.utils.ifLogin
+import com.gh.gamecenter.common.utils.setTextWithHighlightedTextWrappedInsideWrapper
+import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.viewholder.FooterViewHolder
import com.gh.gamecenter.core.utils.NumberUtils
-import com.gh.gamecenter.core.utils.SpanBuilder
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
import com.gh.gamecenter.databinding.PersonalHomeRatingBinding
import com.gh.gamecenter.entity.PersonalHistoryEntity
-import com.gh.gamecenter.feature.entity.AnswerEntity
-import com.gh.gamecenter.feature.entity.ForumVideoEntity
-import com.gh.gamecenter.forum.home.AnswerArticleVideoViewEventHelper
-import com.gh.gamecenter.forum.home.ArticleItemVideoView
-import com.gh.gamecenter.forum.home.ArticleItemVideoView.Companion.toArticleVideoData
+import com.gh.gamecenter.forum.home.follow.viewholder.FollowFooterViewHolder
+import com.gh.gamecenter.forum.home.recommend.ImageArticleDetailActivity
+import com.gh.gamecenter.forum.home.recommend.adapter.MyImageArticleAdapter.MyImageArticleViewHolder
import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity
import com.gh.gamecenter.personalhome.PersonalItemViewHolder
-import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
-import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
-import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
-import com.shuyu.gsyvideoplayer.utils.OrientationUtils
import java.util.regex.Pattern
class UserHistoryAdapter(
@@ -48,10 +40,15 @@ class UserHistoryAdapter(
private var mExpandSparseBooleanArray = SparseBooleanArray()
+ override fun setListData(updateData: MutableList?) {
+ super.setListData(updateData)
+ }
+
override fun getItemViewType(position: Int): Int {
return when {
position == itemCount - 1 -> ItemViewType.ITEM_FOOTER
mEntityList[position].type == "game_comment" -> ItemViewType.RATING_ITEM
+ mListViewModel.type == UserHistoryViewModel.TYPE.IMAGE_ARTICLE -> ITEM_TYPE_IMAGE_ARTICLE
else -> ItemViewType.ITEM_BODY
}
}
@@ -61,6 +58,11 @@ class UserHistoryAdapter(
return when (viewType) {
ItemViewType.ITEM_FOOTER -> {
view = mLayoutInflater.inflate(com.gh.gamecenter.common.R.layout.refresh_footerview, parent, false)
+ val layoutParams = view.layoutParams
+ if (layoutParams is StaggeredGridLayoutManager.LayoutParams) {
+ layoutParams.isFullSpan = true
+ view.layoutParams = layoutParams
+ }
FooterViewHolder(view)
}
@@ -69,6 +71,22 @@ class UserHistoryAdapter(
PersonalHomeRatingViewHolder(PersonalHomeRatingBinding.bind(view))
}
+ ITEM_TYPE_IMAGE_ARTICLE -> {
+ MyImageArticleViewHolder(parent.toBinding(),
+ object : MyImageArticleViewHolder.OnMyArticleImageListener {
+ override fun navigateToImageArticleDetailPage(id: String) {
+ ARouter.getInstance().build(RouteConsts.activity.imageArticleDetailActivity)
+ .withString(EntranceConsts.KEY_IMAGE_ARTICLE_ID, id)
+ .navigation()
+ }
+
+ override fun voteImageArticle(id: String, vote: Boolean) {
+ mListViewModel.useCase.voteImageArticle(id, vote)
+ }
+
+ })
+ }
+
else -> {
view = mLayoutInflater.inflate(R.layout.community_answer_item, parent, false)
PersonalItemViewHolder(mContext, CommunityAnswerItemBinding.bind(view), itemClickCallback)
@@ -76,8 +94,26 @@ class UserHistoryAdapter(
}
}
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: List) {
+ if (payloads.isNotEmpty()) {
+ when (holder) {
+ is PersonalItemViewHolder -> {
+ bindNormalItem(holder, position, payloads)
+ }
+
+ is MyImageArticleViewHolder -> {
+ bindImageArticle(holder, position, payloads)
+ }
+ }
+ } else {
+ super.onBindViewHolder(holder, position, payloads)
+ }
+
+ }
+
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
+ is MyImageArticleViewHolder -> bindImageArticle(holder, position)
is PersonalItemViewHolder -> bindNormalItem(holder, position)
is PersonalHomeRatingViewHolder -> bindRatingItem(holder, position)
is FooterViewHolder -> {
@@ -87,10 +123,18 @@ class UserHistoryAdapter(
}
}
- private fun bindNormalItem(holder: PersonalItemViewHolder, position: Int) {
- val historyEntity = mEntityList[holder.adapterPosition]
+ private fun bindImageArticle(
+ holder: MyImageArticleViewHolder,
+ position: Int,
+ payloads: List? = null
+ ) {
+ val historyEntity = mEntityList[position]
+ holder.bind(historyEntity, payloads)
+ }
- holder.bindPersonalItem(historyEntity, mEntrance, position)
+ private fun bindNormalItem(holder: PersonalItemViewHolder, position: Int, payloads: List? = null) {
+ val historyEntity = mEntityList[holder.adapterPosition]
+ holder.bindPersonalItem(historyEntity, mEntrance, position, payloads)
}
private fun bindRatingItem(holder: PersonalHomeRatingViewHolder, position: Int) {
@@ -159,4 +203,9 @@ class UserHistoryAdapter(
override fun getItemCount(): Int {
return if (mEntityList == null || mEntityList.isEmpty()) 0 else mEntityList.size + 1
}
+
+ companion object {
+ const val USER_HISTORY_PAYLOADS_VOTE_CHANGED = "user_history_payloads_vote_changed"
+ private const val ITEM_TYPE_IMAGE_ARTICLE = 110
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryFragment.kt
index 501979cccf..ae960db22b 100644
--- a/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/personalhome/home/UserHistoryFragment.kt
@@ -7,21 +7,32 @@ import android.view.View
import android.widget.TextView
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.ItemDecoration
+import androidx.recyclerview.widget.StaggeredGridLayoutManager
+import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.core.AppExecutor
import com.gh.common.util.*
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.baselist.ListFragment
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.common.utils.*
+import com.gh.gamecenter.common.view.FixLinearLayoutManager
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.databinding.FragmentUserPublishBinding
import com.gh.gamecenter.entity.*
+import com.gh.gamecenter.eventbus.EBImageArticleChanged
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.PersonalEntity
import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper
+import com.gh.gamecenter.forum.home.recommend.ImageArticleDetailActivity
+import com.gh.gamecenter.forum.home.recommend.fragment.ImageArticleHomeFragment
import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity
+import com.gh.gamecenter.personalhome.home.UserHistoryAdapter.Companion.USER_HISTORY_PAYLOADS_VOTE_CHANGED
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
@@ -30,6 +41,8 @@ import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
import com.gh.gamecenter.video.detail.CustomManager
import com.halo.assistant.HaloApp
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
class UserHistoryFragment : ListFragment() {
@@ -79,6 +92,68 @@ class UserHistoryFragment : ListFragment {
+ updateImageArticleItem(changed.id, true) {
+ val newCount = it.count
+ newCount.vote = changed.count
+ it.count = newCount
+ it.me.isCommunityArticleVote = changed.isVoted
+ }
+ }
+
+ changed is EBImageArticleChanged.CommentChanged -> {
+ updateImageArticleItem(changed.id, true) {
+ val newCount = it.count
+ newCount.comment = changed.count
+ it.count = newCount
+ }
+ }
+
+ changed is EBImageArticleChanged.DataDeleted -> {
+ mViewModel?.deleteImageArticle(changed.imageArticleId)
+ }
+
+ changed is EBImageArticleChanged.DataChanged -> {
+ val imageArticle = changed.data
+ updateImageArticleItem(changed.data.id, false) {
+ it.title = imageArticle.title
+ it.brief = imageArticle.content
+ it.images = imageArticle.images
+ it.imagesInfo = imageArticle.imagesInfos
+ it.community = imageArticle.community?.toCommunityEntity() ?: CommunityEntity()
+ it.sections = imageArticle.sections
+ it.count = imageArticle.count
+ it.user = imageArticle.user
+ it.time = imageArticle.time.update
+ it.status = imageArticle.status
+ }
+ }
+ }
+ }
+
+ private fun updateImageArticleItem(
+ id: String,
+ hasPayload: Boolean,
+ block: (PersonalHistoryEntity) -> Unit
+ ) {
+ val dataList = mAdapter?.entityList ?: return
+ val position =
+ dataList.indexOfFirst { it.type == ImageArticleEntity.IMAGE_ARTICLE_TYPE && it.id == id }
+ if (position != -1) {
+ val item = dataList[position]
+ block(item)
+ if (hasPayload) {
+ mAdapter?.notifyItemChanged(position, USER_HISTORY_PAYLOADS_VOTE_CHANGED)
+ } else {
+ mAdapter?.notifyItemChanged(position)
+ }
+
+ }
+ }
+
override fun onResume() {
resumeVideo()
super.onResume()
@@ -148,12 +223,14 @@ class UserHistoryFragment : ListFragment {
allType.text = "全部 $count"
}
+
+ UserHistoryViewModel.TYPE.IMAGE_ARTICLE -> {
+ imageArticleType.text = getString(R.string.image_article_with_count, "$count")
+ }
+
UserHistoryViewModel.TYPE.VIDEO -> {
mCount.video = count
videoType.text = "视频 $count"
}
+
UserHistoryViewModel.TYPE.COMMUNITY_ARTICLE -> {
mCount.communityArticle = count
articleType.text = "帖子 $count"
}
+
UserHistoryViewModel.TYPE.QUESTION -> {
mCount.question = count
questionType.text = "提问 $count"
}
+
UserHistoryViewModel.TYPE.ANSWER -> {
mCount.answer = count
answerType.text = "回答 $count"
@@ -288,6 +373,10 @@ class UserHistoryFragment : ListFragment?) {
+ if (ts != null) {
+ val layoutManager = mListRv.layoutManager ?: return
+ if (mViewModel?.type == UserHistoryViewModel.TYPE.IMAGE_ARTICLE) {
+ if (layoutManager !is StaggeredGridLayoutManager) {
+ mListRv.layoutManager = StaggeredGridLayoutManager(2, RecyclerView.VERTICAL)
+ if (mListRv.itemDecorationCount == 0) {
+ itemDecoration?.let(mListRv::addItemDecoration)
+ }
+ mListRv.adapter = mAdapter
+ }
+ } else {
+ if (layoutManager is StaggeredGridLayoutManager) {
+ mListRv.layoutManager = FixLinearLayoutManager(requireContext())
+ if (mListRv.itemDecorationCount > 0) {
+ mItemDecoration?.let(mListRv::removeItemDecoration)
+ }
+ mListRv.adapter = mAdapter
+ }
+ }
+ }
+ super.onChanged(ts)
+ }
+
private fun getCurrentTabName(): String {
return when (mCurrentType) {
UserHistoryViewModel.TYPE.ALL -> "全部"
+ UserHistoryViewModel.TYPE.IMAGE_ARTICLE -> "图文"
UserHistoryViewModel.TYPE.VIDEO -> "视频"
UserHistoryViewModel.TYPE.COMMUNITY_ARTICLE -> "帖子"
UserHistoryViewModel.TYPE.QUESTION -> "提问"
@@ -339,7 +454,14 @@ class UserHistoryFragment : ListFragment {
val resultData =
data.getParcelableExtra(ArticleDetailEntity::class.java.simpleName)
@@ -411,6 +534,7 @@ class UserHistoryFragment : ListFragment {
val resultData =
data.getParcelableExtra(CommentEntity::class.java.simpleName)
@@ -421,6 +545,8 @@ class UserHistoryFragment : ListFragment {
val resultData =
data.getParcelableExtra(QuestionsDetailEntity::class.java.simpleName)
@@ -431,6 +557,7 @@ class UserHistoryFragment : ListFragment {
val resultData =
data.getParcelableExtra(ForumVideoEntity::class.java.simpleName)
@@ -468,6 +595,7 @@ class UserHistoryFragment : ListFragment()
var count = MutableLiveData()
+ private val imageArticleRepository = ImageArticleRepository.newInstance()
+ private val compositeDisposable = CompositeDisposable()
+ val useCase = ImageArticleUseCase(imageArticleRepository, compositeDisposable)
+
init {
setOverLimitSize(1)
}
@@ -50,7 +59,6 @@ class UserHistoryViewModel(
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData) { list ->
-
videoList = ArrayList(list.map { it.transformForumVideoEntity() })
mResultLiveData.postValue(list)
@@ -132,6 +140,16 @@ class UserHistoryViewModel(
}
}
+ fun deleteImageArticle(imageArticleId: String) {
+ val oldData = mResultLiveData.value ?: return
+ val newData = oldData.toMutableList()
+ val hasRemoved =
+ newData.removeAll { it.type == ImageArticleEntity.IMAGE_ARTICLE_TYPE && it.id == imageArticleId }
+ if (hasRemoved) {
+ mResultLiveData.value = newData
+ }
+ }
+
enum class SCENE(val value: String) {
COMMENT("comment"),
QUESTION_ANSWER("question_answer")
@@ -139,6 +157,7 @@ class UserHistoryViewModel(
enum class TYPE(val value: String) {
ALL("all"),
+ IMAGE_ARTICLE("image_article"),
VIDEO("video"),
COMMUNITY_ARTICLE("community_article"),
ANSWER("answer"),
diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt
index f5e214af59..65f86143c4 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt
@@ -4,17 +4,21 @@ import android.annotation.SuppressLint
import android.view.View
import android.widget.TextView
import com.airbnb.lottie.LottieAnimationView
+import com.alibaba.android.arouter.launcher.ARouter
import com.gh.common.util.*
import com.gh.common.util.LogUtils
import com.gh.common.util.NewLogUtils
import com.gh.gamecenter.feature.view.GameIconView
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.*
+import com.gh.gamecenter.entity.ImageArticleEntity
import com.gh.gamecenter.entity.PersonalHistoryEntity
import com.gh.gamecenter.entity.VoteEntity
import com.gh.gamecenter.forum.detail.ForumDetailActivity
@@ -24,6 +28,7 @@ import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.CommunityItemData
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.entity.Count
+import com.gh.gamecenter.forum.home.recommend.ImageArticleDetailActivity
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
import com.gh.gamecenter.qa.questions.invite.QuestionsInviteActivity
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
@@ -267,6 +272,12 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
)
}
+ ImageArticleEntity.IMAGE_ARTICLE_TYPE -> {
+ ARouter.getInstance().build(RouteConsts.activity.imageArticleDetailActivity)
+ .withString(EntranceConsts.KEY_IMAGE_ARTICLE_ID, entity.id)
+ .navigation()
+ }
+
else -> {
val communityId = entity.community.id
val intent = ArticleDetailActivity.getCommentIntent(
@@ -398,19 +409,27 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
}
})
} else {
- val voteObservable = if (entity.type == "community_article") {
- RetrofitManager.getInstance().api.postCommunityArticleVote(entity.id)
- } else {
- RetrofitManager.getInstance().api
- .postVoteQuestionComment(entity.questions.id, entity.id)
- .map { GsonUtils.fromJson(it.string(), VoteEntity::class.java) }
+ when (entity.type) {
+ "community_article" -> {
+ RetrofitManager.getInstance().api.postCommunityArticleVote(entity.id)
+ }
+
+ ImageArticleEntity.IMAGE_ARTICLE_TYPE -> {
+ RetrofitManager.getInstance().api.voteArticleImage(entity.id)
+ .toObservable()
+ }
+
+ else -> {
+ RetrofitManager.getInstance().api
+ .postVoteQuestionComment(entity.questions.id, entity.id)
+ .map { GsonUtils.fromJson(it.string(), VoteEntity::class.java) }
+ }
}
- voteObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response() {
override fun onResponse(response: VoteEntity?) {
- if (entity.type == "community_article") {
+ if (entity.type == "community_article" || entity.type == ImageArticleEntity.IMAGE_ARTICLE_TYPE) {
entity.me.isCommunityArticleVote = true
} else {
entity.me.isAnswerVoted = true
@@ -484,18 +503,26 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
}
})
} else {
- val unVoteObservable = if (entity.type == "community_article") {
- RetrofitManager.getInstance().api.postCommunityArticleUnVote(entity.id)
- } else {
- RetrofitManager.getInstance().api
- .postAnswerUnvote(entity.id)
+ when (entity.type) {
+ "community_article" -> {
+ RetrofitManager.getInstance().api.postCommunityArticleUnVote(entity.id)
+ }
+
+ ImageArticleEntity.IMAGE_ARTICLE_TYPE -> {
+ RetrofitManager.getInstance().api.unVoteArticleImage(entity.id)
+ .toObservable()
+ }
+
+ else -> {
+ RetrofitManager.getInstance().api
+ .postAnswerUnvote(entity.id)
+ }
}
- unVoteObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response() {
override fun onResponse(response: VoteEntity?) {
- if (entity.type == "community_article") {
+ if (entity.type == "community_article" || entity.type == ImageArticleEntity.IMAGE_ARTICLE_TYPE) {
entity.me.isCommunityArticleVote = false
} else {
entity.me.isAnswerVoted = false
diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailCommentListFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailCommentListFragment.kt
index e59c98e811..546f103f1d 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailCommentListFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailCommentListFragment.kt
@@ -13,7 +13,6 @@ import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.common.view.CustomDividerItemDecoration
import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter
import com.halo.assistant.HaloApp
-import splitties.views.backgroundColor
class ArticleDetailCommentListFragment : ListFragment() {
diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt
index 21c7e16ae7..5c2e9d2150 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt
@@ -793,7 +793,8 @@ class ArticleDetailFragment : BaseCommentFragment {
ifLogin("帖子详情") {
- BbsReportHelper.showReportDialog(mViewModel.detailEntity?.id ?: "")
+
+ BbsReportHelper.showReportDialog(BbsReportHelper.PostsReporter(mViewModel.detailEntity?.id ?: ""))
}
NewLogUtils.logSharePanelClick(
"click_report",
@@ -991,7 +992,6 @@ class ArticleDetailFragment : BaseCommentFragment {
GameCollectionCommentConversationFragment().with(intent.extras)
}
+
isCommentConversation && commentId.isNotEmpty() -> {
CommentConversationFragment().with(intent.extras)
}
+
answerId.isNotEmpty() -> {
NewCommentFragment.getAnswerCommentInstance(
answerId,
@@ -94,6 +97,7 @@ class CommentActivity : BaseActivity() {
commentCallback
)
}
+
articleId.isNotEmpty() -> {
NewCommentFragment.getCommunityArticleCommentInstance(
articleId,
@@ -106,6 +110,7 @@ class CommentActivity : BaseActivity() {
commentCallback
)
}
+
questionId.isNotEmpty() -> {
NewCommentFragment.getCommunityQuestionCommentInstance(
questionId,
@@ -118,6 +123,7 @@ class CommentActivity : BaseActivity() {
commentCallback
)
}
+
gameCollectionId.isNotEmpty() -> {
NewCommentFragment.getGameCollectionCommentInstance(
gameCollectionId,
@@ -131,6 +137,18 @@ class CommentActivity : BaseActivity() {
commentCallback
)
}
+
+ imageArticleId.isNotEmpty() -> {
+ NewCommentFragment.getImageArticleCommentInstance(
+ imageArticleId,
+ showKeyboard,
+ commentCount,
+ mShowInputOnly,
+ commentEntity,
+ commentCallback
+ )
+ }
+
else -> {
NewCommentFragment.getVideoCommentInstance(
videoId,
@@ -197,6 +215,8 @@ class CommentActivity : BaseActivity() {
const val REQUEST_CODE = 8123
+ const val IMAGE_ARTICLE_ID = "image_article_id"
+
@JvmStatic
fun getAnswerCommentIntent(
context: Context,
@@ -280,6 +300,7 @@ class CommentActivity : BaseActivity() {
articleId: String,
videoId: String,
questionId: String,
+ imageArticleId: String,
showKeyboard: Boolean = false,
position: Int = -1,
entrance: String,
@@ -290,6 +311,7 @@ class CommentActivity : BaseActivity() {
intent.putExtra(EntranceConsts.KEY_COMMUNITY_ARTICLE_ID, articleId)
intent.putExtra(VIDEO_ID, videoId)
intent.putExtra(QUESTION_ID, questionId)
+ intent.putExtra(IMAGE_ARTICLE_ID, imageArticleId)
intent.putExtra(EntranceConsts.KEY_COMMUNITY_ID, communityId)
intent.putExtra(EntranceConsts.KEY_POSITION, position)
intent.putExtra(KEY_COMMENT_ID, commentId)
@@ -446,6 +468,30 @@ class CommentActivity : BaseActivity() {
return intent
}
+ /**
+ * 回复图文评论
+ */
+ fun getImageArticleCommentReplyIntent(
+ context: Context,
+ imageArticleId: String,
+ commentId: String,
+ commentCount: Int? = 0,
+ commentEntity: CommentEntity? = null
+ ): Intent {
+ val intent = Intent(context, CommentActivity::class.java)
+ intent.putExtra(KEY_COMMENT_ID, commentId)
+ intent.putExtra(COMMENT_COUNT, commentCount)
+ intent.putExtra(SHOW_KEYBOARD, true)
+ intent.putExtra(SHOW_INPUT_ONLY, true)
+ intent.putExtra(COMMENT_ENTITY, commentEntity)
+ intent.putExtra(IMAGE_ARTICLE_ID, imageArticleId)
+ intent.putExtra(USE_REPLY_API, true)
+ if (context is Activity) {
+ context.overridePendingTransition(0, 0)
+ }
+ return intent
+ }
+
/**
* 游戏单评论对话
*/
@@ -467,6 +513,28 @@ class CommentActivity : BaseActivity() {
intent.putExtra(EntranceConsts.KEY_IS_COMMENT_CONVERSATION, true)
return intent
}
+
+ /**
+ * 评论图文
+ */
+ @JvmStatic
+ fun getImageArticleCommentIntent(
+ context: Context,
+ imageArticleId: String,
+ communityId: String,
+ commentCount: Int? = 0,
+ ): Intent {
+ val intent = Intent(context, CommentActivity::class.java)
+ intent.putExtra(IMAGE_ARTICLE_ID, imageArticleId)
+ intent.putExtra(COMMENT_COUNT, commentCount)
+ intent.putExtra(SHOW_KEYBOARD, true)
+ intent.putExtra(COMMUNITY_ID, communityId)
+ intent.putExtra(SHOW_INPUT_ONLY, true)
+ if (context is Activity) {
+ context.overridePendingTransition(0, 0)
+ }
+ return intent
+ }
}
interface CommentListener {
diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentAdapter.kt
index bfc41b5155..fa5e3d145a 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentAdapter.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentAdapter.kt
@@ -172,6 +172,7 @@ class NewCommentAdapter(
mViewModel.communityId,
mViewModel.videoId,
mViewModel.questionId,
+ mViewModel.imageArticleId,
commentEntity,
holder.commentLikeCountTv,
holder.commentLikeIv,
@@ -251,6 +252,8 @@ class NewCommentAdapter(
CommentType.GAME_COLLECTION,
CommentType.GAME_COLLECTION_CONVERSATION -> "游戏单详情-评论管理"
+
+ CommentType.IMAGE_ARTICLE -> "图文详情-评论管理"
}
val userHomePageTabPosition = if (mViewModel.commentType.isVideo()) 2 else 1
diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentConversationFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentConversationFragment.kt
index 990f722a72..be5b0c4b2f 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentConversationFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentConversationFragment.kt
@@ -44,9 +44,12 @@ class NewCommentConversationFragment : NewCommentFragment() {
mVideoId = arguments?.getString(CommentActivity.VIDEO_ID) ?: ""
mIsVideoAuthor = arguments?.getBoolean(CommentActivity.IS_VIDEO_AUTHOR, false) ?: false
+ mImageArticleId = arguments?.getString(CommentActivity.IMAGE_ARTICLE_ID) ?: ""
+
mCommentType = when {
mAnswerId.isNotEmpty() -> CommentType.ANSWER_CONVERSATION
mArticleId.isNotEmpty() -> CommentType.COMMUNITY_ARTICLE_CONVERSATION
+ mImageArticleId.isNotEmpty() -> CommentType.IMAGE_ARTICLE
else -> CommentType.VIDEO_CONVERSATION
}
diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt
index 1c9175acb7..fc10ec2867 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt
@@ -42,6 +42,7 @@ import com.gh.gamecenter.feature.eventbus.EBDeleteComment
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.GAME_COLLECTION_ID
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.GAME_COLLECTION_TITLE
+import com.gh.gamecenter.qa.comment.CommentActivity.Companion.IMAGE_ARTICLE_ID
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.QUESTION_ID
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
@@ -82,6 +83,7 @@ open class NewCommentFragment : ListFragment
protected var mGameCollectionTitle: String = ""
protected var mCommentId: String = ""
protected var mRootCommentId: String = ""
+ protected var mImageArticleId: String = ""
protected var mShowInputOnly: Boolean = false // 是否只显示输入框,不显示列表
protected var mIsVideoAuthor: Boolean = false//是否是视频作者
protected var mCommentType = CommentType.ANSWER
@@ -130,6 +132,7 @@ open class NewCommentFragment : ListFragment
mShowInputOnly = getBoolean(SHOW_INPUT_ONLY, false)
mCommentEntity = getParcelable(COMMENT_ENTITY)
mIsVideoAuthor = getBoolean(IS_VIDEO_AUTHOR, false)
+ mImageArticleId = getString(IMAGE_ARTICLE_ID) ?: ""
}
super.onCreate(savedInstanceState)
@@ -229,6 +232,8 @@ open class NewCommentFragment : ListFragment
CommentType.GAME_COLLECTION,
CommentType.GAME_COLLECTION_CONVERSATION -> "游戏单评论及回复"
+ CommentType.IMAGE_ARTICLE -> "图文的评论和回复"
+
else -> ""
}
ErrorHelper.handleError(
@@ -412,7 +417,8 @@ open class NewCommentFragment : ListFragment
gameCollectionId = mGameCollectionId,
rootCommentId = mRootCommentId,
commentType = mCommentType,
- isVideoAuthor = mIsVideoAuthor
+ isVideoAuthor = mIsVideoAuthor,
+ imageArticleId = mImageArticleId
)
)
return mViewModel
@@ -435,6 +441,8 @@ open class NewCommentFragment : ListFragment
CommentType.GAME_COLLECTION,
CommentType.GAME_COLLECTION_CONVERSATION -> "(游戏单详情-评论列表)"
+
+ CommentType.IMAGE_ARTICLE -> "图文详情-评论列表"
}
mAdapter = NewCommentAdapter(requireContext(), mViewModel, true, this, this, entrance)
}
@@ -598,6 +606,10 @@ open class NewCommentFragment : ListFragment
"游戏单详情-评论-回复"
}
}
+
+ CommentType.IMAGE_ARTICLE -> {
+ "图文详情-评论-写评论"
+ }
}
}
@@ -945,5 +957,28 @@ open class NewCommentFragment : ListFragment
)
}
}
+
+ fun getImageArticleCommentInstance(
+ imageArticleId: String,
+ showSoftKeyboardOnStartUp: Boolean,
+ commentCount: Int,
+ showInputOnly: Boolean,
+ commentEntity: CommentEntity?,
+ listener: CommentActivity.CommentListener
+ ): NewCommentFragment {
+ return NewCommentFragment().apply {
+ mCommentListener = listener
+ with(
+ bundleOf(
+ SHOW_SOFT_KEY_BOARD_ON_STARTUP to showSoftKeyboardOnStartUp,
+ IMAGE_ARTICLE_ID to imageArticleId,
+ COMMENT_COUNT to commentCount,
+ COMMENT_TYPE to CommentType.IMAGE_ARTICLE,
+ SHOW_INPUT_ONLY to showInputOnly,
+ COMMENT_ENTITY to commentEntity
+ )
+ )
+ }
+ }
}
}
diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt
index 07ae53161b..b541449f97 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt
@@ -29,7 +29,6 @@ import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
-import org.json.JSONArray
import org.json.JSONObject
import retrofit2.HttpException
@@ -44,7 +43,8 @@ open class NewCommentViewModel(
var gameCollectionId: String = "",
var rootCommentId: String = "",
var commentType: CommentType = CommentType.ANSWER,
- var isVideoAuthor: Boolean = false
+ var isVideoAuthor: Boolean = false,
+ var imageArticleId: String = ""
) : ListViewModel(application) {
private val mPostCommentLiveData = MutableLiveData>()
@@ -138,6 +138,7 @@ open class NewCommentViewModel(
this.articleDetail = it
}, {})
}
+
CommentType.VIDEO,
CommentType.VIDEO_CONVERSATION -> {
api.getBbsVideoDetail(videoId)
@@ -147,6 +148,7 @@ open class NewCommentViewModel(
this.videoDetail = it
}, {})
}
+
CommentType.COMMUNITY_QUESTION,
CommentType.COMMUNITY_QUESTION_CONVERSATION -> {
api.getQuestionsById(questionId)
@@ -156,6 +158,7 @@ open class NewCommentViewModel(
this.questionDetail = it
}, {})
}
+
else -> {}
}
}
@@ -165,8 +168,7 @@ open class NewCommentViewModel(
CommentType.COMMUNITY_QUESTION,
CommentType.COMMUNITY_QUESTION_CONVERSATION -> {
questionDetail?.let {
- if (reply)
- {
+ if (reply) {
SensorsBridge.trackArticleReply(
customerType = it.user.auth?.text ?: "",
articleId = it.id ?: "",
@@ -439,6 +441,15 @@ open class NewCommentViewModel(
api.postReplyToGameCollectionComment(gameCollectionId, commentEntity.id, body)
}
}
+
+ CommentType.IMAGE_ARTICLE -> {
+ if (commentEntity == null) {
+ api.postImageArticleComment(imageArticleId, body)
+ } else {
+ api.postReplyToImageArticleComment(commentEntity.id, body)
+ }
+
+ }
}
// TODO Remove this apiResponse crap.
@@ -632,7 +643,8 @@ open class NewCommentViewModel(
private val gameCollectionId: String = "",
private val rootCommentId: String = "",
private val isVideoAuthor: Boolean = false,
- private val commentType: CommentType
+ private val commentType: CommentType,
+ private val imageArticleId: String = ""
) : ViewModelProvider.NewInstanceFactory() {
override fun create(modelClass: Class): T {
@@ -647,7 +659,8 @@ open class NewCommentViewModel(
commentId = commentId,
rootCommentId = rootCommentId,
commentType = commentType,
- isVideoAuthor = isVideoAuthor
+ isVideoAuthor = isVideoAuthor,
+ imageArticleId = imageArticleId
) as T
}
}
@@ -667,7 +680,9 @@ enum class CommentType {
VIDEO_CONVERSATION,
GAME_COLLECTION,
- GAME_COLLECTION_CONVERSATION;
+ GAME_COLLECTION_CONVERSATION,
+ IMAGE_ARTICLE;
+
fun isVideo(): Boolean {
return (this == VIDEO || this == VIDEO_CONVERSATION)
diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentFragment.kt
index 3c97c37caa..f6a0a49b03 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentFragment.kt
@@ -39,6 +39,8 @@ class StairsCommentFragment : NewCommentFragment() {
CommentType.GAME_COLLECTION,
CommentType.GAME_COLLECTION_CONVERSATION -> "(游戏单详情-评论列表)"
+
+ CommentType.IMAGE_ARTICLE -> "(图文详情-评论列表)"
}
mAdapter = StairsCommentAdapter(requireContext(), mViewModel, true, this, this, entrance)
}
diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentViewHolder.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentViewHolder.kt
index bbf79c7fb8..414132bed7 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentViewHolder.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/comment/StairsCommentViewHolder.kt
@@ -261,6 +261,7 @@ class StairsCommentViewHolder(
mViewModel.articleId,
mViewModel.communityId,
mViewModel.videoId,
+ mViewModel.imageArticleId,
commentEntity,
holder.binding.commentLikeCount,
holder.binding.commentLike,
@@ -314,6 +315,8 @@ class StairsCommentViewHolder(
CommentType.GAME_COLLECTION,
CommentType.GAME_COLLECTION_CONVERSATION -> "游戏单详情-评论管理"
+
+ CommentType.IMAGE_ARTICLE ->"图文详情-评论管理"
}
holder.binding.commentUserIcon.setOnClickListener {
DirectUtils.directToHomeActivity(
diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt
index 661b63db4b..636a216c7a 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt
@@ -32,6 +32,7 @@ import com.gh.gamecenter.core.utils.SpanBuilder
import com.gh.gamecenter.databinding.*
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.feature.entity.Permissions
+import com.gh.gamecenter.forum.home.recommend.ImageArticleUseCase
import com.gh.gamecenter.qa.article.detail.*
import com.gh.gamecenter.qa.comment.CommentActivity
import com.gh.gamecenter.qa.comment.CommentPictureAdapter
@@ -178,6 +179,7 @@ abstract class BaseCommentAdapter(
//刷新 ITEM_FILTER
mViewModel.commentCount -= 1
+ mViewModel.updateCommentCount(mViewModel.commentCount)
notifyItemChanged(1)
val isArticleDetail = this is ArticleDetailAdapter
@@ -241,14 +243,17 @@ abstract class BaseCommentAdapter(
binding.progressBar.visibility = View.GONE
binding.footerTv.setText(com.gh.gamecenter.common.R.string.loading_failed_retry)
}
+
isOver -> {
binding.progressBar.visibility = View.GONE
binding.footerTv.setText(loadOverHint)
}
+
isLoading -> {
binding.progressBar.visibility = View.VISIBLE
binding.footerTv.setText(com.gh.gamecenter.common.R.string.loading)
}
+
else -> {
binding.progressBar.visibility = View.GONE
}
@@ -285,18 +290,23 @@ abstract class BaseCommentAdapter(
article != null -> {
"全部评论"
}
+
questions != null -> {
"全部回答"
}
+
comment != null -> {
"全部回复"
}
+
gameCollection != null -> {
"玩家评论"
}
+
commentDetail != null -> {
"全部讨论"
}
+
else -> {
""
}
@@ -325,6 +335,7 @@ abstract class BaseCommentAdapter(
}
}
+
1 -> {
mViewModel.changeSort(BaseCommentViewModel.SortType.LATEST)
@@ -380,6 +391,7 @@ abstract class BaseCommentAdapter(
}
MtaHelper.onEvent("帖子详情", "全部评论", "评论正文")
}
+
viewModel.videoId.isNotEmpty() -> {
CommentActivity.getVideoCommentReplyIntent(
binding.root.context,
@@ -396,6 +408,7 @@ abstract class BaseCommentAdapter(
)
}
}
+
viewModel.questionId.isNotEmpty() -> {
CommentActivity.getQuestionCommentReplyIntent(
binding.root.context,
@@ -426,11 +439,13 @@ abstract class BaseCommentAdapter(
viewModel.articleId,
viewModel.videoId,
viewModel.questionId,
+ viewModel.imageArticleId,
false,
comment.floor,
entrance,
- PATH_ARTICLE_DETAIL
- ).apply {
+ PATH_ARTICLE_DETAIL,
+
+ ).apply {
binding.root.context.startActivity(this)
}
MtaHelper.onEvent("帖子详情", "全部评论", "回复")
@@ -527,9 +542,11 @@ abstract class BaseCommentAdapter(
viewModel.articleId.isNotEmpty() -> {
viewModel.topItemData?.articleDetail?.user?.id ?: ""
}
+
viewModel.questionId.isNotEmpty() -> {
viewModel.topItemData?.questionDetail?.user?.id ?: ""
}
+
else -> {
""
}
@@ -577,6 +594,7 @@ abstract class BaseCommentAdapter(
viewModel.articleId,
viewModel.videoId,
viewModel.questionId,
+ viewModel.imageArticleId,
false,
comment.floor,
entrance,
@@ -665,12 +683,19 @@ abstract class BaseCommentAdapter(
viewModel.articleId.isNotEmpty() -> {
if (viewModel is CommentConversationViewModel) PATH_ARTICLE_DETAIL_COMMENT else PATH_ARTICLE_DETAIL
}
+
viewModel.questionId.isNotEmpty() -> {
if (viewModel is CommentConversationViewModel) PATH_QUESTION_DETAIL_COMMENT else PATH_QUESTION_DETAIL
}
+
viewModel.videoId.isNotEmpty() -> {
if (viewModel is CommentConversationViewModel) PATH_VIDEO_DETAIL_COMMENT else PATH_VIDEO_DETAIL
}
+
+ viewModel.imageArticleId.isNotEmpty() -> {
+ if (viewModel is CommentConversationViewModel) PATH_IMAGE_ARTICLE_DETAIL_COMMENT else PATH_IMAGE_ARTICLE_DETAIL
+ }
+
else -> ""
}
val mtaKey = if (viewModel is ArticleDetailViewModel) "全部评论" else "评论详情-全部回复"
@@ -843,6 +868,7 @@ abstract class BaseCommentAdapter(
deleteCallBack
)
}
+
viewModel.videoId.isNotEmpty() -> {
showVideoCommentOptions(
view,
@@ -852,6 +878,7 @@ abstract class BaseCommentAdapter(
deleteCallBack
)
}
+
viewModel.questionId.isNotEmpty() -> {
showQuestionCommentOption(
view,
@@ -861,6 +888,16 @@ abstract class BaseCommentAdapter(
deleteCallBack
)
}
+
+ viewModel.imageArticleId.isNotEmpty() -> {
+ showImageArticleOption(
+ view,
+ comment,
+ viewModel,
+ path,
+ deleteCallBack
+ )
+ }
}
}
@@ -872,10 +909,12 @@ abstract class BaseCommentAdapter(
bbsId = viewModel.topItemData?.articleDetail?.community?.id ?: ""
type = viewModel.topItemData?.articleDetail?.type ?: ""
}
+
viewModel.questionId.isNotEmpty() -> {
bbsId = viewModel.topItemData?.questionDetail?.community?.id ?: ""
type = viewModel.topItemData?.questionDetail?.type ?: ""
}
+
else -> {
bbsId = (viewModel as? VideoCommentViewModel)?.videoDetail?.bbs?.id ?: ""
type = (viewModel as? VideoCommentViewModel)?.videoDetail?.type ?: ""
@@ -916,6 +955,7 @@ abstract class BaseCommentAdapter(
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
}
+
"采纳" -> {
DialogHelper.showDialog(
view.context,
@@ -929,6 +969,7 @@ abstract class BaseCommentAdapter(
extraConfig = DialogHelper.Config(centerContent = true, centerTitle = true)
)
}
+
"取消采纳" -> {
viewModel.acceptQuestionComment(
viewModel.questionId,
@@ -936,12 +977,15 @@ abstract class BaseCommentAdapter(
false
)
}
+
"加精选" -> {
showHighlightQuestionCommentDialog(comment, view.context, viewModel, true)
}
+
"取消精选" -> {
showHighlightQuestionCommentDialog(comment, view.context, viewModel, false)
}
+
"置顶" -> {
DialogHelper.showDialog(
view.context,
@@ -955,6 +999,7 @@ abstract class BaseCommentAdapter(
extraConfig = DialogHelper.Config(centerContent = true, centerTitle = true)
)
}
+
"取消置顶" -> {
DialogHelper.showDialog(
view.context,
@@ -1048,6 +1093,7 @@ abstract class BaseCommentAdapter(
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
}
+
"置顶" -> {
DialogHelper.showDialog(
view.context,
@@ -1061,6 +1107,7 @@ abstract class BaseCommentAdapter(
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
}
+
"取消置顶" -> {
DialogHelper.showDialog(
view.context,
@@ -1119,6 +1166,7 @@ abstract class BaseCommentAdapter(
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
}
+
"置顶" -> {
DialogHelper.showDialog(
view.context,
@@ -1132,6 +1180,7 @@ abstract class BaseCommentAdapter(
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
}
+
"取消置顶" -> {
DialogHelper.showDialog(
view.context,
@@ -1166,6 +1215,78 @@ abstract class BaseCommentAdapter(
}
}
+ private fun showImageArticleOption(
+ view: View,
+ comment: CommentEntity,
+ viewModel: BaseCommentViewModel,
+ path: String,
+ deleteCallBack: ((comment: CommentEntity) -> Unit)?
+ ) {
+ CommentHelper.showImageArticleCommentOptions(
+ view,
+ comment,
+ viewModel.imageArticleId,
+ isShowTop = path == PATH_IMAGE_ARTICLE_DETAIL,
+ listener = object : OnCommentOptionClickListener {
+ override fun onCommentOptionClick(entity: CommentEntity, option: String) {
+ when (option) {
+ "删除评论" -> {
+ DialogHelper.showDialog(
+ view.context,
+ "提示",
+ "删除评论后,评论下所有的回复都将被删除",
+ "删除",
+ "取消",
+ {
+ viewModel.deleteComment(comment) {
+ deleteCallBack?.invoke(comment)
+ }
+ }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
+ )
+ }
+
+ "置顶" -> {
+ DialogHelper.showDialog(
+ view.context,
+ "提示",
+ "是否将此条评论置顶?",
+ "确认",
+ "取消",
+ confirmClickCallback = {
+ commentTop(view.context, viewModel, comment, false)
+ },
+ extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
+ )
+ }
+
+ "取消置顶" -> {
+ DialogHelper.showDialog(
+ view.context,
+ "提示",
+ "是否将此条评论取消置顶?",
+ "确认",
+ "取消",
+ confirmClickCallback = {
+ viewModel.updateCommentTop(
+ comment.id ?: "",
+ top = false,
+ isAgain = false
+ ) { isSuccess, _ ->
+ if (isSuccess) {
+ viewModel.onUpdateCommentTopSuccess()
+ }
+ }
+ },
+ extraConfig = DialogHelper.Config(centerContent = true, centerTitle = true)
+ )
+ }
+ }
+
+ }
+
+ })
+ }
+
private fun commentTop(
context: Context,
viewModel: BaseCommentViewModel,
@@ -1212,6 +1333,9 @@ abstract class BaseCommentAdapter(
const val PATH_VIDEO_DETAIL = "视频详情"
const val PATH_VIDEO_DETAIL_COMMENT = "视频评论详情"
+
+ const val PATH_IMAGE_ARTICLE_DETAIL = "帖子详情"
+ const val PATH_IMAGE_ARTICLE_DETAIL_COMMENT = "帖子评论详情"
}
enum class AdapterType {
diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentViewModel.kt
index 4e86e91e27..314dc48736 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentViewModel.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentViewModel.kt
@@ -2,10 +2,12 @@ package com.gh.gamecenter.qa.comment.base
import android.annotation.SuppressLint
import android.app.Application
+import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.gh.common.util.ErrorHelper
import com.gh.common.util.NewLogUtils
import com.gh.common.util.PostCommentUtils
+import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.common.baselist.LoadParams
import com.gh.gamecenter.common.baselist.LoadStatus
@@ -24,10 +26,12 @@ import com.gh.gamecenter.entity.CommentDraft
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.feature.entity.MeEntity
import com.gh.gamecenter.feature.entity.Permissions
+import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.qa.article.detail.CommentItemData
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.retrofit.service.ApiService
import com.gh.gamecenter.room.AppDatabase
+import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
@@ -42,7 +46,8 @@ abstract class BaseCommentViewModel(
var questionId: String,
var communityId: String,
var gameCollectionId: String,
- var topCommentId: String = ""
+ var topCommentId: String = "",
+ val imageArticleId: String = "",
) : ListViewModel(application) {
protected val mApi: ApiService = RetrofitManager.getInstance().api
protected var mTotal: Int = 0
@@ -56,20 +61,29 @@ abstract class BaseCommentViewModel(
var isHandleTopComment = false
+ private val _updateCommentCountAction = MutableLiveData>()
+ val updateCommentCountAction: LiveData> = _updateCommentCountAction
+ fun updateCommentCount(count: Int) {
+ _updateCommentCountAction.value = Event(count)
+ }
+
override fun loadStatusControl(size: Int) {
if (mCurLoadParams.loadOffset == LoadParams.DEFAULT_OFFSET) { // 初始化列表
when {
size == 0 -> {
mLoadStatusLiveData.setValue(LoadStatus.INIT_EMPTY)
}
+
size == REQUEST_FAILURE_SIZE -> {
// TODO 处理列表加载失败问题
mLoadStatusLiveData.setValue(LoadStatus.INIT_LOADED)
}
+
size < mOverLimitSize -> {
// 避免一个屏幕出现两次分页
mLoadStatusLiveData.setValue(LoadStatus.INIT_OVER)
}
+
else -> mLoadStatusLiveData.setValue(LoadStatus.INIT_LOADED)
}
} else {
@@ -146,15 +160,28 @@ abstract class BaseCommentViewModel(
articleId.isNotEmpty() -> {
mApi.getCommunityArticleComment(commentId)
}
+
videoId.isNotEmpty() -> {
mApi.getCommunityVideoComment(commentId)
}
+
questionId.isNotEmpty() -> {
mApi.getCommunityQuestionComment(questionId, commentId)
}
+
gameCollectionId.isNotEmpty() -> {
mApi.getGameCollectionComment(gameCollectionId, commentId)
}
+
+ imageArticleId.isNotEmpty() -> {
+ mApi.getImageArticleCommentDetail(
+ commentId,
+ BuildConfig.VERSION_NAME,
+ HaloApp.getInstance().channel,
+ System.currentTimeMillis()
+ )
+ }
+
else -> null
} ?: return
single.compose(singleToMain())
@@ -172,6 +199,7 @@ abstract class BaseCommentViewModel(
load(LoadType.REFRESH)
}
}
+
SortType.OLDEST -> {
//根据total判断是否插入到最后
if (count >= mTotal) {
@@ -185,6 +213,7 @@ abstract class BaseCommentViewModel(
}
}
}
+
override fun onFailure(exception: Exception) {
super.onFailure(exception)
load(LoadType.REFRESH)
@@ -221,6 +250,7 @@ abstract class BaseCommentViewModel(
articleId,
videoId,
questionId,
+ imageArticleId,
comment.id,
object : PostCommentUtils.PostCommentListener {
override fun postSuccess(response: JSONObject?) {
@@ -273,7 +303,7 @@ abstract class BaseCommentViewModel(
}
fun unLike(comment: CommentEntity) {
- PostCommentUtils.unLikeComment(articleId, communityId, videoId, questionId, comment.id,
+ PostCommentUtils.unLikeComment(articleId, communityId, videoId, questionId, imageArticleId, comment.id,
object : PostCommentUtils.PostCommentListener {
override fun postSuccess(response: JSONObject?) {
val cloneComment = comment.clone()
@@ -319,12 +349,19 @@ abstract class BaseCommentViewModel(
mApi.deleteVideoComment(entity.id).toObservable()
}
}
+
questionId.isNotEmpty() -> {
mApi.deleteQuestionComment(questionId, entity.id).toObservable()
}
+
articleId.isNotEmpty() -> {
mApi.hideCommunityArticleComment(entity.id)
}
+
+ imageArticleId.isNotEmpty() -> {
+ mApi.deleteImageArticleComment(entity.id)
+ }
+
else -> null
} ?: return
observable.compose(observableToMain())
@@ -373,12 +410,19 @@ abstract class BaseCommentViewModel(
articleId.isNotEmpty() -> {
mApi.postArticleCommentTop(commentId, map)
}
+
questionId.isNotEmpty() -> {
mApi.postQuestionCommentTop(questionId, commentId, map)
}
+
videoId.isNotEmpty() -> {
mApi.postVideoCommentTop(commentId, map)
}
+
+ imageArticleId.isNotEmpty() -> {
+ mApi.postImageArticleTop(commentId, map)
+ }
+
else -> null
}
} else {
@@ -386,12 +430,19 @@ abstract class BaseCommentViewModel(
articleId.isNotEmpty() -> {
mApi.postArticleCommentUnTop(commentId)
}
+
questionId.isNotEmpty() -> {
mApi.postQuestionCommentUnTop(questionId, commentId)
}
+
videoId.isNotEmpty() -> {
mApi.postVideoCommentUnTop(commentId)
}
+
+ imageArticleId.isNotEmpty() -> {
+ mApi.postImageArticleUnTop(commentId)
+ }
+
else -> null
}
} ?: return
@@ -502,6 +553,7 @@ abstract class BaseCommentViewModel(
when {
commentNormal?.id == cloneComment.id -> it[index] =
CommentItemData(commentNormal = cloneComment)
+
commentTop?.id == cloneComment.id -> it[index] = CommentItemData(commentTop = cloneComment)
}
}
diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationFragment.kt
index a956f973aa..dcf2c24476 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationFragment.kt
@@ -29,6 +29,7 @@ import com.gh.gamecenter.qa.comment.base.BaseCommentFragment
import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel
import com.gh.gamecenter.qa.article.detail.CommentItemData
import com.gh.gamecenter.qa.comment.CommentActivity
+import com.gh.gamecenter.qa.comment.CommentActivity.Companion.IMAGE_ARTICLE_ID
import com.halo.assistant.HaloApp
class CommentConversationFragment :
@@ -118,7 +119,8 @@ class CommentConversationFragment :
// 取消掉
if (mUseAlternativeLayout) {
toolbarContainer.root.visibility = View.GONE
- container.updateLayoutParams { this as FrameLayout.LayoutParams
+ container.updateLayoutParams {
+ this as FrameLayout.LayoutParams
setMargins(0, 0, 0, 0)
}
}
@@ -192,6 +194,7 @@ class CommentConversationFragment :
}
}, 100)
}
+
else -> {
if (it == BaseCommentViewModel.LoadResult.DELETED) {
mReuseNoConn?.visibility = View.GONE
@@ -228,7 +231,8 @@ class CommentConversationFragment :
arguments?.getString(EntranceConsts.KEY_COMMUNITY_ID) ?: "",
arguments?.getString(CommentActivity.GAME_COLLECTION_ID) ?: "",
arguments?.getString(EntranceConsts.KEY_COMMENT_ID) ?: "",
- arguments?.getString(EntranceConsts.KEY_TOP_COMMENT_ID) ?: ""
+ arguments?.getString(EntranceConsts.KEY_TOP_COMMENT_ID) ?: "",
+ arguments?.getString(IMAGE_ARTICLE_ID) ?: ""
)
)
}
@@ -284,6 +288,15 @@ class CommentConversationFragment :
comment
)
startActivityForResult(intent, CommentActivity.REQUEST_CODE)
+ } else if (mViewModel.imageArticleId.isNotEmpty()) {
+ val intent = CommentActivity.getImageArticleCommentReplyIntent(
+ requireContext(),
+ mViewModel.imageArticleId,
+ arguments?.getString(EntranceConsts.KEY_COMMENT_ID) ?: "",
+ mViewModel.commentCount,
+ comment
+ )
+ startActivityForResult(intent, CommentActivity.REQUEST_CODE)
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt
index 94d741e8dd..de9ac40e1c 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt
@@ -4,10 +4,12 @@ import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
+import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.qa.article.detail.CommentItemData
import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel
+import com.halo.assistant.HaloApp
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
@@ -21,8 +23,9 @@ class CommentConversationViewModel(
communityId: String = "",
gameCollectionId: String = "",
var commentId: String = "",
- topCommentId: String = ""
-) : BaseCommentViewModel(application, articleId, videoId, questionId, communityId, topCommentId) {
+ topCommentId: String = "",
+ imageArticleId: String = ""
+) : BaseCommentViewModel(application, articleId, videoId, questionId, communityId,gameCollectionId, topCommentId, imageArticleId) {
var commentDetail: CommentEntity? = null
var positionInOriginList = -1
@@ -42,15 +45,30 @@ class CommentConversationViewModel(
map
)
}
+
videoId.isNotEmpty() -> {
mApi.getVideoCommentReply(videoId, commentId, currentSortType.value, page, map)
}
+
questionId.isNotEmpty() -> {
mApi.getQuestionCommentReply(questionId, commentId, currentSortType.value, page, map)
}
+
gameCollectionId.isNotEmpty() -> {
mApi.getGameCollectionCommentReply(gameCollectionId, commentId, page, map)
}
+
+ imageArticleId.isNotEmpty() -> {
+ mApi.getImageArticleDetailCommentReply(
+ commentId,
+ currentSortType.value,
+ page,
+ BuildConfig.VERSION_NAME,
+ HaloApp.getInstance().channel,
+ System.currentTimeMillis()
+ )
+ }
+
else -> null
}
}
@@ -61,15 +79,28 @@ class CommentConversationViewModel(
articleId.isNotEmpty() -> {
mApi.getCommunityArticleComment(commentId)
}
+
videoId.isNotEmpty() -> {
mApi.getCommunityVideoComment(commentId)
}
+
questionId.isNotEmpty() -> {
mApi.getCommunityQuestionComment(questionId, commentId)
}
+
gameCollectionId.isNotEmpty() -> {
mApi.getGameCollectionComment(gameCollectionId, commentId)
}
+
+ imageArticleId.isNotEmpty() -> {
+ mApi.getImageArticleCommentDetail(
+ commentId,
+ BuildConfig.VERSION_NAME,
+ HaloApp.getInstance().channel,
+ System.currentTimeMillis()
+ )
+ }
+
else -> null
} ?: return
single.subscribeOn(Schedulers.io())
@@ -113,6 +144,7 @@ class CommentConversationViewModel(
when {
commentNormal?.id == cloneComment.id -> it[index] =
CommentItemData(commentNormal = cloneComment)
+
commentTop?.id == cloneComment.id -> {
commentDetail = cloneComment
it[index] = CommentItemData(commentTop = cloneComment)
@@ -132,7 +164,8 @@ class CommentConversationViewModel(
private val communityId: String = "",
private val gameCollectionId: String = "",
private val commentId: String,
- private val topCommentId: String
+ private val topCommentId: String,
+ private val imageArticleId: String,
) : ViewModelProvider.NewInstanceFactory() {
override fun create(modelClass: Class): T {
@@ -144,7 +177,8 @@ class CommentConversationViewModel(
communityId = communityId,
gameCollectionId = gameCollectionId,
commentId = commentId,
- topCommentId = topCommentId
+ topCommentId = topCommentId,
+ imageArticleId = imageArticleId
) as T
}
}
diff --git a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt
index b53f8749fd..40c1dccc19 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt
@@ -2,12 +2,15 @@ package com.gh.gamecenter.qa.dialog
import android.annotation.SuppressLint
import android.app.Activity
+import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Build
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputMethodManager
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import com.gh.gamecenter.common.base.activity.BaseActivity
@@ -45,8 +48,8 @@ class ChooseForumActivity : BaseActivity() {
val sourceEntrance = intent.getStringExtra(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: ""
initViewPager()
- binding.searchEt.doOnTextChanged { _, _, _, _ ->
- val searchKey = binding.searchEt.text?.toString()
+ binding.searchEt.doOnTextChanged { text, _, _, _ ->
+ val searchKey = text?.toString()
if (searchKey.isNullOrEmpty()) {
switchUI(false)
} else {
@@ -61,6 +64,25 @@ class ChooseForumActivity : BaseActivity() {
}
false
}
+
+ binding.searchEt.setOnEditorActionListener { v, actionId, event ->
+ if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+ // 开始搜索
+ mSearchResultFragment?.setSearchKeyAndType(
+ binding.searchEt.text?.toString() ?: "",
+ SearchType.MANUAL.value
+ )
+ // 清除焦点以保持搜索按钮
+ binding.searchEt.clearFocus()
+
+ // 隐藏软键盘
+ val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ imm.hideSoftInputFromWindow(binding.searchEt.windowToken, 0)
+ return@setOnEditorActionListener true
+ } else {
+ return@setOnEditorActionListener false
+ }
+ }
binding.closeIv.setOnClickListener {
NewLogUtils.logChooseForumPanelClick("click_select_forum_panel_close", "", "", "")
finish()
diff --git a/app/src/main/java/com/gh/gamecenter/qa/draft/CommunityDraftWrapperActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/draft/CommunityDraftWrapperActivity.kt
index abc36a54f6..6d06cf36dd 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/draft/CommunityDraftWrapperActivity.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/draft/CommunityDraftWrapperActivity.kt
@@ -5,7 +5,10 @@ import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import com.gh.gamecenter.common.base.activity.BaseActivity_TabLayout
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.utils.toResString
import com.gh.gamecenter.common.utils.updateStatusBarColor
+import com.gh.gamecenter.forum.home.recommend.fragment.ImageArticleDraftFragment
import com.gh.gamecenter.qa.article.draft.ArticleDraftFragment
import com.gh.gamecenter.qa.questions.draft.QuestionDraftFragment
import com.gh.gamecenter.video.videomanager.VideoDraftFragment
@@ -22,15 +25,17 @@ class CommunityDraftWrapperActivity : BaseActivity_TabLayout() {
}
override fun initFragmentList(fragments: MutableList) {
+ fragments.add(ImageArticleDraftFragment())
fragments.add(ArticleDraftFragment())
fragments.add(QuestionDraftFragment())
fragments.add(VideoDraftFragment())
}
override fun initTabTitleList(tabTitleList: MutableList) {
- tabTitleList.add("帖子草稿")
- tabTitleList.add("问题草稿")
- tabTitleList.add("视频草稿")
+ tabTitleList.add(R.string.image_article.toResString())
+ tabTitleList.add("帖子")
+ tabTitleList.add("问题")
+ tabTitleList.add("视频")
}
override fun isAutoResetViewBackgroundEnabled(): Boolean = true
diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt
index 5113117fee..b1345525d8 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt
@@ -324,6 +324,7 @@ class NewQuestionDetailFragment :
mBinding.root.setBackgroundColor(Color.TRANSPARENT)
updateView()
}
+
else -> {
if (it == BaseCommentViewModel.LoadResult.DELETED) {
mReuseNoConn?.visibility = View.GONE
@@ -430,7 +431,7 @@ class NewQuestionDetailFragment :
mAdapter?.questionDetailVH?.bindView(it)
}
mViewModel.top.observeNonNull(this) { top ->
- val topSuccessToast = if(top) {
+ val topSuccessToast = if (top) {
R.string.article_detail_top_success_toast
} else {
R.string.article_detail_cancel_top_success_toast
@@ -626,12 +627,24 @@ class NewQuestionDetailFragment :
// 置顶/取消置顶
if (questionEntity.me.isModerator
&& !questionEntity.me.isCommunityTop
- && moderatorPermissions.topQuestion > Permissions.GUEST) {
- entities.add(MenuItemEntity(getString(R.string.article_detail_more_top_title), R.drawable.icon_more_panel_top))
+ && moderatorPermissions.topQuestion > Permissions.GUEST
+ ) {
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_top_title),
+ R.drawable.icon_more_panel_top
+ )
+ )
} else if (questionEntity.me.isModerator
&& questionEntity.me.isCommunityTop
- && moderatorPermissions.cancelTopQuestion > Permissions.GUEST) {
- entities.add(MenuItemEntity(getString(R.string.article_detail_more_cancel_top_title), R.drawable.icon_more_panel_top_cancel))
+ && moderatorPermissions.cancelTopQuestion > Permissions.GUEST
+ ) {
+ entities.add(
+ MenuItemEntity(
+ getString(R.string.article_detail_more_cancel_top_title),
+ R.drawable.icon_more_panel_top_cancel
+ )
+ )
}
MoreFunctionPanelDialog.showMoreDialog(
@@ -682,8 +695,7 @@ class NewQuestionDetailFragment :
"投诉" -> {
ifLogin("提问贴") {
BbsReportHelper.showReportDialog(
- mViewModel.questionDetail?.id
- ?: ""
+ BbsReportHelper.PostsReporter(mViewModel.questionDetail?.id ?: "")
)
}
NewLogUtils.logSharePanelClick(
@@ -695,6 +707,7 @@ class NewQuestionDetailFragment :
mViewModel.questionDetail?.community?.typeChinese ?: "综合论坛"
)
}
+
"编辑" -> {
val intent = if (questionEntity.me.isModerator) {
QuestionEditActivity.getManagerIntent(
@@ -737,6 +750,7 @@ class NewQuestionDetailFragment :
mViewModel.questionDetail?.community?.typeChinese ?: "综合论坛"
)
}
+
"解决", "已解决" -> {
val content =
if (!questionEntity.finish) "该问题确定标记已解决?" else "该问题确定标记未解决?"
@@ -752,6 +766,7 @@ class NewQuestionDetailFragment :
mViewModel.questionDetail?.community?.typeChinese ?: "综合论坛"
)
}
+
getString(R.string.article_detail_more_top_title) -> {
TopCommunityCategoryDialog.show(
childFragmentManager
@@ -759,6 +774,7 @@ class NewQuestionDetailFragment :
mViewModel.topCommunityQuestion(category.id)
}
}
+
getString(R.string.article_detail_more_cancel_top_title) -> {
DialogHelper.showDialog(
requireContext(),
diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt
index 9d1fcd49e2..57d3634957 100644
--- a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt
@@ -171,7 +171,8 @@ class ForumVideoDetailFragment : BaseLazyTabFragment() {
mVideoId = arguments?.getString(EntranceConsts.KEY_VIDEO_ID) ?: ""
mBbsId = arguments?.getString(EntranceConsts.KEY_BBS_ID) ?: ""
mTopCommentId = arguments?.getString(EntranceConsts.KEY_TOP_COMMENT_ID) ?: ""
- mBasicExposureSourceList = arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) ?: arrayListOf()
+ mBasicExposureSourceList =
+ arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) ?: arrayListOf()
sourceEntrance = arguments?.getString(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: ""
super.onCreate(savedInstanceState)
NewLogUtils.logVideoDetailClick("view_video_detail")
@@ -189,7 +190,8 @@ class ForumVideoDetailFragment : BaseLazyTabFragment() {
}
}
if (mIsFromMainWrapper) {
- (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = DisplayUtils.getStatusBarHeight(requireContext().resources)
+ (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin =
+ DisplayUtils.getStatusBarHeight(requireContext().resources)
}
mViewModel = viewModelProviderFromParent(
ForumVideoDetailViewModel.Factory(
@@ -704,7 +706,7 @@ class ForumVideoDetailFragment : BaseLazyTabFragment() {
}
"投诉" -> {
- BbsReportHelper.showReportDialog(mForumVideoEntity?.id ?: "")
+ BbsReportHelper.showReportDialog(BbsReportHelper.PostsReporter(mForumVideoEntity?.id ?: ""))
NewLogUtils.logSharePanelClick(
"click_report",
mForumVideoEntity?.user?.id ?: "",
@@ -746,9 +748,11 @@ class ForumVideoDetailFragment : BaseLazyTabFragment() {
)
}
}
+
"取消精选" -> {
showHighlightDialog(false)
}
+
"修改活动标签" -> {
ChooseActivityDialogFragment.show(
requireActivity() as AppCompatActivity,
@@ -758,6 +762,7 @@ class ForumVideoDetailFragment : BaseLazyTabFragment() {
tag ?: ""
)
}
+
"删除", "隐藏" -> {
DialogHelper.showDialog(
requireContext(),
@@ -770,7 +775,7 @@ class ForumVideoDetailFragment : BaseLazyTabFragment() {
)
NewLogUtils.logSharePanelClick(
"click_delete",
- mForumVideoEntity?.user?.id ?: "",
+ mForumVideoEntity?.user?.id ?: "",
"视频帖",
mForumVideoEntity?.id ?: "",
mForumVideoEntity?.bbs?.id ?: "",
@@ -785,6 +790,7 @@ class ForumVideoDetailFragment : BaseLazyTabFragment() {
mViewModel.topCommunityVideo(category.id)
}
}
+
getString(R.string.article_detail_more_cancel_top_title) -> {
DialogHelper.showDialog(
requireContext(),
diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java
index 4fee92cc4c..bb2d360b3f 100644
--- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java
+++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java
@@ -55,6 +55,7 @@ import com.gh.gamecenter.entity.GamesCollectionEntity;
import com.gh.gamecenter.entity.HaloAddonEntity;
import com.gh.gamecenter.entity.HomeGameCollectionEntity;
import com.gh.gamecenter.entity.HomeItemTestV2Entity;
+import com.gh.gamecenter.entity.ImageArticleEntity;
import com.gh.gamecenter.entity.ImageInfoEntity;
import com.gh.gamecenter.entity.InterestedGameEntity;
import com.gh.gamecenter.entity.LibaoDetailEntity;
@@ -66,6 +67,7 @@ import com.gh.gamecenter.entity.NewsDetailEntity;
import com.gh.gamecenter.entity.PackageFilter;
import com.gh.gamecenter.entity.PackageGame;
import com.gh.gamecenter.entity.PersonalHistoryEntity;
+import com.gh.gamecenter.entity.PublishImageTextRequest;
import com.gh.gamecenter.entity.PullDownPush;
import com.gh.gamecenter.entity.Rating;
import com.gh.gamecenter.entity.RatingComment;
@@ -154,6 +156,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.Single;
import okhttp3.RequestBody;
@@ -3425,4 +3428,195 @@ public interface ApiService {
@GET("game_lists/search")
Single> searchGameList(@Query("keyword") String keyword, @Query("page") int page);
+
+ /**
+ * 发布图文
+ */
+ @POST("communities/image_articles")
+ Single publishImageText(@Body PublishImageTextRequest request);
+
+ /**
+ * 编辑图文
+ */
+ @PUT("communities/image_articles/{id}")
+ Completable editImageArticle(@Path("id") String id, @Body PublishImageTextRequest request);
+
+
+ /**
+ * 新建草稿
+ */
+ @POST("users/{user_id}/communities/image_article_drafts")
+ Single createImageArticleDrafts(@Path("user_id") String userId, @Body PublishImageTextRequest request);
+
+ /**
+ * 编辑草稿
+ */
+ @PUT("users/{user_id}/communities/image_article_drafts/{id}")
+ Completable editImageArticleDrafts(@Path("user_id") String userId, @Path("id") String draftId, @Body PublishImageTextRequest request);
+
+ /**
+ * 草稿箱
+ */
+ @GET("users/{user_id}/communities/image_article_drafts")
+ Single> loadImageTextDrafts(@Path("user_id") String userId, @Query("version") String version, @Query("channel") String channel);
+
+ /**
+ * 删除草稿
+ */
+ @DELETE("users/{user_id}/communities/image_article_drafts/{id}")
+ Completable deleteImageArticleDraft(@Path("user_id") String userId, @Path("id") String id);
+
+ /**
+ * 图文详情
+ */
+ @GET("communities/image_articles/{id}")
+ Single loadImageArticleDetail(
+ @Path("id") String imageArticleId,
+ @Query("view") String view,
+ @Query("version") String version,
+ @Query("channel") String channel);
+
+ /**
+ * 推荐-图文列表
+ */
+ @GET("bbses/image_article_recommends")
+ Single> loadImageArticleRecommends(
+ @Query("sort") String sort,
+ @Query("page") int page,
+ @Query("version") String version,
+ @Query("channel") String channel);
+
+ /**
+ * 评论图文
+ */
+ @POST("communities/image_articles/{article_id}/comments")
+ Observable postImageArticleComment(@Path("article_id") String articleId, @Body RequestBody body);
+
+ /**
+ * 回复图文评论
+ */
+ @POST("communities/image_articles/comments/{comment_id}:reply")
+ Observable postReplyToImageArticleComment(@Path("comment_id") String commentId, @Body RequestBody body);
+
+ /**
+ * 图文详情评论列表
+ */
+ @GET("communities/image_articles/{article_id}/comments")
+ Observable> getImageArticleComments(@Path("article_id") String articleId,
+ @Query("sort") String type,
+ @Query("page") int page,
+ @QueryMap Map params);
+
+ /**
+ * 图文评论详情
+ */
+ @GET("communities/image_articles/comments/{comment_id}")
+ Single getImageArticleCommentDetail(
+ @Path("comment_id") String commentId,
+ @Query("version") String version,
+ @Query("channel") String channel,
+ @Query("timestamp") long timeStamp);
+
+ /**
+ * 图文详情回复列表
+ */
+ @GET("communities/image_articles/comments/{comment_id}/replies")
+ Single> getImageArticleDetailCommentReply(
+ @Path("comment_id") String commentId,
+ @Query("sort") String sort,
+ @Query("page") int page,
+ @Query("version") String version,
+ @Query("channel") String channel,
+ @Query("timestamp") long timestamp);
+
+ /**
+ * 投诉图文评论
+ */
+ @POST("communities/image_articles/comments/{comment_id}:report")
+ Observable reportImageArticleComment(@Path("comment_id") String commentId, @Body RequestBody body);
+
+ /**
+ * 删除图文评论
+ */
+ @POST("communities/image_articles/comments/{comment_id}:hide")
+ Observable deleteImageArticleComment(@Path("comment_id") String commendId);
+
+ /**
+ * 收藏图文
+ */
+ @POST("users/favorites/communities/image_articles/{id}")
+ Single collectImageArticle(@Path("id") String id);
+
+ /**
+ * 取消收藏图文
+ */
+ @DELETE("users/favorites/communities/image_articles/{id}")
+ Completable cancelImageArticleCollection(@Path("id") String id);
+
+ /**
+ * 点赞图文
+ */
+ @POST("communities/image_articles/{id}:vote")
+ Single voteArticleImage(@Path("id") String id);
+
+ /**
+ * 取消点赞图文
+ */
+ @POST("communities/image_articles/{id}:unvote")
+ Single unVoteArticleImage(@Path("id") String id);
+
+ /**
+ * 置顶图文
+ */
+ @POST("communities/image_articles/comments/{comment_id}:set-top")
+ Observable postImageArticleTop(@Path("comment_id") String commentId, @QueryMap Map params);
+
+ /**
+ * 取消置顶图文
+ */
+ @POST("communities/image_articles/comments/{comment_id}:unset-top")
+ Observable postImageArticleUnTop(@Path("comment_id") String commentId);
+
+ /**
+ * 点赞图文
+ */
+ @POST("communities/image_articles/comments/{comment_id}:vote")
+ Observable postVoteToImageArticle(@Path("comment_id") String commentId);
+
+ /**
+ * 取消点赞图文
+ */
+ @POST("communities/image_articles/comments/{comment_id}:unvote")
+ Observable postUnVoteImageArticle(@Path("comment_id") String commentId);
+
+ /**
+ * 图文申请加精
+ */
+ @POST("communities/image_articles/{id}:choiceness")
+ Completable applyHighlightForImageArticle(@Path("id") String id);
+
+ /**
+ * 图文加精选
+ */
+ @POST("communities/image_articles/{id}:moderator_choiceness")
+ Single addHighlightForImageArticle(@Path("id") String imageArticleId);
+
+ /**
+ * 图文取消加精
+ */
+ @POST("communities/image_articles/{id}:cancel_choiceness")
+ Single cancelHighlightForImageArticle(@Path("id") String imageArticleId);
+
+ /**
+ * 删除/隐藏图文
+ */
+ @POST("communities/image_articles/{id}:hide")
+ Single deleteOrHideImageArticle(@Path("id") String imageArticleId);
+
+ /**
+ * 投诉图文
+ */
+ @POST("bbses/contents/{content_id}:report")
+ Single postImageArticleReport(@Path("content_id") String contentId, @Body RequestBody body);
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt b/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt
index ffe12e0ef1..8f1ac12243 100644
--- a/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt
@@ -94,7 +94,8 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- mBasicExposureSourceList = arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) ?: arrayListOf()
+ mBasicExposureSourceList =
+ arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) ?: arrayListOf()
mInitialVideoId = arguments?.getString(EntranceConsts.KEY_ID) ?: ""
mLocation = arguments?.getString(EntranceConsts.KEY_LOCATION) ?: ""
mReferer = arguments?.getString(EntranceConsts.KEY_REFERER) ?: ""
@@ -154,7 +155,8 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener {
mBinding.recyclerview.visibility = View.VISIBLE
mBinding.attentionNoDataContainer.visibility = View.GONE
if (!::mAdapter.isInitialized) {
- mAdapter = VideoAdapter(this, mBinding.recyclerview, mBinding.refresh, mViewModel, mBasicExposureSourceList)
+ mAdapter =
+ VideoAdapter(this, mBinding.recyclerview, mBinding.refresh, mViewModel, mBasicExposureSourceList)
mExposureListener = ExposureListener(this, mAdapter)
mBinding.recyclerview.addOnScrollListener(mExposureListener!!)
mBinding.recyclerview.adapter = mAdapter
@@ -574,6 +576,7 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener {
CheckLoginUtils.checkLogin(context, "(视频详情)") {}
}
}
+
"取消收藏" -> {
if (UserManager.getInstance().isLoggedIn) {
MtaHelper.onEvent("视频详情", "更多-取消收藏", combinedTitleAndId)
@@ -587,9 +590,10 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener {
CheckLoginUtils.checkLogin(context, "(视频详情)") {}
}
}
+
"投诉" -> {
ifLogin("视频详情") {
- BbsReportHelper.showReportDialog(videoEntity.id)
+ BbsReportHelper.showReportDialog(BbsReportHelper.PostsReporter(videoEntity.id))
}
}
}
diff --git a/app/src/main/res/drawable-xxxhdpi/community_edit_recommend.webp b/app/src/main/res/drawable-xxxhdpi/community_edit_recommend.webp
new file mode 100644
index 0000000000..f79227aa15
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/community_edit_recommend.webp differ
diff --git a/app/src/main/res/drawable/bg_image_article_detail_indicator.xml b/app/src/main/res/drawable/bg_image_article_detail_indicator.xml
new file mode 100644
index 0000000000..7855fb3622
--- /dev/null
+++ b/app/src/main/res/drawable/bg_image_article_detail_indicator.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/btn_fab_container__1__1.xml b/app/src/main/res/drawable/btn_fab_container__1__1.xml
new file mode 100644
index 0000000000..1e704d174a
--- /dev/null
+++ b/app/src/main/res/drawable/btn_fab_container__1__1.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_community_float.xml b/app/src/main/res/drawable/ic_community_float.xml
new file mode 100644
index 0000000000..567ad0e612
--- /dev/null
+++ b/app/src/main/res/drawable/ic_community_float.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_image_and_text_add.xml b/app/src/main/res/drawable/ic_image_and_text_add.xml
new file mode 100644
index 0000000000..b73d876b58
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_and_text_add.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_image_and_text_arr_right.xml b/app/src/main/res/drawable/ic_image_and_text_arr_right.xml
new file mode 100644
index 0000000000..143ccf260e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_and_text_arr_right.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_image_and_text_bbs.xml b/app/src/main/res/drawable/ic_image_and_text_bbs.xml
new file mode 100644
index 0000000000..a606231d72
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_and_text_bbs.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_image_and_text_clear.xml b/app/src/main/res/drawable/ic_image_and_text_clear.xml
new file mode 100644
index 0000000000..f8d12adbfe
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_and_text_clear.xml
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_image_and_text_drag_tips.xml b/app/src/main/res/drawable/ic_image_and_text_drag_tips.xml
new file mode 100644
index 0000000000..5384506125
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_and_text_drag_tips.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_image_and_text_right.xml b/app/src/main/res/drawable/ic_image_and_text_right.xml
new file mode 100644
index 0000000000..03cc75d79c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_and_text_right.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_image_article_detail_go_bbs.xml b/app/src/main/res/drawable/ic_image_article_detail_go_bbs.xml
new file mode 100644
index 0000000000..6d452a33f4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_article_detail_go_bbs.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_image_article_draft.xml b/app/src/main/res/drawable/ic_image_article_draft.xml
new file mode 100644
index 0000000000..2c363cee5a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_article_draft.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_image_article_liked.xml b/app/src/main/res/drawable/ic_image_article_liked.xml
new file mode 100644
index 0000000000..d0fdd03374
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_article_liked.xml
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_image_article_unlike.xml b/app/src/main/res/drawable/ic_image_article_unlike.xml
new file mode 100644
index 0000000000..f5a4868ec3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_article_unlike.xml
@@ -0,0 +1,17 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/selector_recommend_home_vote.xml b/app/src/main/res/drawable/selector_recommend_home_vote.xml
new file mode 100644
index 0000000000..8ab2c6acff
--- /dev/null
+++ b/app/src/main/res/drawable/selector_recommend_home_vote.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_image_and_text_cover.xml b/app/src/main/res/drawable/shape_image_and_text_cover.xml
new file mode 100644
index 0000000000..8edc204102
--- /dev/null
+++ b/app/src/main/res/drawable/shape_image_and_text_cover.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_skeleton_circle.xml b/app/src/main/res/drawable/shape_skeleton_circle.xml
new file mode 100644
index 0000000000..dc8c0eddac
--- /dev/null
+++ b/app/src/main/res/drawable/shape_skeleton_circle.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_skeleton_radius_4.xml b/app/src/main/res/drawable/shape_skeleton_radius_4.xml
new file mode 100644
index 0000000000..3f0c7b07ba
--- /dev/null
+++ b/app/src/main/res/drawable/shape_skeleton_radius_4.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_skeleton_radius_8_only_top.xml b/app/src/main/res/drawable/shape_skeleton_radius_8_only_top.xml
new file mode 100644
index 0000000000..95dbfa730a
--- /dev/null
+++ b/app/src/main/res/drawable/shape_skeleton_radius_8_only_top.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_publish_image_article.xml b/app/src/main/res/layout/activity_publish_image_article.xml
new file mode 100644
index 0000000000..1cbd5ebe0f
--- /dev/null
+++ b/app/src/main/res/layout/activity_publish_image_article.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/community_edit_window.xml b/app/src/main/res/layout/community_edit_window.xml
index d8d7611277..4ee7b1ade5 100644
--- a/app/src/main/res/layout/community_edit_window.xml
+++ b/app/src/main/res/layout/community_edit_window.xml
@@ -60,6 +60,34 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_forum_detail.xml b/app/src/main/res/layout/fragment_forum_detail.xml
index 200785d435..5bbcf2e0fb 100644
--- a/app/src/main/res/layout/fragment_forum_detail.xml
+++ b/app/src/main/res/layout/fragment_forum_detail.xml
@@ -432,17 +432,13 @@
diff --git a/app/src/main/res/layout/fragment_image_article_detail.xml b/app/src/main/res/layout/fragment_image_article_detail.xml
new file mode 100644
index 0000000000..73a1b014f4
--- /dev/null
+++ b/app/src/main/res/layout/fragment_image_article_detail.xml
@@ -0,0 +1,524 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_image_article_draft.xml b/app/src/main/res/layout/fragment_image_article_draft.xml
new file mode 100644
index 0000000000..9de91345f9
--- /dev/null
+++ b/app/src/main/res/layout/fragment_image_article_draft.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_image_article_home.xml b/app/src/main/res/layout/fragment_image_article_home.xml
new file mode 100644
index 0000000000..500d8a8d63
--- /dev/null
+++ b/app/src/main/res/layout/fragment_image_article_home.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_image_article_home_skeleton.xml b/app/src/main/res/layout/fragment_image_article_home_skeleton.xml
new file mode 100644
index 0000000000..5b8ad76c05
--- /dev/null
+++ b/app/src/main/res/layout/fragment_image_article_home_skeleton.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_image_article_request_permission.xml b/app/src/main/res/layout/fragment_image_article_request_permission.xml
new file mode 100644
index 0000000000..f225944eeb
--- /dev/null
+++ b/app/src/main/res/layout/fragment_image_article_request_permission.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_publish_image_article.xml b/app/src/main/res/layout/fragment_publish_image_article.xml
new file mode 100644
index 0000000000..31191dec58
--- /dev/null
+++ b/app/src/main/res/layout/fragment_publish_image_article.xml
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_user_publish.xml b/app/src/main/res/layout/fragment_user_publish.xml
index b57f4b88de..4590116837 100644
--- a/app/src/main/res/layout/fragment_user_publish.xml
+++ b/app/src/main/res/layout/fragment_user_publish.xml
@@ -53,6 +53,13 @@
android:text="全部"
android:textColor="@color/text_theme" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_skeleton_recommend_home_2.xml b/app/src/main/res/layout/layout_skeleton_recommend_home_2.xml
new file mode 100644
index 0000000000..3412b3374f
--- /dev/null
+++ b/app/src/main/res/layout/layout_skeleton_recommend_home_2.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_skeleton_recommend_home_3.xml b/app/src/main/res/layout/layout_skeleton_recommend_home_3.xml
new file mode 100644
index 0000000000..c467c66ee0
--- /dev/null
+++ b/app/src/main/res/layout/layout_skeleton_recommend_home_3.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_vote.xml b/app/src/main/res/layout/layout_vote.xml
new file mode 100644
index 0000000000..343864b1ab
--- /dev/null
+++ b/app/src/main/res/layout/layout_vote.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/recycler_image_and_text.xml b/app/src/main/res/layout/recycler_image_and_text.xml
new file mode 100644
index 0000000000..4b4cf86824
--- /dev/null
+++ b/app/src/main/res/layout/recycler_image_and_text.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/recycler_image_article_detail_banner.xml b/app/src/main/res/layout/recycler_image_article_detail_banner.xml
new file mode 100644
index 0000000000..9eb84dc804
--- /dev/null
+++ b/app/src/main/res/layout/recycler_image_article_detail_banner.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/recycler_image_article_draft.xml b/app/src/main/res/layout/recycler_image_article_draft.xml
new file mode 100644
index 0000000000..12577829b3
--- /dev/null
+++ b/app/src/main/res/layout/recycler_image_article_draft.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/recycler_recommend_home.xml b/app/src/main/res/layout/recycler_recommend_home.xml
new file mode 100644
index 0000000000..e26f60b4d7
--- /dev/null
+++ b/app/src/main/res/layout/recycler_recommend_home.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 86bfa927e1..a9ae8a5454 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -60,6 +60,7 @@
首頁
我的光環
說點什麽吧
+ 說點什麽吧~
發表評論...
用戶協議
隱私政策
@@ -649,5 +650,44 @@
我的遊戲
玩過
預約
+ 讚
+ 圖文動態
+ 發圖文
+ 填寫標題可以讓更多人看見哦~
+ 在這裡展開說說你的想法吧
+ 發布圖文動態
+ 發佈到論壇
+ 封面
+ 至少發布一張圖片哦
+ 標題最多20個字哦
+ 正文最多1000個字哦
+ 選擇子版塊
+ 圖文草稿
+ 儲存至草稿箱並退出?
+ 不保存
+ 儲存並退出
+ 警
+ 確定要刪除該圖文草稿嗎?刪除之後不可恢復
+ 正序
+ 倒序
+ 發布後不可修改論壇~
+ 草稿保存成功
+ 草稿保存失敗
+ 草稿保存中
+ 圖文發布中
+ 發布圖文成功
+ 發布圖文失敗
+ 圖文%1$s
+ 圖文
+ 已收藏
+ 提交成功
+ 提交失敗
+ 權限錯誤
+ 權限錯誤,請刷新後重試
+ 操作成功
+ 已删除
+ 已隱藏
+ %1$s萬
+ https://m.ghzs666.com/bbs/note-%1$s
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index fb05df8772..7ac405b95c 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -88,4 +88,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b6b1377527..63727d3956 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -60,6 +60,7 @@
首页
我的光环
说点什么吧
+ 说点什么吧~
发表评论...
用户协议
隐私政策
@@ -649,4 +650,43 @@
我的游戏
玩过
预约
+ 赞
+ 图文动态
+ 发图文
+ 填写标题可以让更多人看见哦~
+ 在这里展开说说你的想法吧
+ 发布图文动态
+ 发布到论坛
+ 封面
+ 至少发布一张图片哦
+ 标题最多20个字哦
+ 正文最多1000个字哦
+ 选择子版块
+ 图文草稿
+ 是否保存至草稿箱再退出?
+ 不保存
+ 保存并退出
+ 警告
+ 确定要删除该图文草稿吗?删除之后不可恢复
+ 正序
+ 倒序
+ 发布后不可修改论坛~
+ 草稿保存成功
+ 草稿保存失败
+ 草稿保存中
+ 图文发布中
+ 发布图文成功
+ 发布图文失败
+ 图文%1$s
+ 图文
+ 已收藏
+ 提交成功
+ 提交失败
+ 权限错误
+ 权限错误,请刷新后重试
+ 操作成功
+ 已删除
+ 已隐藏
+ %1$s万
+ https://m.ghzs666.com/bbs/note-%1$s
diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java
index fffb9bc9b4..9870cb6b3f 100644
--- a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java
+++ b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java
@@ -138,6 +138,7 @@ public abstract class BaseFragment extends Fragment implements OnRequestCallB
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ System.out.println("kayn -->onCreate:"+this.getClass().getSimpleName());
final Intent intent = getActivity().getIntent();
mEntrance = intent.getStringExtra(KEY_ENTRANCE);
if (getArguments() != null) {
diff --git a/module_common/src/main/java/com/gh/gamecenter/common/baselist/ListAdapter.java b/module_common/src/main/java/com/gh/gamecenter/common/baselist/ListAdapter.java
index e10603d79b..d0e8d1bfa1 100644
--- a/module_common/src/main/java/com/gh/gamecenter/common/baselist/ListAdapter.java
+++ b/module_common/src/main/java/com/gh/gamecenter/common/baselist/ListAdapter.java
@@ -9,6 +9,7 @@ import com.lightgame.adapter.BaseRecyclerAdapter;
import java.util.ArrayList;
import java.util.List;
+import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
/**
diff --git a/module_common/src/main/java/com/gh/gamecenter/common/baselist/PageLoader.kt b/module_common/src/main/java/com/gh/gamecenter/common/baselist/PageLoader.kt
new file mode 100644
index 0000000000..d837ea2eb6
--- /dev/null
+++ b/module_common/src/main/java/com/gh/gamecenter/common/baselist/PageLoader.kt
@@ -0,0 +1,126 @@
+package com.gh.gamecenter.common.baselist
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.gh.gamecenter.common.retrofit.BiResponse
+import com.gh.gamecenter.common.utils.singleToMain
+import io.reactivex.Single
+import io.reactivex.disposables.CompositeDisposable
+
+/**
+ * 分页加载简易封装,只处理分页逻辑和页面状态
+ */
+class PageLoader(
+ private val pageSize: Int = PAGE_SIZE_DEFAULT,
+ private val compositeDisposable: CompositeDisposable = CompositeDisposable(),
+ private val load: (Int, Int) -> Single>
+) {
+
+ private var _pageNo = 1
+ val pageNo: Int
+ get() = _pageNo
+
+ private val _pageState = MutableLiveData()
+ val pageState: LiveData = _pageState
+
+ private val _dataList = MutableLiveData>()
+ val dataList: LiveData> = _dataList
+ fun updateData(newData: MutableList) {
+ _dataList.value = newData
+ }
+
+ fun loadInit() {
+ _pageState.value = PageState.PageInitLoading
+ load(pageNo, pageSize)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse>() {
+ override fun onSuccess(data: List