Merge branch 'sync_page_data'
This commit is contained in:
@ -19,6 +19,9 @@ import androidx.fragment.app.FragmentTransaction;
|
||||
import com.gh.base.OnListClickListener;
|
||||
import com.gh.base.OnRequestCallBackListener;
|
||||
import com.gh.common.constant.Constants;
|
||||
import com.gh.common.syncpage.ISyncAdapterHandler;
|
||||
import com.gh.common.syncpage.SyncDataEntity;
|
||||
import com.gh.common.syncpage.SyncPageRepository;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.eventbus.EBMiPush;
|
||||
import com.lightgame.OnTitleClickListener;
|
||||
@ -32,10 +35,18 @@ import org.greenrobot.eventbus.ThreadMode;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.ButterKnife;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import kotlin.Pair;
|
||||
|
||||
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
|
||||
|
||||
@ -142,6 +153,36 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
ButterKnife.bind(this, mCachedView);
|
||||
|
||||
initView(mCachedView);
|
||||
|
||||
if (addSyncPageObserver()) {
|
||||
initSyncPageObserver();
|
||||
}
|
||||
}
|
||||
|
||||
private void initSyncPageObserver() {
|
||||
SyncPageRepository.INSTANCE.getSyncPageLiveData().observe(this, syncDataEntities -> {
|
||||
try {
|
||||
if (syncDataEntities == null || syncDataEntities.isEmpty()) return;
|
||||
RecyclerView.Adapter adapter = provideSyncAdapter();
|
||||
if (!(adapter instanceof ISyncAdapterHandler)) return;
|
||||
for (int i = 0; i < adapter.getItemCount(); i++) {
|
||||
Pair<String, Object> syncKey = ((ISyncAdapterHandler) adapter).getSyncData(i);
|
||||
if (syncKey == null) return;
|
||||
for (SyncDataEntity syncDataEntity : syncDataEntities) {
|
||||
if (syncDataEntity.getSyncId().equals(syncKey.getFirst())) {
|
||||
boolean isSuccess = SyncPageRepository.INSTANCE.handleSyncData(syncKey.getSecond(), syncDataEntity);
|
||||
if (isSuccess) adapter.notifyItemChanged(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
throw e;
|
||||
} else {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 必须的有subscribe才能register
|
||||
@ -252,4 +293,12 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected RecyclerView.Adapter provideSyncAdapter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean addSyncPageObserver() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
12
app/src/main/java/com/gh/common/annotation/SyncPage.java
Normal file
12
app/src/main/java/com/gh/common/annotation/SyncPage.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.gh.common.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SyncPage {
|
||||
String[] syncNames();
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.gh.common.syncpage
|
||||
|
||||
interface ISyncAdapterHandler {
|
||||
|
||||
/**
|
||||
* @param position position to query
|
||||
* @return Pair first: item sync id
|
||||
* Pair second: item data entity
|
||||
*/
|
||||
fun getSyncData(position: Int): Pair<String, Any>?
|
||||
|
||||
}
|
||||
43
app/src/main/java/com/gh/common/syncpage/SyncDataEntity.kt
Normal file
43
app/src/main/java/com/gh/common/syncpage/SyncDataEntity.kt
Normal file
@ -0,0 +1,43 @@
|
||||
package com.gh.common.syncpage
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class SyncDataEntity(
|
||||
/**
|
||||
* 标识一条数据的唯一ID
|
||||
*
|
||||
* 与[ISyncAdapterHandler.getSyncData]返回的Pair first一致
|
||||
*/
|
||||
val syncId: String,
|
||||
|
||||
/**
|
||||
* 需要同步的字段名
|
||||
*
|
||||
* 与@SyncPage注解的值一致
|
||||
*/
|
||||
val syncFieldName: String?,
|
||||
|
||||
/**
|
||||
* 需要同步的具体内容
|
||||
*/
|
||||
val syncFieldValue: Any?,
|
||||
|
||||
/**
|
||||
* 同步完一次是否自动删除
|
||||
*/
|
||||
val remove: Boolean = true,
|
||||
|
||||
/**
|
||||
* 是否需要查询同步实体的父级字段
|
||||
*
|
||||
* 由于反射可能会导致较大的性能消耗,默认关闭,具体按实际情况开启
|
||||
*/
|
||||
val checkInherited: Boolean = false,
|
||||
|
||||
/**
|
||||
* 是否需要查询同步实体的嵌套实体内容
|
||||
*
|
||||
* 由于反射可能会导致较大的性能消耗,默认关闭,具体按实际情况开启
|
||||
*/
|
||||
val checkFieldEntity: Boolean = false)
|
||||
@ -0,0 +1,17 @@
|
||||
package com.gh.common.syncpage
|
||||
|
||||
object SyncFieldConstants {
|
||||
|
||||
// 是否点赞
|
||||
const val ANSWER_VOTE = "ANSWER_VOTE"
|
||||
const val ARTICLE_VOTE = "ARTICLE_VOTE"
|
||||
|
||||
// 赞同数量
|
||||
const val ANSWER_VOTE_COUNT = "ANSWER_VOTE_COUNT"
|
||||
const val ARTICLE_VOTE_COUNT = "ARTICLE_VOTE_COUNT"
|
||||
|
||||
// 评论数量
|
||||
const val ANSWER_COMMENT_COUNT = "ANSWER_COMMENT_COUNT"
|
||||
const val ARTICLE_COMMENT_COUNT = "ARTICLE_COMMENT_COUNT"
|
||||
|
||||
}
|
||||
116
app/src/main/java/com/gh/common/syncpage/SyncPageRepository.kt
Normal file
116
app/src/main/java/com/gh/common/syncpage/SyncPageRepository.kt
Normal file
@ -0,0 +1,116 @@
|
||||
package com.gh.common.syncpage
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.annotation.SyncPage
|
||||
import com.gh.common.util.toJson
|
||||
import com.gh.common.util.tryCatchInRelease
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import java.lang.reflect.Field
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
/**
|
||||
* 页面之间实现数据同步(主要是单个字段的同步)
|
||||
*
|
||||
* 实现思路:
|
||||
* 1.把需要同步的数据以一个特殊ID作为唯一标识,并存于一个全局的仓库
|
||||
* 2.利用LiveData进行页面回调,再通过[ISyncAdapterHandler.getSyncData]找到需要被同步的数据
|
||||
* 3.最后利用反射进行数据替换,具体请见[SyncPageRepository.replaceSyncData]
|
||||
*
|
||||
* 具体的接入方式(以列表为例):
|
||||
* 1.通过[SyncPageRepository.postSyncData]提交同步数据
|
||||
* 2.在Fragment重写[BaseFragment.addSyncPageObserver]开启同步事件监听
|
||||
* 3.在Fragment重写[BaseFragment.provideSyncAdapter]提供获取数据的来源
|
||||
* - [BaseFragment.provideSyncAdapter]提供的Adapter必须实现[ISyncAdapterHandler]接口
|
||||
*/
|
||||
object SyncPageRepository {
|
||||
|
||||
// 只有在新增操作时,才需要进行页面刷新
|
||||
val syncPageLiveData = MutableLiveData<List<SyncDataEntity>>()
|
||||
|
||||
val syncDataList = CopyOnWriteArrayList<SyncDataEntity>()
|
||||
|
||||
@JvmStatic
|
||||
fun clearSyncData() {
|
||||
tryCatchInRelease {
|
||||
syncDataList.clear()
|
||||
}
|
||||
}
|
||||
|
||||
// 提交同步数据
|
||||
fun postSyncData(entity: SyncDataEntity) {
|
||||
tryCatchInRelease {
|
||||
// 检查是否存在重复操作
|
||||
for (syncDataEntity in syncDataList) {
|
||||
if (syncDataEntity.syncId == entity.syncId && syncDataEntity.syncFieldName == entity.syncFieldName) {
|
||||
syncDataList.remove(syncDataEntity)
|
||||
syncDataList.add(entity)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
syncDataList.add(entity)
|
||||
syncPageLiveData.postValue(syncDataList)
|
||||
|
||||
Utils.log("SyncPageRepository postSyncData->" + entity.toJson())
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSyncData(rawData: Any, syncData: SyncDataEntity): Boolean {
|
||||
val fields = if (syncData.checkInherited) {
|
||||
getAllFieldsList(rawData::class.java)
|
||||
} else rawData::class.java.declaredFields.toList()
|
||||
|
||||
var isNeedNotify = false
|
||||
for (field in fields) {
|
||||
isNeedNotify = replaceSyncData(field, rawData, syncData)
|
||||
if (isNeedNotify) break
|
||||
}
|
||||
|
||||
if (syncData.remove) syncDataList.remove(syncData)
|
||||
|
||||
if (!isNeedNotify) {
|
||||
Utils.log("SyncPageRepository sync failure-> " + syncData.syncFieldName)
|
||||
} else {
|
||||
Utils.log("SyncPageRepository sync success-> " + syncData.syncFieldName)
|
||||
}
|
||||
|
||||
return isNeedNotify
|
||||
}
|
||||
|
||||
private fun replaceSyncData(field: Field, extractObject: Any, syncData: SyncDataEntity): Boolean {
|
||||
field.getAnnotation(SyncPage::class.java)?.syncNames?.forEach { syncName ->
|
||||
if (syncName == syncData.syncFieldName) {
|
||||
field.isAccessible = true
|
||||
field.set(extractObject, syncData.syncFieldValue)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (syncData.checkFieldEntity) {
|
||||
// 递归查询
|
||||
val pkgName = field.type.getPackage()?.name ?: return false
|
||||
if (pkgName.contains(HaloApp.getInstance().application.packageName)) {
|
||||
field.isAccessible = true
|
||||
val extractEntityObject = field.get(extractObject) ?: return false
|
||||
field.type.declaredFields.forEach {
|
||||
if (replaceSyncData(it, extractEntityObject, syncData)) return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 包括父类 Field 对象
|
||||
private fun getAllFieldsList(cls: Class<*>?): List<Field> {
|
||||
if (cls == null) return arrayListOf()
|
||||
val allFields = arrayListOf<Field>()
|
||||
var currentClass = cls
|
||||
while (currentClass != null) {
|
||||
val declaredFields = currentClass.declaredFields
|
||||
allFields.addAll(declaredFields)
|
||||
currentClass = currentClass.superclass
|
||||
}
|
||||
return allFields
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
package com.gh.common.syncpage.example
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.constant.ItemViewType
|
||||
import com.gh.common.syncpage.ISyncAdapterHandler
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.qa.answer.CommunityAnswerItemViewHolder
|
||||
import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
|
||||
import com.gh.gamecenter.qa.entity.AnswerEntity
|
||||
import com.gh.gamecenter.qa.entity.Questions
|
||||
import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity
|
||||
|
||||
class ExampleAdapter(context: Context) : ListAdapter<AnswerEntity>(context), ISyncAdapterHandler {
|
||||
|
||||
override fun areItemsTheSame(oldItem: AnswerEntity?, newItem: AnswerEntity?): Boolean {
|
||||
return oldItem?.id == newItem?.id
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (position == itemCount - 1) return ItemViewType.ITEM_FOOTER
|
||||
return ItemViewType.ITEM_BODY
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val view: View
|
||||
return when (viewType) {
|
||||
ItemViewType.ITEM_FOOTER -> {
|
||||
view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)
|
||||
FooterViewHolder(view)
|
||||
}
|
||||
else -> {
|
||||
view = mLayoutInflater.inflate(R.layout.community_answer_item, parent, false)
|
||||
CommunityAnswerItemViewHolder(CommunityAnswerItemBinding.bind(view))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return if (mEntityList.isNotEmpty()) mEntityList.size + FOOTER_ITEM_COUNT else 0
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (getItemViewType(position)) {
|
||||
ItemViewType.ITEM_BODY -> {
|
||||
val answer = mEntityList[position]
|
||||
if ("community_article" == answer.type) {
|
||||
val questions = Questions()
|
||||
questions.title = answer.articleTitle
|
||||
answer.questions = questions
|
||||
}
|
||||
|
||||
val answerViewHolder = holder as CommunityAnswerItemViewHolder
|
||||
val binding = answerViewHolder.binding
|
||||
answerViewHolder.bindAnswerItem(answer, "", getPath())
|
||||
binding.title.setOnClickListener {
|
||||
if ("community_article" == answer.type) {
|
||||
mContext.startActivity(ArticleDetailActivity.getIntent(mContext, UserManager.getInstance().community, answer.id!!, "", getPath()))
|
||||
} else {
|
||||
val questions = answer.questions
|
||||
mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, questions.id, "", getPath()))
|
||||
}
|
||||
}
|
||||
|
||||
answerViewHolder.itemView.setOnClickListener {
|
||||
if ("community_article" == answer.type) {
|
||||
mContext.startActivity(ArticleDetailActivity.getIntent(mContext, UserManager.getInstance().community, answer.id!!, "", getPath()))
|
||||
} else {
|
||||
mContext.startActivity(AnswerDetailActivity.getIntent(mContext, answer.id, "", getPath()))
|
||||
}
|
||||
}
|
||||
}
|
||||
ItemViewType.ITEM_FOOTER -> {
|
||||
val footerViewHolder = holder as FooterViewHolder
|
||||
footerViewHolder.initItemPadding()
|
||||
footerViewHolder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver, R.string.ask_loadover_hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSyncData(position: Int): Pair<String, AnswerEntity>? {
|
||||
if (position >= mEntityList.size) return null
|
||||
val entity = mEntityList[position]
|
||||
return Pair(entity.id ?: "", entity)
|
||||
}
|
||||
|
||||
fun getPath(): String {
|
||||
return "问答-推荐-按时间"
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package com.gh.common.syncpage.example
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.view.VerticalItemDecoration
|
||||
import com.gh.gamecenter.baselist.ListFragment
|
||||
import com.gh.gamecenter.baselist.NormalListViewModel
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.qa.entity.AnswerEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Observable
|
||||
|
||||
class ExampleFragment : ListFragment<AnswerEntity, NormalListViewModel<AnswerEntity>>() {
|
||||
|
||||
private var mAdapter: ExampleAdapter? = null
|
||||
|
||||
override fun provideListAdapter(): ExampleAdapter {
|
||||
if (mAdapter == null) {
|
||||
mAdapter = ExampleAdapter(requireContext())
|
||||
}
|
||||
return mAdapter!!
|
||||
}
|
||||
|
||||
override fun getItemDecoration(): RecyclerView.ItemDecoration {
|
||||
return VerticalItemDecoration(context, 8F, false)
|
||||
}
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<MutableList<AnswerEntity>> {
|
||||
return RetrofitManager.getInstance(context).api.getCommunitiesRecommendNewest(UserManager.getInstance().community.id, page)
|
||||
}
|
||||
|
||||
override fun provideListViewModel(): NormalListViewModel<AnswerEntity> {
|
||||
val factory = NormalListViewModel.Factory(HaloApp.getInstance().application, this)
|
||||
return ViewModelProviders.of(this, factory).get(NormalListViewModel::class.java) as NormalListViewModel<AnswerEntity>
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
// SyncPageRepository.syncPageLiveData.observe(this, Observer {
|
||||
// it ?: return@Observer
|
||||
// val adapter = mListRv.adapter
|
||||
// if (adapter !is ISyncAdapterHandler) return@Observer
|
||||
// for(position in 0 until adapter.itemCount) {
|
||||
// val syncKey = adapter.getSyncData(position)
|
||||
// for (syncDataEntity in it) {
|
||||
// if (syncDataEntity.syncId == syncKey?.first) {
|
||||
// val isSuccess = SyncPageRepository.handleSyncData(syncKey.second, syncDataEntity)
|
||||
// if (isSuccess) adapter.notifyItemChanged(position)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
override fun provideSyncAdapter(): RecyclerView.Adapter<*>? {
|
||||
return mListRv.adapter
|
||||
}
|
||||
|
||||
override fun addSyncPageObserver(): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,8 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.gh.common.annotation.SyncPage
|
||||
import com.gh.common.syncpage.SyncFieldConstants
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@ -14,6 +16,7 @@ class MeEntity(@SerializedName("is_community_voted")
|
||||
@SerializedName("is_user_invite")
|
||||
var isUserInvite: Boolean = false,
|
||||
|
||||
@SyncPage(syncNames = [SyncFieldConstants.ANSWER_VOTE])
|
||||
@SerializedName("is_answer_voted")
|
||||
var isAnswerVoted: Boolean = false,
|
||||
|
||||
@ -50,10 +53,10 @@ class MeEntity(@SerializedName("is_community_voted")
|
||||
@SerializedName("is_toolkit_favorite")
|
||||
var isToolkitFavorite: Boolean = false,
|
||||
|
||||
@SerializedName("is_comment_own", alternate = ["is_answer_commented", "is_community_article_commented", "is_video_commented"])
|
||||
@SerializedName("is_comment_own", alternate = ["is_answer_commented", "is_community_article_commented", "is_video_commented"])
|
||||
var isCommentOwner: Boolean = false, // 是否是当前评论的拥有者
|
||||
|
||||
@SerializedName("is_comment_voted", alternate = ["is_answer_comment_voted","is_video_comment_voted", "is_community_article_comment_voted"])
|
||||
@SerializedName("is_comment_voted", alternate = ["is_answer_comment_voted", "is_video_comment_voted", "is_community_article_comment_voted"])
|
||||
var isCommentVoted: Boolean = false, // 是否已经点赞过当前评论
|
||||
|
||||
@SerializedName("is_version_requested")
|
||||
@ -65,6 +68,7 @@ class MeEntity(@SerializedName("is_community_voted")
|
||||
@SerializedName("is_favorite")
|
||||
var isCommunityArticleFavorite: Boolean = false,
|
||||
|
||||
@SyncPage(syncNames = [SyncFieldConstants.ARTICLE_VOTE])
|
||||
@SerializedName("is_vote")
|
||||
var isCommunityArticleVote: Boolean = false,
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@ import com.facebook.imagepipeline.image.ImageInfo;
|
||||
import com.gh.base.OnDoubleTapListener;
|
||||
import com.gh.base.fragment.BaseFragment_ViewPager_Checkable;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.syncpage.SyncPageRepository;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.EntranceUtils;
|
||||
@ -264,6 +265,10 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem
|
||||
super.onResume();
|
||||
//mViewModel.getDiscoveryData(false);
|
||||
MessageUnreadRepository.INSTANCE.loadMessageUnreadTotal();
|
||||
|
||||
if (isEverPause) {
|
||||
mBaseHandler.postDelayed(SyncPageRepository::clearSyncData, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -6,6 +6,9 @@ import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.history.HistoryHelper
|
||||
import com.gh.common.syncpage.SyncDataEntity
|
||||
import com.gh.common.syncpage.SyncFieldConstants
|
||||
import com.gh.common.syncpage.SyncPageRepository
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.SpecialColumn
|
||||
@ -88,6 +91,8 @@ class AnswerDetailViewModel(application: Application) : AndroidViewModel(applica
|
||||
val apiResponse = ApiResponse<VoteEntity>()
|
||||
apiResponse.data = response
|
||||
mVoteLiveData.postValue(apiResponse)
|
||||
|
||||
syncVoteData(answerId)
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
@ -111,6 +116,8 @@ class AnswerDetailViewModel(application: Application) : AndroidViewModel(applica
|
||||
val apiResponse = ApiResponse<VoteEntity>()
|
||||
apiResponse.data = response
|
||||
mVoteLiveData.postValue(apiResponse)
|
||||
|
||||
syncVoteData(answerId)
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
@ -134,6 +141,8 @@ class AnswerDetailViewModel(application: Application) : AndroidViewModel(applica
|
||||
answerDetail?.me?.isAnswerVoted = false
|
||||
|
||||
dislike.postValue(true)
|
||||
|
||||
syncVoteData(answerId)
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
@ -150,6 +159,8 @@ class AnswerDetailViewModel(application: Application) : AndroidViewModel(applica
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
answerDetail?.me?.isAnswerOpposed = false
|
||||
dislike.postValue(false)
|
||||
|
||||
syncVoteData(answerId)
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
@ -158,6 +169,16 @@ class AnswerDetailViewModel(application: Application) : AndroidViewModel(applica
|
||||
})
|
||||
}
|
||||
|
||||
private fun syncVoteData(answerId: String) {
|
||||
SyncPageRepository.postSyncData(SyncDataEntity(answerId,
|
||||
SyncFieldConstants.ANSWER_VOTE_COUNT,
|
||||
answerDetail!!.vote))
|
||||
SyncPageRepository.postSyncData(SyncDataEntity(answerId,
|
||||
SyncFieldConstants.ANSWER_VOTE,
|
||||
answerDetail?.me?.isAnswerVoted,
|
||||
checkFieldEntity = true))
|
||||
}
|
||||
|
||||
fun collectAnswer(answerId: String) {
|
||||
CollectionUtils.postCollection(getApplication(), answerId, CollectionUtils.CollectionType.answer, object : CollectionUtils.OnCollectionListener {
|
||||
override fun onSuccess() {
|
||||
|
||||
@ -5,6 +5,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.constant.ItemViewType
|
||||
import com.gh.common.syncpage.ISyncAdapterHandler
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
@ -15,7 +16,7 @@ import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
|
||||
import com.gh.gamecenter.qa.entity.ArticleEntity
|
||||
import com.gh.gamecenter.qa.entity.Questions
|
||||
|
||||
class SimpleArticleListAdapter(context: Context) : ListAdapter<ArticleEntity>(context) {
|
||||
class SimpleArticleListAdapter(context: Context) : ListAdapter<ArticleEntity>(context), ISyncAdapterHandler {
|
||||
|
||||
override fun areItemsTheSame(oldItem: ArticleEntity?, newItem: ArticleEntity?): Boolean {
|
||||
return oldItem?.id == newItem?.id
|
||||
@ -72,4 +73,10 @@ class SimpleArticleListAdapter(context: Context) : ListAdapter<ArticleEntity>(co
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSyncData(position: Int): Pair<String, Any>? {
|
||||
if (position >= mEntityList.size) return null
|
||||
val entity = mEntityList[position]
|
||||
return Pair(entity.id, entity)
|
||||
}
|
||||
|
||||
}
|
||||
@ -86,4 +86,12 @@ class SimpleArticleListFragment : ListFragment<ArticleEntity, SimpleArticleListV
|
||||
mBaseHandler.postDelayed({ mListViewModel.load(LoadType.REFRESH) }, 100)
|
||||
}
|
||||
}
|
||||
|
||||
override fun provideSyncAdapter(): RecyclerView.Adapter<*>? {
|
||||
return mAdapter
|
||||
}
|
||||
|
||||
override fun addSyncPageObserver(): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,9 @@ import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.syncpage.SyncDataEntity
|
||||
import com.gh.common.syncpage.SyncFieldConstants
|
||||
import com.gh.common.syncpage.SyncPageRepository
|
||||
import com.gh.common.util.CollectionUtils
|
||||
import com.gh.common.util.ErrorHelper
|
||||
import com.gh.gamecenter.R
|
||||
@ -79,6 +82,8 @@ class ArticleDetailViewModel(application: Application) : AndroidViewModel(applic
|
||||
detailEntity!!.count.vote--
|
||||
|
||||
like.postValue(response)
|
||||
|
||||
syncVoteData()
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
@ -99,6 +104,8 @@ class ArticleDetailViewModel(application: Application) : AndroidViewModel(applic
|
||||
detailEntity!!.count.vote++
|
||||
|
||||
like.postValue(response)
|
||||
|
||||
syncVoteData()
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
@ -119,6 +126,8 @@ class ArticleDetailViewModel(application: Application) : AndroidViewModel(applic
|
||||
detailEntity?.me?.isCommunityArticleVote = false
|
||||
|
||||
dislike.postValue(true)
|
||||
|
||||
syncVoteData()
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
@ -135,6 +144,8 @@ class ArticleDetailViewModel(application: Application) : AndroidViewModel(applic
|
||||
detailEntity?.me?.isCommunityArticleOppose = false
|
||||
|
||||
dislike.postValue(false)
|
||||
|
||||
syncVoteData()
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
@ -143,6 +154,19 @@ class ArticleDetailViewModel(application: Application) : AndroidViewModel(applic
|
||||
})
|
||||
}
|
||||
|
||||
private fun syncVoteData() {
|
||||
articleId?.apply {
|
||||
SyncPageRepository.postSyncData(SyncDataEntity(this,
|
||||
SyncFieldConstants.ARTICLE_VOTE_COUNT,
|
||||
detailEntity?.count?.vote,
|
||||
checkFieldEntity = true))
|
||||
SyncPageRepository.postSyncData(SyncDataEntity(this,
|
||||
SyncFieldConstants.ARTICLE_VOTE,
|
||||
detailEntity?.me?.isCommunityArticleVote,
|
||||
checkFieldEntity = true))
|
||||
}
|
||||
}
|
||||
|
||||
fun collectionCommand(isCollection: Boolean, callback: (isFollow: Boolean) -> Unit) {
|
||||
val observable = if (isCollection) {
|
||||
mApi.postCommunityArticleFavorites(UserManager.getInstance().userId, community?.id, articleId)
|
||||
|
||||
@ -21,6 +21,9 @@ import butterknife.BindView
|
||||
import butterknife.OnClick
|
||||
import butterknife.Optional
|
||||
import com.gh.base.fragment.BaseDialogWrapperFragment
|
||||
import com.gh.common.syncpage.SyncDataEntity
|
||||
import com.gh.common.syncpage.SyncFieldConstants
|
||||
import com.gh.common.syncpage.SyncPageRepository
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.view.VerticalItemDecoration
|
||||
import com.gh.gamecenter.R
|
||||
@ -93,6 +96,17 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
|
||||
mCommentListener?.onCommentDraftChange("")
|
||||
}
|
||||
commentEt.postDelayed({ setSoftInput(false) }, 100)
|
||||
|
||||
if (mCommentType == CommentType.COMMUNITY_ARTICLE) {
|
||||
SyncPageRepository.postSyncData(SyncDataEntity(mArticleId,
|
||||
SyncFieldConstants.ARTICLE_COMMENT_COUNT,
|
||||
mCommentCount,
|
||||
checkFieldEntity = true))
|
||||
} else if (mCommentType == CommentType.ANSWER) {
|
||||
SyncPageRepository.postSyncData(SyncDataEntity(mAnswerId,
|
||||
SyncFieldConstants.ANSWER_COMMENT_COUNT,
|
||||
mCommentCount))
|
||||
}
|
||||
}
|
||||
apiResponse.httpException != null -> {
|
||||
mSendingDialog?.dismiss()
|
||||
|
||||
@ -7,6 +7,8 @@ import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverters
|
||||
import com.gh.common.annotation.SyncPage
|
||||
import com.gh.common.syncpage.SyncFieldConstants
|
||||
import com.gh.gamecenter.entity.MeEntity
|
||||
import com.gh.gamecenter.entity.UserEntity
|
||||
import com.gh.gamecenter.room.converter.AnswerUserConverter
|
||||
@ -47,6 +49,7 @@ class AnswerEntity() : Parcelable {
|
||||
@TypeConverters(CommunityVideoConverter::class)
|
||||
var videos: List<CommunityVideoEntity> = ArrayList()
|
||||
|
||||
@SyncPage(syncNames = [SyncFieldConstants.ANSWER_VOTE_COUNT, SyncFieldConstants.ARTICLE_VOTE_COUNT])
|
||||
var vote: Int = 0
|
||||
|
||||
@TypeConverters(AnswerUserConverter::class)
|
||||
@ -59,6 +62,7 @@ class AnswerEntity() : Parcelable {
|
||||
@SerializedName("community_name")
|
||||
var communityName: String? = null
|
||||
|
||||
@SyncPage(syncNames = [SyncFieldConstants.ANSWER_COMMENT_COUNT, SyncFieldConstants.ARTICLE_COMMENT_COUNT])
|
||||
@SerializedName("comment_count")
|
||||
var commentCount: Int = 0
|
||||
|
||||
|
||||
@ -5,6 +5,8 @@ import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverters
|
||||
import com.gh.common.annotation.SyncPage
|
||||
import com.gh.common.syncpage.SyncFieldConstants
|
||||
import com.gh.gamecenter.entity.CommunityEntity
|
||||
import com.gh.gamecenter.entity.MeEntity
|
||||
import com.gh.gamecenter.entity.UserEntity
|
||||
@ -69,7 +71,9 @@ data class ArticleEntity(
|
||||
|
||||
@Parcelize
|
||||
data class Count(
|
||||
@SyncPage(syncNames = [SyncFieldConstants.ARTICLE_COMMENT_COUNT])
|
||||
var comment: Int = 0,
|
||||
@SyncPage(syncNames = [SyncFieldConstants.ARTICLE_VOTE_COUNT])
|
||||
var vote: Int = 0,
|
||||
var answer: Int = 0) : Parcelable
|
||||
|
||||
|
||||
Reference in New Issue
Block a user