Merge branch 'feature-bbs' into dev-4.7.0

This commit is contained in:
张玉久
2021-01-18 09:13:45 +08:00
93 changed files with 2929 additions and 1735 deletions

View File

@ -118,7 +118,7 @@ class SimulatorDownloadManager private constructor() {
key = if (shouldShowUpdate && isInstalled) "更新弹窗" else "下载弹窗",
logShowEvent = true
)
DialogUtils.showNewAlertDialog(context, title, message, negativeText, positiveText, trackableEntity, Gravity.LEFT, {
DialogUtils.showNewAlertDialog(context, title, message, negativeText, positiveText, trackableEntity, Gravity.LEFT, false, {
if (shouldShowUpdate && isInstalled) {
cancelCallback?.invoke()
MtaHelper.onEvent(trackableEntity.event, trackableEntity.key, "点击下次再说")

View File

@ -334,15 +334,16 @@ public class CommentUtils {
});
});
}
public static void voteVideoComment(final Context context,
String answerId,
String articleId,
String articleCommunityId,
String videoId,
final CommentEntity commentEntity,
final TextView commentLikeCountTv,
final ImageView commentLikeIv,
final OnVoteListener listener) {
String answerId,
String articleId,
String articleCommunityId,
String videoId,
final CommentEntity commentEntity,
final TextView commentLikeCountTv,
final ImageView commentLikeIv,
final OnVoteListener listener) {
String entrance = "视频流-评论-点赞";
CheckLoginUtils.checkLogin(context, entrance, () -> {
@ -389,10 +390,10 @@ public class CommentUtils {
}
public static void unVoteVideoComment(final Context context,
String videoId,
final CommentEntity commentEntity,
final TextView commentLikeCountTv,
final ImageView commentLikeIv) {
String videoId,
final CommentEntity commentEntity,
final TextView commentLikeCountTv,
final ImageView commentLikeIv) {
String entrance = "视频流-评论-取消点赞";
CheckLoginUtils.checkLogin(context, entrance, () -> {
RetrofitManager.getInstance(context).getApi()
@ -434,10 +435,10 @@ public class CommentUtils {
holder.commentLikeIv.setImageResource(R.drawable.comment_vote_unselect);
if (userDataEntity == null || !userDataEntity.isCommentOwner()) {
holder.replyLine.setVisibility(View.VISIBLE);
if (holder.replyLine != null) holder.replyLine.setVisibility(View.VISIBLE);
holder.commentReply.setVisibility(View.VISIBLE);
} else {
holder.replyLine.setVisibility(View.GONE);
if (holder.replyLine != null) holder.replyLine.setVisibility(View.GONE);
holder.commentReply.setVisibility(View.GONE);
}

View File

@ -350,7 +350,7 @@ public class DialogUtils {
* @param cmListener 确认按钮监听
*/
public static Dialog showNewAlertDialog(Context context, String title, CharSequence message
, String negative, String positive, TrackableEntity trackableEntity, int gravity, final CancelListener clListener, final ConfirmListener cmListener) {
, String negative, String positive, TrackableEntity trackableEntity, int gravity, boolean shouldShowCloseBtn, final CancelListener clListener, final ConfirmListener cmListener) {
context = checkDialogContext(context);
final Dialog dialog;
if (trackableEntity != null) {
@ -373,6 +373,7 @@ public class DialogUtils {
TextView cancelBtn = contentView.findViewById(R.id.cancel);
TextView confirmBtn = contentView.findViewById(R.id.confirm);
View middleLine = contentView.findViewById(R.id.middle_line);
View closeIv = contentView.findViewById(R.id.closeIv);
titleTv.setGravity(gravity);
contentTv.setGravity(gravity);
@ -389,6 +390,8 @@ public class DialogUtils {
confirmBtn.setVisibility(View.GONE);
middleLine.setVisibility(View.GONE);
}
closeIv.setVisibility(shouldShowCloseBtn ? View.VISIBLE : View.GONE);
closeIv.setOnClickListener(v -> dialog.dismiss());
cancelBtn.setOnClickListener(v -> {
if (clListener != null) clListener.onCancel();
@ -413,7 +416,12 @@ public class DialogUtils {
public static Dialog showNewAlertDialog(Context context, String title, CharSequence message
, String negative, String positive, final CancelListener clListener, final ConfirmListener cmListener) {
return showNewAlertDialog(context, title, message, negative, positive, null, Gravity.LEFT, clListener, cmListener);
return showNewAlertDialog(context, title, message, negative, positive, null, Gravity.LEFT, false, clListener, cmListener);
}
public static Dialog showNewAlertDialog(Context context, String title, CharSequence message
, String negative, String positive, int gravity, boolean shouldShowCloseBtn, final CancelListener clListener, final ConfirmListener cmListener) {
return showNewAlertDialog(context, title, message, negative, positive, null, gravity, shouldShowCloseBtn, clListener, cmListener);
}
/**
@ -1951,8 +1959,8 @@ public class DialogUtils {
final Dialog dialog = new Dialog(context, R.style.DialogWindowTransparent);
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_energy, null);
((TextView)contentView.findViewById(R.id.userName)).setText("\"" + userName + "\"");
((TextView)contentView.findViewById(R.id.energy)).setText(energy + "");
((TextView) contentView.findViewById(R.id.userName)).setText("\"" + userName + "\"");
((TextView) contentView.findViewById(R.id.energy)).setText(energy + "");
contentView.findViewById(R.id.dialog_positive).setOnClickListener(v -> {
dialog.dismiss();

View File

@ -319,7 +319,7 @@ public class ShareUtils {
});
}
public void shareGameDetail(Activity activity, String url, String icon, String shareTitle, String shareSummary, ShareEntrance shareEntrance, String id, ShareCallBack callBack) {
public void shareParamsDetail(Activity activity, String url, String icon, String shareTitle, String shareSummary, ShareEntrance shareEntrance, String id, ShareCallBack callBack) {
if (activity.isFinishing()) return;
this.mActivity = new WeakReference<>(activity);
this.shareIcon = icon;
@ -654,6 +654,10 @@ public class ShareUtils {
}
}
public String getTitle() {
return mTitle;
}
private class ShareRecyclerViewAdapter extends RecyclerView.Adapter<ShareRecyclerViewAdapter.ViewHolder> {
private OnItemClickListener listener;

View File

@ -116,6 +116,7 @@ object UploadImageUtils {
Observable.create(ObservableOnSubscribe<Map<String, String>> {
val compressList = compressImageList(imgs, compressGif)
listener.onCompressSuccess(compressList.map { file -> file.absolutePath }.toList())
var listTotal = 0L // 总大小
var listProgress = 0L // 已上传的大小
for (img in compressList) {
@ -274,7 +275,7 @@ object UploadImageUtils {
}
// 防止GIF图片文件后缀不是GIF这个FileName只是告诉服务端后缀格式没有其他用处
fun getFileName(file: File): String {
fun getFileName(file: File): String {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(file.absolutePath, options)
@ -292,6 +293,7 @@ object UploadImageUtils {
interface OnUploadImageListListener {
fun onSuccess(imageUrl: LinkedHashMap<String, String>, errorMap: Map<String, Exception>) // key:sourceImage value:compressImage
fun onCompressSuccess(imageUrls: List<String>) {}
fun onError(errorMap: Map<String, Exception>) // 全部上传失败时回调
fun onProgress(total: Long, progress: Long)
}

View File

@ -24,6 +24,7 @@ import com.gh.common.util.HtmlUtils;
import com.gh.common.util.ImageUtils;
import com.gh.common.util.MtaHelper;
import com.gh.common.util.NetworkUtils;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.RichEditorUtils;
import com.gh.common.util.SPUtils;
import com.gh.gamecenter.BuildConfig;
@ -321,6 +322,10 @@ public class RichEditor extends WebView {
exec("javascript:RE.setBaseFontSize('" + px + "px');");
}
public boolean hasPlaceholderImage() {
return getHtml().contains("placeholder-image-container");
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
@ -444,6 +449,15 @@ public class RichEditor extends WebView {
exec("javascript:RE.setTextColor('" + hex + "');");
}
public void insertPlaceholderImage(String id) {
exec("javascript:RE.prepareInsert();");
exec("javascript:RE.insertPlaceholderImage('" + id + "');");
}
public void replacePlaceholderImage(String list) {
exec("javascript:RE.replacePlaceholderImage('" + list + "');");
}
public void removeFormat() {
exec("javascript:RE.removeFormat();");
}
@ -649,6 +663,16 @@ public class RichEditor extends WebView {
return DeviceUtils.getNetwork(HaloApp.getInstance().getApplication());
}
@JavascriptInterface
public String getAppVersion() {
return BuildConfig.VERSION_NAME;
}
@JavascriptInterface
public int getAppVersionCode() {
return PackageUtils.getVersionCode();
}
/**
* 显示toast
*
@ -737,7 +761,7 @@ public class RichEditor extends WebView {
public void invalidate() {
super.invalidate();
if (mInitialLayoutCallback != null &&getHeight() > 0) {
if (mInitialLayoutCallback != null && getHeight() > 0) {
// 仅初次 layout 完成会有回调
mInitialLayoutCallback.onCallback();
mInitialLayoutCallback = null;

View File

@ -967,6 +967,11 @@ public class SuggestionActivity extends ToolBarActivity implements OnRequestCall
private void postPic(final String email) {
UploadImageUtils.INSTANCE.compressAndUploadImageList(UploadImageUtils.UploadType.suggestion
, mAdapter.getFileList(), false, new UploadImageUtils.OnUploadImageListListener() {
@Override
public void onCompressSuccess(@NotNull List<String> imageUrls) {
}
@Override
public void onProgress(long total, long progress) {
int percent = (int) (100 * (progress / (float) total));

View File

@ -9,6 +9,7 @@ import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.gamecenter.R;
import androidx.annotation.Nullable;
import butterknife.BindView;
/**
@ -33,7 +34,7 @@ public class CommentViewHolder extends BaseRecyclerViewHolder {
@BindView(R.id.comment_user_name)
public TextView commentUserNameTv;
@BindView(R.id.comment_author)
public TextView commentAuthorTv;
public View commentAuthorTv;
@BindView(R.id.comment_badge)
public View commentBadge;
@ -54,6 +55,7 @@ public class CommentViewHolder extends BaseRecyclerViewHolder {
public View commentMore;
@BindView(R.id.comment_reply)
public View commentReply;
@Nullable
@BindView(R.id.reply_dividing_line)
public View replyLine;

View File

@ -3,5 +3,6 @@ package com.gh.gamecenter.entity
data class MenuItemEntity(
val text: String = "",
val normalIcon: Int = 0,
val disableIcon: Int = 0
val disableIcon: Int = 0,
val isEnable:Boolean = true
)

View File

@ -31,6 +31,7 @@ class ForumArticleAskListFragment : ListFragment<AnswerEntity, ForumArticleAskLi
mPath = arguments?.getString(EntranceUtils.KEY_PATH) ?: ""
super.onCreate(savedInstanceState)
mListRv.overScrollMode = View.OVER_SCROLL_NEVER
mListRefresh?.isEnabled = false
mListRv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.white))
}
@ -54,6 +55,14 @@ class ForumArticleAskListFragment : ListFragment<AnswerEntity, ForumArticleAskLi
})
}
fun refresh() {
onLoadRefresh()
}
override fun hideRefreshingLayout() {
(parentFragment as? ForumDetailFragment)?.hideRefreshingLayout()
}
fun onRefresh(filter: String) {
mViewModel?.sort = if (filter == "最新发布") "time.create" else "time.reply"
onRefresh()
@ -79,7 +88,7 @@ class ForumArticleAskListFragment : ListFragment<AnswerEntity, ForumArticleAskLi
override fun provideSyncAdapter(): ForumArticleAskListAdapter = mAdapter
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(detail: EBDeleteDetail){
fun onEvent(detail: EBDeleteDetail) {
val currentEntity = mAdapter.entityList?.find { it.id == detail.id }
val indexOf = mAdapter.entityList?.indexOf(currentEntity) ?: 0
mAdapter.entityList?.remove(currentEntity)

View File

@ -43,7 +43,6 @@ import com.gh.gamecenter.mvvm.Status
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity
import com.google.android.material.appbar.AppBarLayout
import kotterknife.bindView
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@ -51,11 +50,6 @@ import kotlin.math.abs
class ForumDetailFragment : BaseLazyTabFragment() {
private val mReuseLoading by bindView<LinearLayout>(R.id.reuse_ll_loading)
private val mReuseNoConnection by bindView<View>(R.id.reuse_no_connection)
private val mReuseNoData by bindView<View>(R.id.reuse_none_data)
private val mReuseNoDataTv by bindView<TextView>(R.id.reuse_tv_none_data)
private var mAllForumArticleAskListFragment: ForumArticleAskListFragment? = null
private var mEssenceForumArticleAskListFragment: ForumArticleAskListFragment? = null
private var mAskForumArticleAskListFragment: ForumArticleAskListFragment? = null
@ -96,11 +90,11 @@ class ForumDetailFragment : BaseLazyTabFragment() {
val factory = ForumDetailViewModel.Factory(bbsId)
mViewModel = viewModelProvider(factory)
mViewModel?.forumDetail?.observe(this, Observer {
mReuseLoading.visibility = View.GONE
mBinding.reuseLoading.reuseLlLoading.visibility = View.GONE
if (it.status == Status.SUCCESS) {
mBinding.communityEdit.visibility = View.VISIBLE
mBinding.forumContainer.visibility = View.VISIBLE
mReuseNoConnection.visibility = View.GONE
mBinding.reuseNoConnection.reuseNoConnection.visibility = View.GONE
if (it.data != null) {
mForumDetail = it.data
initUI()
@ -109,16 +103,20 @@ class ForumDetailFragment : BaseLazyTabFragment() {
mBinding.communityEdit.visibility = View.GONE
mBinding.forumContainer.visibility = View.GONE
if (it.exception != null && it.exception.code() == 404) {
mReuseNoDataTv.text = "页面为空"
mReuseNoData.visibility = View.VISIBLE
mReuseNoConnection.visibility = View.GONE
mBinding.reuseNoneData.reuseTvNoneData.text = "页面为空"
mBinding.reuseNoneData.reuseNoneData.visibility = View.VISIBLE
mBinding.reuseNoConnection.reuseNoConnection.visibility = View.GONE
ToastUtils.showToast("内容可能已被删除")
} else {
mReuseNoData.visibility = View.GONE
mReuseNoConnection.visibility = View.VISIBLE
mBinding.reuseNoneData.reuseNoneData.visibility = View.GONE
mBinding.reuseNoConnection.reuseNoConnection.visibility = View.VISIBLE
}
}
})
mBinding.refreshLayout.setProgressViewOffset(false, 0, DisplayUtils.dip2px(40f) + DisplayUtils.getStatusBarHeight(requireContext().getResources()));
mBinding.appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
mBinding.refreshLayout.isEnabled = abs(verticalOffset) <= 2
})
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -163,9 +161,24 @@ class ForumDetailFragment : BaseLazyTabFragment() {
MtaHelper.onEvent("论坛详情", getKeyValue(mLastPosition), getKeyValue(it))
mLastPosition = it
}
mBinding.refreshLayout.setOnRefreshListener {
if (mAllForumArticleAskListFragment != null && mViewPager.currentItem == 0) {
mAllForumArticleAskListFragment?.refresh()
}
if (mEssenceForumArticleAskListFragment != null && mViewPager.currentItem == 1) {
mEssenceForumArticleAskListFragment?.refresh()
}
if (mAskForumArticleAskListFragment != null && mViewPager.currentItem == 2) {
mAskForumArticleAskListFragment?.refresh()
}
}
LogUtils.uploadAccessToBbs(bbsId, "论坛详情")
}
fun hideRefreshingLayout() {
mBinding.refreshLayout.isRefreshing = false
}
private fun getKeyValue(position: Int): String {
return when (position) {
0 -> "全部Tab"
@ -242,7 +255,7 @@ class ForumDetailFragment : BaseLazyTabFragment() {
?: "", mEntrance, "论坛详情"))
}
R.id.reuse_no_connection -> {
mReuseLoading.visibility = View.VISIBLE
mBinding.reuseLoading.reuseLlLoading.visibility = View.VISIBLE
mViewModel?.getForumDetail()
}
R.id.gameZoneTv -> {

View File

@ -69,7 +69,7 @@ class ForumMyFollowAdapter(context: Context, val mViewModel: ForumMyFollowViewMo
popupWindow.dismiss()
when (text) {
"取消关注" -> {
DialogUtils.showNewAlertDialog(mContext, "提示", "确定取消关注", "暂不", "确定", null, Gravity.CENTER, {}, {
DialogUtils.showNewAlertDialog(mContext, "提示", "确定取消关注", "暂不", "确定", null, Gravity.CENTER, false, {}, {
MtaHelper.onEvent("论坛首页", "我关注的论坛", "取消关注")
mViewModel.unFollowForum(entity.id) {
EventBus.getDefault().post(EBForumFollowChange(entity, false))

View File

@ -4,20 +4,21 @@ import android.content.Context
import android.view.Gravity
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.RecyclerView
import com.gh.base.BaseRecyclerViewHolder
import com.gh.common.util.*
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.ForumItemBinding
import com.gh.gamecenter.entity.CommunityEntity
import com.gh.gamecenter.entity.ForumEntity
import com.gh.gamecenter.eventbus.EBForumFollowChange
import com.gh.gamecenter.forum.detail.ForumDetailActivity
import com.gh.gamecenter.forum.home.ForumVisitDao
import com.gh.gamecenter.qa.entity.CommunitySelectEntity
import com.lightgame.adapter.BaseRecyclerAdapter
import org.greenrobot.eventbus.EventBus
class ForumAdapter(context: Context, val mViewModel: ForumSelectViewModel?) : BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
class ForumAdapter(val context: Context, val mViewModel: ForumSelectViewModel?, val isSelectForum: Boolean = false, val onSelectCallback: ((entity: CommunityEntity) -> Unit)? = null) : BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
val datas: ArrayList<ForumEntity> = arrayListOf()
@ -40,15 +41,20 @@ class ForumAdapter(context: Context, val mViewModel: ForumSelectViewModel?) : Ba
val forumEntity = datas[position]
holder.binding.entity = forumEntity
holder.binding.forumIcon.displayGameIcon(forumEntity.game.getIcon(), forumEntity.game.iconSubscript)
holder.binding.followTv.goneIf(isSelectForum)
holder.itemView.setOnClickListener {
MtaHelper.onEvent("论坛首页", "选择论坛", forumEntity.name)
mContext.startActivity(ForumDetailActivity.getIntent(mContext, forumEntity.id, "论坛-选择论坛"))
if (isSelectForum) {
onSelectCallback?.invoke(CommunityEntity(forumEntity.id, forumEntity.name, icon = forumEntity.game.getIcon(), iconSubscript = forumEntity.game.iconSubscript))
} else {
MtaHelper.onEvent("论坛首页", "选择论坛", forumEntity.name)
mContext.startActivity(ForumDetailActivity.getIntent(mContext, forumEntity.id, "论坛-选择论坛"))
}
}
holder.binding.followTv.setOnClickListener {
mContext.ifLogin("论坛-选择论坛") {
debounceActionWithInterval(it.id) {
if (forumEntity.isFollow) {
DialogUtils.showNewAlertDialog(mContext, "提示", "确定取消关注", "暂不", "确定", null, Gravity.CENTER, {}, {
DialogUtils.showNewAlertDialog(mContext, "提示", "确定取消关注", "暂不", "确定", null, Gravity.CENTER, false, {}, {
mViewModel?.unFollowForum(forumEntity.id) {
MtaHelper.onEvent("论坛首页", "选择论坛", "关注")
forumEntity.isFollow = false

View File

@ -8,6 +8,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.util.viewModelProvider
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.CommunityEntity
import com.gh.gamecenter.entity.ForumCategoryEntity
import com.gh.gamecenter.eventbus.EBForumFollowChange
import com.gh.gamecenter.mvvm.Status
@ -28,6 +29,7 @@ class ForumSelectFragment : NormalFragment() {
private var mViewModel: ForumSelectViewModel? = null
private var selectCategory: ForumCategoryEntity? = null
var onSelectCallback: ((entity: CommunityEntity) -> Unit)? = null
override fun getLayoutId(): Int = R.layout.fragment_forum_select
@ -38,10 +40,12 @@ class ForumSelectFragment : NormalFragment() {
mLoadingView.visibility = View.GONE
if (it.status == Status.SUCCESS) {
if (it.data != null) {
mForumCategoryAdapter?.setListData(it.data)
val data = it.data as ArrayList
if (onSelectCallback != null) data.add(0, ForumCategoryEntity("", "我的关注"))
mForumCategoryAdapter?.setListData(data)
if (it.data.isNotEmpty()) {
selectCategory = it.data[0]
mViewModel?.getForumByCategoryId(it.data[0].id)
mViewModel?.getForumList(selectCategory)
}
}
mForumCategoryRv.visibility = View.VISIBLE
@ -81,13 +85,13 @@ class ForumSelectFragment : NormalFragment() {
mForumCategoryAdapter = ForumCategoryAdapter(requireContext()) {
selectCategory = it
mLoadingView.visibility = View.VISIBLE
mViewModel?.getForumByCategoryId(it.id)
mViewModel?.getForumList(it)
}
adapter = mForumCategoryAdapter
}
mForumRv.apply {
layoutManager = LinearLayoutManager(requireContext())
mForumAdapter = ForumAdapter(requireContext(), mViewModel)
mForumAdapter = ForumAdapter(requireContext(), mViewModel, onSelectCallback != null, onSelectCallback)
adapter = mForumAdapter
}
@ -97,7 +101,7 @@ class ForumSelectFragment : NormalFragment() {
mViewModel?.getForumCategories()
} else {
mLoadingView.visibility = View.VISIBLE
mViewModel?.getForumByCategoryId(selectCategory?.id ?: "")
mViewModel?.getForumList(selectCategory)
}
}
}

View File

@ -8,6 +8,7 @@ import com.gh.common.util.UrlFilterUtils
import com.gh.gamecenter.entity.ForumCategoryEntity
import com.gh.gamecenter.entity.ForumEntity
import com.gh.gamecenter.entity.LinkEntity
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.mvvm.Resource
import com.gh.gamecenter.qa.entity.CommunitySelectOpenEntity
import com.gh.gamecenter.retrofit.BiResponse
@ -44,10 +45,15 @@ class ForumSelectViewModel(application: Application) : AndroidViewModel(applicat
})
}
fun getForumByCategoryId(categoryId: String) {
RetrofitManager.getInstance(getApplication()).api
.getForumByCategories(categoryId)
.subscribeOn(Schedulers.io())
fun getForumList(category: ForumCategoryEntity?) {
val observable = if (category?.name != "我的关注") {
RetrofitManager.getInstance(getApplication()).api
.getForumByCategories(category?.id ?: "")
} else {
RetrofitManager.getInstance(getApplication()).api
.getFollowsForum(UserManager.getInstance().userId)
}
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<List<ForumEntity>>() {
override fun onResponse(response: List<ForumEntity>?) {
@ -76,6 +82,7 @@ class ForumSelectViewModel(application: Application) : AndroidViewModel(applicat
}
})
}
@SuppressLint("CheckResult")
fun unFollowForum(bbsId: String, onSuccess: () -> Unit) {
RetrofitManager.getInstance(getApplication()).api

View File

@ -102,7 +102,7 @@ class GameDetailMoreDialog : BaseDialogFragment(), View.OnTouchListener {
shareEntrance = ShareUtils.ShareEntrance.plugin
}
}
shareUtils.shareGameDetail(requireActivity(), url, gameEntity?.icon, gameEntity?.name,
shareUtils.shareParamsDetail(requireActivity(), url, gameEntity?.icon, gameEntity?.name,
gameEntity?.brief, shareEntrance, gameEntity?.id ?: "", null)
return shareUtils
}

View File

@ -53,7 +53,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
val intent = CommentActivity.getAnswerCommentIntent(itemView.context,
entity.id!!,
entity.commentCount,
entity.commentCount == 0)
false)
itemView.context.startActivity(intent)
}

View File

@ -4,19 +4,17 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.app.Dialog
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.text.TextUtils
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.animation.LinearInterpolator
import android.webkit.JavascriptInterface
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.databinding.BindingAdapter
import androidx.databinding.DataBindingUtil
@ -48,6 +46,7 @@ import com.gh.gamecenter.normal.NormalFragment
import com.gh.gamecenter.personal.PersonalFragment.LOGIN_TAG
import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity
import com.gh.gamecenter.qa.comment.CommentActivity
import com.gh.gamecenter.qa.dialog.MoreFunctionPanelDialog
import com.gh.gamecenter.qa.editor.OnLinkClickListener
import com.gh.gamecenter.qa.entity.AnswerDetailEntity
import com.gh.gamecenter.qa.entity.Questions
@ -151,6 +150,8 @@ class AnswerDetailFragment : NormalFragment() {
mBinding.richEditor.setInputEnabled(false)
mBinding.richEditor.setPadding(20, 15, 20, 15)
mBinding.bottomController.replyTv.text = "说点什么吧"
mBinding.bottomController.replyTv.setRoundedColorBackground(R.color.text_F5F5F5, 19F)
mElapsedHelper = TimeElapsedHelper(this)
// 只对显示在屏幕上的 fragment 计时
@ -231,8 +232,8 @@ class AnswerDetailFragment : NormalFragment() {
val count = data.getIntExtra(CommentActivity.COMMENT_COUNT, 0)
if (count != 0) {
mViewModel.answerDetail?.commentCount = count
mBinding.bottomController.tvCommentCount.text = String.format("评论 %s", NumberUtils.transSimpleCount(mViewModel.answerDetail?.commentCount
?: 0))
mBinding.bottomController.bottomCommentTv.text = NumberUtils.transSimpleCount(mViewModel.answerDetail?.commentCount
?: 0)
if (EntranceUtils.ENTRANCE_WELCOME == mEntrance) {
LogUtils.uploadCommentFromWelcomeDialog()
@ -281,7 +282,7 @@ class AnswerDetailFragment : NormalFragment() {
if (e != null && e.code() == 404 && e.response().errorBody()!!.string().isNotEmpty()) {
HistoryHelper.deleteAnswerEntity(mAnswerId)
mBinding.bottomController.containerControl.visibility = View.GONE
mBinding.bottomController.root.visibility = View.GONE
mNoDataTv.setText(R.string.content_delete_hint)
mNoData.visibility = View.VISIBLE
mNoConn.visibility = View.GONE
@ -308,7 +309,7 @@ class AnswerDetailFragment : NormalFragment() {
} else {
mNoConn.visibility = View.VISIBLE
mBinding.scrollView.visibility = View.GONE
mBinding.bottomController.containerControl.visibility = View.GONE
mBinding.bottomController.root.visibility = View.GONE
mBinding.bottomDividerView.visibility = View.GONE
mBinding.bottomShadowView.visibility = View.GONE
}
@ -486,25 +487,19 @@ class AnswerDetailFragment : NormalFragment() {
mBinding.followTv.visibility = View.GONE
} else {
mBinding.followTv.isEnabled = true
if (!me.isFollower) {
mBinding.followTv.alpha = 1f
mBinding.followTv.visibility = View.VISIBLE
updateFollowBtn(false)
} else {
mBinding.followTv.visibility = View.GONE
}
updateFollowBtn(me.isFollower)
}
}
private fun toggleComment(enable: Boolean, commentCount: Int) {
if (enable) {
mBinding.bottomController.ivComment.setImageResource(R.drawable.community_content_detail_comment_open)
mBinding.bottomController.tvCommentCount.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_242529))
mBinding.bottomController.tvCommentCount.text = String.format("评论 %d", commentCount)
mBinding.bottomController.bottomCommentIv.setImageResource(R.drawable.community_content_detail_comment_open)
mBinding.bottomController.bottomCommentTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_242529))
mBinding.bottomController.bottomCommentTv.text = if (commentCount > 0) commentCount.toString() else "评论"
} else {
mBinding.bottomController.ivComment.setImageResource(R.drawable.community_content_detail_comment_close)
mBinding.bottomController.tvCommentCount.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_b3b3b3))
mBinding.bottomController.tvCommentCount.text = "评论已关闭"
mBinding.bottomController.bottomCommentIv.setImageResource(R.drawable.community_content_detail_comment_close)
mBinding.bottomController.bottomCommentTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_b3b3b3))
mBinding.bottomController.bottomCommentTv.text = "评论已关闭"
}
}
@ -609,78 +604,76 @@ class AnswerDetailFragment : NormalFragment() {
private fun showMoreItemDialog(answer: AnswerDetailEntity) {
if (fragmentIsVisible()) {
val view = LayoutInflater.from(context).inflate(R.layout.menu_answer_detail_more_new, null)
val popupWindow = PopupWindow(
view,
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
popupWindow.apply {
setBackgroundDrawable(ColorDrawable(0))
isTouchable = true
isFocusable = true
isOutsideTouchable = true
}
view.measure(0, 0)
val viewWidth = view.measuredWidth
popupWindow.showAsDropDown((activity as ToolBarActivity).menu.getItem(2).actionView, -viewWidth - 10, 10)
val container = view.findViewById<LinearLayout>(R.id.container)
val entities = ArrayList<MenuItemEntity>()
entities.add(MenuItemEntity("首页", R.drawable.menu_more_home))
if (answer.user.id != UserManager.getInstance().userId) {
entities.add(MenuItemEntity("投诉", R.drawable.ic_menu_gamedetail_feedback))
entities.add(MenuItemEntity("投诉", R.drawable.icon_gamedetail_copyright))
}
if (answer.me.isModerator) {
entities.add(MenuItemEntity("加精", R.drawable.menu_more_essence_enable,
if (answer.isHighlighted || answer.user.id == UserManager.getInstance().userId)
R.drawable.menu_more_essence_unenable else 0))
entities.add(MenuItemEntity("折叠", R.drawable.menu_more_fold))
val isEnable = answer.isHighlighted || answer.user.id == UserManager.getInstance().userId
entities.add(MenuItemEntity("加精", if (isEnable)
R.drawable.icon_more_panel_essence_unenable else R.drawable.icon_more_panel_essence,
isEnable = !isEnable))
entities.add(MenuItemEntity("折叠", R.drawable.icon_more_panel_fold))
}
if (answer.user.id == UserManager.getInstance().userId) {
entities.add(MenuItemEntity(if (answer.commentable) "关闭评论" else "恢复评论", if (answer.commentable) R.drawable.ic_close_comment else R.drawable.ic_open_comment))
entities.add(MenuItemEntity(if (answer.commentable) "关闭评论" else "恢复评论", if (answer.commentable) R.drawable.icon_more_panel_close_comment_enable
else R.drawable.icon_more_panel_close_comment_unenable))
}
if (answer.me.isModerator || answer.user.id == UserManager.getInstance().userId) {
entities.add(MenuItemEntity("删除", R.drawable.menu_more_delete))
entities.add(MenuItemEntity("删除", R.drawable.icon_more_panel_delete))
}
entities.forEachIndexed { index, item ->
val menuItem = createMenuItem(index, item)
container.addView(menuItem)
menuItem.setOnClickListener {
popupWindow.dismiss()
when (item.text) {
"首页" -> {
val community = mViewModel.answerDetail?.community
MtaHelper.onEvent("回到首页", "回答详情", community?.name + "+" + StringUtils.combineTwoString(mViewModel.answerDetail?.content, mViewModel.answerDetail?.id))
DirectUtils.directToCommunity(requireContext(), community)
}
"投诉" -> {
SuggestionActivity.startSuggestionActivity(context, SuggestType.normal, "report", "回答投诉($mAnswerId")
}
"加精" -> {
addEssenceForum(answer)
}
"折叠" -> {
foldForum(answer)
}
"关闭评论", "恢复评论" -> {
if (mViewModel.answerDetail?.commentable!!) {
DialogUtils.showAlertDialog(requireContext(), "提示", "关闭评论之后,该回答将无法查看和发表评论,确定关闭吗?",
"确定", "取消", { mViewModel.toggleComment(mAnswerId, false) }, null)
} else {
DialogUtils.showAlertDialog(requireContext(), "提示", "确定恢复评论吗?",
"确定", "取消", { mViewModel.toggleComment(mAnswerId, true) }, null)
}
}
"删除" -> {
DialogUtils.showNewAlertDialog(requireContext(), "提示", "删除回答后,其中的所有回复都将被删除", "取消", "删除", {}, {
mViewModel.doHideThisAnswer(mAnswerId)
})
val shareIcon: String = if (mAnswersImages!!.size > 0) {
mAnswersImages[0]
} else {
getString(R.string.share_ghzs_logo)
}
var shareSummary = mBinding.richEditor.text
if (TextUtils.isEmpty(shareSummary)) {
shareSummary = getString(R.string.ask_share_default_summary)
}
val shareUrl = if (isPublishEnv()) {
getString(R.string.share_answers_url, mAnswerId)
} else {
getString(R.string.share_answers_url_dev, answer.question.id, mAnswerId)
}
val shareUtils = ShareUtils.getInstance(context)
shareUtils.shareParamsDetail(activity,
shareUrl,
shareIcon,
getString(R.string.ask_share_answers_title, answer.user.name, answer.question.title, answer.vote),
shareSummary,
ShareUtils.ShareEntrance.answerNormal, mAnswerId, null)
MoreFunctionPanelDialog.showMoreDialog(requireActivity() as AppCompatActivity, entities, answer.question.title
?: "", shareUtils) {
when (it.text) {
"投诉" -> {
SuggestionActivity.startSuggestionActivity(context, SuggestType.normal, "report", "回答投诉($mAnswerId")
}
"加精" -> {
addEssenceForum(answer)
}
"折叠" -> {
foldForum(answer)
}
"关闭评论", "恢复评论" -> {
if (mViewModel.answerDetail?.commentable!!) {
DialogUtils.showAlertDialog(requireContext(), "提示", "关闭评论之后,该回答将无法查看和发表评论,确定关闭吗?",
"确定", "取消", { mViewModel.toggleComment(mAnswerId, false) }, null)
} else {
DialogUtils.showAlertDialog(requireContext(), "提示", "确定恢复评论吗?",
"确定", "取消", { mViewModel.toggleComment(mAnswerId, true) }, null)
}
}
"删除" -> {
DialogUtils.showNewAlertDialog(requireContext(), "提示", "删除回答后,其中的所有回复都将被删除", "取消", "删除", {}, {
mViewModel.doHideThisAnswer(mAnswerId)
})
}
}
}
}
}
@ -731,17 +724,12 @@ class AnswerDetailFragment : NormalFragment() {
}
@OnClick(
R.id.iv_comment,
R.id.tv_comment_count,
R.id.iv_collect,
R.id.tv_collect,
R.id.iv_share,
R.id.tv_share,
R.id.container_dislike,
R.id.replyTv,
R.id.bottomStarContainer,
R.id.reuse_no_connection,
R.id.status_tv,
R.id.bottomLikeContainer,
R.id.answer_count_container,
R.id.container_like,
R.id.follow_tv,
R.id.user_icon_iv,
R.id.questionsdetail_item_pic1,
@ -758,11 +746,11 @@ class AnswerDetailFragment : NormalFragment() {
mViewModel.getAnswerDetail(mAnswerId, mEntrance)
}
R.id.iv_share, R.id.tv_share -> {
shareAnswer()
}
// R.id.iv_share, R.id.tv_share -> {
// shareAnswer()
// }
R.id.iv_collect, R.id.tv_collect -> {
R.id.bottomStarContainer -> {
ifLogin("回答详情-收藏") {
mViewModel.answerDetail?.let {
if (!it.me.isAnswerFavorite) {
@ -777,42 +765,32 @@ class AnswerDetailFragment : NormalFragment() {
startActivity(QuestionsDetailActivity.getIntent(context, mViewModel.answerDetail!!.question.id, mEntrance, "回答详情"))
}
R.id.container_like -> debounceActionWithInterval(R.id.container_like, 1000) {
ifLogin("回答详情-赞同") {
if (mViewModel.answerDetail != null && !mViewModel.answerDetail!!.me.isAnswerVoted) {
mViewModel.like(mAnswerId)
R.id.bottomLikeContainer -> {
debounceActionWithInterval(R.id.container_like, 1000) {
ifLogin("回答详情-赞同") {
if (mViewModel.answerDetail != null && !mViewModel.answerDetail!!.me.isAnswerVoted) {
mViewModel.like(mAnswerId)
if (EntranceUtils.ENTRANCE_WELCOME == mEntrance) {
LogUtils.uploadLikeFromWelcomeDialog()
}
if (EntranceUtils.ENTRANCE_WELCOME == mEntrance) {
LogUtils.uploadLikeFromWelcomeDialog()
}
DataUtils.onMtaEvent(context,
"答案点赞量_社区加位置",
mViewModel.answerDetail?.community?.name,
mPath)
} else {
mViewModel.cancelLike(mAnswerId)
}
}
}
R.id.container_dislike -> debounceActionWithInterval(R.id.container_like, 1000) {
ifLogin("回答详情-反对") {
mViewModel.answerDetail?.let {
if (it.me.isAnswerOpposed) {
mViewModel.cancelDislike(mAnswerId)
DataUtils.onMtaEvent(context,
"答案点赞量_社区加位置",
mViewModel.answerDetail?.community?.name,
mPath)
} else {
mViewModel.dislike(mAnswerId)
mViewModel.cancelLike(mAnswerId)
}
}
}
}
R.id.iv_comment, R.id.tv_comment_count -> {
R.id.replyTv -> {
mViewModel.answerDetail?.run {
if (commentable) {
DataUtils.onMtaEvent(context, "详情页面", "答案评论详情", content.subStringIfPossible(30))
showCommentDialog(commentCount == 0)
showCommentDialog(false)
} else {
toast("作者已关闭评论")
}
@ -853,6 +831,8 @@ class AnswerDetailFragment : NormalFragment() {
ifLogin("回答详情-[关注]用户") {
if (mBinding.followTv.text == "关注") {
mViewModel.follow(mViewModel.answerDetail!!.user.id!!)
} else {
mViewModel.unfollow(mViewModel.answerDetail!!.user.id!!)
}
}
}
@ -883,10 +863,6 @@ class AnswerDetailFragment : NormalFragment() {
R.id.questionsdetail_item_pic3 -> showQuestionImageDetail(view, 2 - videoSize)
R.id.title_tv -> {
// startActivity(QuestionsDetailActivity.getIntent(
// context,
// mViewModel.answerDetail?.question?.id, mEntrance,
// "回答详情"))
val intent = QuestionsDetailActivity.getIntent(
context,
mViewModel.answerDetail?.question?.id, mEntrance,
@ -931,13 +907,13 @@ class AnswerDetailFragment : NormalFragment() {
private fun updateCollectView(isCollected: Boolean) {
if (isCollected) {
mBinding.bottomController.tvCollect.text = "已收藏"
mBinding.bottomController.ivCollect.setImageResource(R.drawable.community_content_detail_collect_select)
mBinding.bottomController.tvCollect.setTextColor(ContextCompat.getColor(requireContext(), R.color.theme_font))
mBinding.bottomController.bottomStarIv.setImageResource(R.drawable.community_content_detail_collect_select)
mBinding.bottomController.bottomStarTv.text = "已收藏"
mBinding.bottomController.bottomStarTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.theme_font))
} else {
mBinding.bottomController.tvCollect.text = "收藏"
mBinding.bottomController.ivCollect.setImageResource(R.drawable.community_content_detail_collect_unselect)
mBinding.bottomController.tvCollect.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_242529))
mBinding.bottomController.bottomStarIv.setImageResource(R.drawable.community_content_detail_collect_unselect)
mBinding.bottomController.bottomStarTv.text = "收藏"
mBinding.bottomController.bottomStarTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_242529))
}
}
@ -989,7 +965,7 @@ class AnswerDetailFragment : NormalFragment() {
if (mIsShowCommentManager) {
mIsShowCommentManager = false
mBinding.bottomController.ivComment.performClick()
mBinding.bottomController.bottomCommentContainer.performClick()
}
mBinding.richEditor.clearFocus()
@ -1013,7 +989,7 @@ class AnswerDetailFragment : NormalFragment() {
private fun hideLoadingViewAndShowContent() {
mNoConn.visibility = View.GONE
mBinding.bottomController.containerControl.visibility = View.VISIBLE
mBinding.bottomController.root.visibility = View.VISIBLE
mBinding.scrollView.visibility = View.VISIBLE
}
@ -1126,21 +1102,11 @@ class AnswerDetailFragment : NormalFragment() {
}
private fun updateFollowBtn(isFollowed: Boolean) {
mBinding.followTv.visibility = View.VISIBLE
if (isFollowed) {
if (mBinding.followTv.visibility == View.GONE) return
DrawableView.setTextDrawable(mBinding.followTv, null, "已关注")
mBinding.followTv.setTextColor(R.color.text_999999.toColor())
mBinding.followTv.postDelayed({
if (context == null) return@postDelayed
mBinding.followTv.animate()
.alpha(0f)
.setInterpolator(LinearInterpolator())
.doOnEnd { context?.let { mBinding.followTv.visibility = View.GONE } }
.setDuration(500L)
.start()
}, 2000L)
} else {
mBinding.followTv.visibility = View.VISIBLE
mBinding.followTv.background = DrawableView.getOvalDrawable(R.color.background, 2F)
mBinding.followTv.setTextColor(R.color.theme_font.toColor())
DrawableView.setTextDrawable(
@ -1152,22 +1118,24 @@ class AnswerDetailFragment : NormalFragment() {
@SuppressLint("SetTextI18n")
private fun updateLikeView(alreadyVoted: Boolean, voteCount: Int) {
mBinding.bottomController.tvLike.text = "赞同 ${NumberUtils.transSimpleCount(voteCount)}"
// mBinding.bottomController.bottomLikeTv.text = "赞同 ${NumberUtils.transSimpleCount(voteCount)}"
if (alreadyVoted) {
mBinding.bottomController.ivLike.setImageResource(R.drawable.community_content_detail_vote_select)
mBinding.bottomController.tvLike.setTextColor(ContextCompat.getColor(requireContext(), R.color.theme_font))
mBinding.bottomController.bottomLikeIv.setImageResource(R.drawable.community_content_detail_vote_select)
mBinding.bottomController.bottomLikeTv.text = NumberUtils.transSimpleCount(voteCount)
mBinding.bottomController.bottomLikeTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.theme_font))
} else {
mBinding.bottomController.ivLike.setImageResource(R.drawable.community_content_detail_vote_unselect)
mBinding.bottomController.tvLike.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_242529))
mBinding.bottomController.bottomLikeIv.setImageResource(R.drawable.community_content_detail_vote_unselect)
mBinding.bottomController.bottomLikeTv.text = "赞同"
mBinding.bottomController.bottomLikeTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_242529))
}
}
private fun updateDislikeView(disliked: Boolean) {
if (disliked) {
mBinding.bottomController.ivDislike.setImageResource(R.drawable.community_content_detail_oppose_select)
} else {
mBinding.bottomController.ivDislike.setImageResource(R.drawable.community_content_detail_oppose_unselect)
}
// if (disliked) {
// mBinding.bottomController.ivDislike.setImageResource(R.drawable.community_content_detail_oppose_select)
// } else {
// mBinding.bottomController.ivDislike.setImageResource(R.drawable.community_content_detail_oppose_unselect)
// }
}
private fun showDragHintDialog() {

View File

@ -33,6 +33,7 @@ import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
import com.halo.assistant.HaloApp
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.MimeType
import org.json.JSONArray
import org.json.JSONObject
/**
@ -41,6 +42,7 @@ import org.json.JSONObject
class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
private lateinit var mMenuDraft: MenuItem
private lateinit var mMenuPost: MenuItem
private lateinit var mBinding: FragmentAnswerEditBinding
private lateinit var mViewModel: AnswerEditViewModel
@ -81,6 +83,7 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
super.onCreate(savedInstanceState)
setToolbarMenu(R.menu.menu_answer_post)
mMenuDraft = mToolbar.menu.findItem(R.id.menu_draft)
mMenuPost = mToolbar.menu.findItem(R.id.menu_answer_post)
mCommunityName = intent?.getStringExtra(EntranceUtils.KEY_COMMUNITY_NAME)
mOpenAnswerInNewPage = intent?.getBooleanExtra(EntranceUtils.KEY_ANSWER_OPEN_IN_NEW_PAGE, false)!!
mBinding = FragmentAnswerEditBinding.bind(mContentView)
@ -105,6 +108,7 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mBinding.answerPlaceholder.visibility = if (t.contains("<img src") || !TextUtils.isEmpty(mRichEditor.text)) {
View.GONE
} else View.VISIBLE
checkPostButtonEnable()
}
mRichEditor.setOnInitialLoadListener { isReady ->
@ -131,6 +135,7 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mKeyboardHeightProvider = KeyboardHeightProvider(this)
mBinding.root.post { mKeyboardHeightProvider?.start() }
checkPostButtonEnable()
}
override fun getLayoutId(): Int {
@ -245,15 +250,24 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
}
}
mViewModel.postImageLiveData.observeNonNull(this) {
if (it.status == Status.SUCCESS) {
val imageUrl = it.data!!
for (sourceImage in imageUrl.keys) {
mViewModel.mapImages.set(TextUtils.htmlEncode(sourceImage).decodeURI(), imageUrl[sourceImage]!!)
mRichEditor.insertImage(FILE_HOST + sourceImage.decodeURI())
}
mViewModel.chooseImagesUpload.observe(this, Observer {
for (key in it.keys) {
mRichEditor.focusEditor()
mRichEditor.insertPlaceholderImage(key)
}
}
})
mViewModel.chooseImagesUploadSuccess.observe(this, Observer {
val jsonArray = JSONArray()
mRichEditor.focusEditor()
for (key in it.keys) {
val jsonObject = JSONObject()
jsonObject.put("id", key)
jsonObject.put("url", it[key])
jsonArray.put(jsonObject)
}
mRichEditor.replacePlaceholderImage(jsonArray.toString())
})
mViewModel.deleteDraftLiveDate.observe(this, Observer {
if (it == true) {
@ -265,6 +279,7 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mViewModel.draftsLiveData.observe(this, Observer {
mRichEditor.setHtml(it, false)
requestFocusAndMoveCursorToEnd()
checkPostButtonEnable()
})
mViewModel.saveDraftsLiveData.observeNonNull(this) {
@ -362,29 +377,33 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mRichEditor.postDelayed({
val answerContent = getReplaceRealContent()
mRichEditor.showLinkStyle()
// filter rule
val answerLength = HtmlUtils.stripHtml(answerContent).length
if (answerLength < MIN_ANSWER_TEXT_LENGTH) {
// toast(R.string.answer_beneath_length_limit)
ToastUtils.showToast(getString(R.string.answer_beneath_length_limit), if (mIsKeyBoardShow) Gravity.CENTER else -1)
return@postDelayed
} else if (answerLength > MAX_ANSWER_TEXT_LENGTH) {
/* DialogUtils.showAlertDialog(this,
getString(R.string.answer_post_failed),
getString(R.string.answer_exceed_length_limit,
MAX_ANSWER_TEXT_LENGTH,
answerLength - MAX_ANSWER_TEXT_LENGTH),
getString(R.string.answer_resume_edit), "", {
// do nothing
}, null)*/
ToastUtils.showToast("回答最多输入10000个字", if (mIsKeyBoardShow) Gravity.CENTER else -1)
if (mRichEditor.hasPlaceholderImage()) {
ToastUtils.showToast("图片上传ing")
return@postDelayed
}
mViewModel.postAnswer(answerContent)
if (checkData(answerContent)) {
mViewModel.postAnswer(answerContent)
}
}, 100)
}
}
private fun checkData(answerContent: String, isShowToast: Boolean = true): Boolean {
val answerLength = HtmlUtils.stripHtml(answerContent).length
if (answerLength < MIN_ANSWER_TEXT_LENGTH) {
if (isShowToast) ToastUtils.showToast(getString(R.string.answer_beneath_length_limit), if (mIsKeyBoardShow) Gravity.CENTER else -1)
return false
} else if (answerLength > MAX_ANSWER_TEXT_LENGTH) {
if (isShowToast) ToastUtils.showToast("回答最多输入10000个字", if (mIsKeyBoardShow) Gravity.CENTER else -1)
return false
}
val draftValue = mViewModel.draftsLiveData.value
if (!draftValue.isNullOrEmpty()) {
return draftValue != answerContent
}
return true
}
private fun getReplaceRealContent(): String {
var answerContent = mRichEditor.html
for (s in mViewModel.mapImages.keys) {
@ -396,6 +415,12 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
return answerContent
}
private fun checkPostButtonEnable() {
val answerContent = getReplaceRealContent()
val isEnabled = checkData(answerContent, false)
mMenuPost.actionView.alpha = if (isEnabled) 1f else 0.6f
}
override fun handleBackPressed(): Boolean {
return if (TextUtils.isEmpty(UserManager.getInstance().token)) {
false
@ -410,14 +435,9 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
&& !mRichEditor.html.contains("<img src")
&& TextUtils.isEmpty(mRichEditor.text)) {
consume {
DialogUtils.showAlertDialog(
this,
"提示",
"当前内容为空,退出即会删除该草稿,确定退出吗?",
"确定",
"取消",
{ mViewModel.deleteAnswerDraft() },
null)
DialogUtils.showNewAlertDialog(this, "提示", "当前内容为空,退出即会删除该草稿,确定退出吗?", "取消", "确定", null, {
mViewModel.deleteAnswerDraft()
})
}
} else if (!TextUtils.isEmpty(mViewModel.cacheAnswerContent)
&& mViewModel.cacheAnswerContent == mRichEditor.html) {
@ -436,21 +456,17 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
}
private fun showDraftFailureDialog() {
DialogUtils.showAlertDialog(this,
"提示",
"确定退出?已撰写的内容将会丢失",
"继续撰写", "退出", null, { finish() })
DialogUtils.showNewAlertDialog(this, "提示", "确定退出?已撰写的内容将会丢失?", "退出", "继续撰写", {
finish()
}, null)
}
private fun showPatchBackDialog() {
DialogUtils.showCancelAlertDialog(this,
"提示",
"即将退出修改,是否需要将此次编辑保存到草稿箱?",
"保存草稿",
"继续退出",
{
saveDraft(true)
}, { finish() })
DialogUtils.showNewAlertDialog(this, "提示", "是否保存修改内容用于下次编辑?", "不保存", "保存并退出", {
finish()
}, {
saveDraft(true)
})
}
/**
@ -458,6 +474,7 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
*/
private fun saveDraft(forcedExit: Boolean) {
val editContent = getReplaceRealContent()
if (mRichEditor.hasPlaceholderImage()) return
if (TextUtils.isEmpty(UserManager.getInstance().token)) {
return
} else if (forcedExit && TextUtils.isEmpty(editContent)) {

View File

@ -1,12 +1,9 @@
package com.gh.gamecenter.qa.answer.edit
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import android.content.Intent
import android.text.TextUtils
import androidx.lifecycle.*
import com.gh.base.fragment.WaitingDialogFragment
import com.gh.common.syncpage.SyncDataEntity
import com.gh.common.syncpage.SyncFieldConstants
@ -56,6 +53,10 @@ class AnswerEditViewModel(application: Application,
val draftsLiveData = MediatorLiveData<String>()
val saveDraftsLiveData = MediatorLiveData<Boolean>() // 自动保存不会回调
val uploadingImage = ArrayList<HashMap<String, String>>()
val chooseImagesUpload = MutableLiveData<HashMap<String, String>>()
val chooseImagesUploadSuccess = MutableLiveData<HashMap<String, String>>()
var uploadImageSubscription: Disposable? = null
val mapImages = HashMap<String, String>()
@ -156,19 +157,30 @@ class AnswerEditViewModel(application: Application,
}
if (pictureList.size == 0) return
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("上传中...", true))
uploadImageSubscription = UploadImageUtils.compressAndUploadImageList(UploadImageUtils.UploadType.answer, pictureList
, false, object : UploadImageUtils.OnUploadImageListListener {
override fun onProgress(total: Long, progress: Long) {
var percent = (100 * (progress / total.toFloat())).toInt()
if (percent >= 100) percent = 99
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("图片上传中 $percent%", true))
}
override fun onCompressSuccess(imageUrls: List<String>) {
val chooseImageMd5Map = HashMap<String, String>()
imageUrls.forEach {
chooseImageMd5Map[MD5Utils.getUrlMD5(it)] = ""
}
uploadingImage.add(chooseImageMd5Map)
chooseImagesUpload.postValue(chooseImageMd5Map)
}
override fun onSuccess(imageUrl: LinkedHashMap<String, String>, errorMap: Map<String, Exception>) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("上传中...", false))
postImageLiveData.postValue(Resource.success(imageUrl))
val uploadMap = uploadingImage.find { it.containsKey(MD5Utils.getUrlMD5(imageUrl.entries.iterator().next().key)) }
uploadMap?.let {
for (key in imageUrl.keys) {
uploadMap[MD5Utils.getUrlMD5(key)] = AnswerEditActivity.FILE_HOST + key.decodeURI()
mapImages[TextUtils.htmlEncode(key).decodeURI()] = imageUrl[key] ?: ""
}
chooseImagesUploadSuccess.postValue(uploadMap)
uploadingImage.remove(uploadMap)
}
val errorSize = pictureList.size - imageUrl.size
if (errorSize > 0) {
for (error in errorMap.values) {
@ -182,8 +194,6 @@ class AnswerEditViewModel(application: Application,
}
override fun onError(errorMap: Map<String, Exception>) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("上传中...", false))
postImageLiveData.postValue(Resource.error(null))
val errorSize = pictureList.size
for (error in errorMap.values) {

View File

@ -44,77 +44,6 @@ class ArticleDetailContentViewHolder(var binding: ItemArticleDetailContentBindin
}
})
likeContainer.setDebouncedClickListener {
MtaHelper.onEvent("帖子详情", "内容区域", "点赞")
root.context.ifLogin("帖子详情-赞同") {
if (!article.me.isCommunityArticleVote) {
viewModel.likeArticle()
if (EntranceUtils.ENTRANCE_WELCOME == mEntrance) {
LogUtils.uploadLikeFromWelcomeDialog()
}
} else {
viewModel.cancelLikeArticle()
}
}
}
shareContainer.setOnClickListener {
MtaHelper.onEvent("帖子详情", "内容区域", "分享")
// GdtHelper.logAction(ActionType.SHARE,
// GdtHelper.CONTENT_TYPE, "QA_ARTICLE",
// GdtHelper.CONTENT_ID, viewModel.articleId)
var shareSummary = richEditor.text
val shareIcon: String = if (articleImgUrlList.size > 0) {
articleImgUrlList[0]
} else {
root.context.getString(R.string.share_ghzs_logo)
}
if (TextUtils.isEmpty(shareSummary)) {
shareSummary = root.context.getString(R.string.ask_share_default_summary)
}
val shareUrl = if (isPublishEnv()) {
root.context.getString(R.string.share_community_article_url, article.community.id, article.id)
} else {
root.context.getString(R.string.share_community_article_url_dev, article.community.id, article.id)
}
ShareUtils.getInstance(root.context).showShareWindows(
root.context as Activity?,
root,
shareUrl,
shareIcon,
root.context.getString(R.string.share_community_article_title, article.user.name, article.title, article.count.vote),
shareSummary,
ShareUtils.ShareEntrance.communityArticle,
article.id)
}
starContainer.setDebouncedClickListener {
MtaHelper.onEvent("帖子详情", "内容区域", "收藏")
root.context.ifLogin(entrance = "帖子详情-收藏") {
viewModel.collectionCommand(!article.me.isCommunityArticleFavorite, callback = {
viewModel.detailEntity?.me?.isCommunityArticleFavorite = it
if (it) {
// GdtHelper.logAction(ActionType.ADD_TO_WISHLIST,
// GdtHelper.CONTENT_TYPE, "QA_ARTICLE",
// GdtHelper.CONTENT_ID, viewModel.articleId)
}
updateCollectView(it)
})
}
}
dislikeContainer.setDebouncedClickListener {
MtaHelper.onEvent("帖子详情", "内容区域", "反对")
root.context.ifLogin("帖子详情-反对") {
if (!article.me.isCommunityArticleOppose) {
viewModel.dislikeArticle()
} else {
viewModel.cancelDislikeArticle()
}
}
}
followBtn.setOnClickListener {
MtaHelper.onEvent("帖子详情", "内容区域", "关注")
root.context.ifLogin("帖子详情-[关注]用户") {
@ -143,14 +72,6 @@ class ArticleDetailContentViewHolder(var binding: ItemArticleDetailContentBindin
}
binding.run {
if (article.me.isCommunityArticleFavorite) {
starIv.setImageResource(R.drawable.ic_article_detail_stared)
starTv.text = "已收藏"
} else {
starIv.setImageResource(R.drawable.ic_article_detail_star)
starTv.text = "收藏"
}
detail = article
userIconIv.display(article.user.border, article.user.icon, article.user.auth?.icon)
richEditor.setContentOwner(article.me.isContentOwner)
@ -160,9 +81,9 @@ class ArticleDetailContentViewHolder(var binding: ItemArticleDetailContentBindin
}
if (article.time.create == article.time.edit) {
releaseTimeTv.text = String.format("发布于 %s", NewsUtils.getFormattedTime(article.time.create))
releaseTimeTv.text = String.format("发布于%s", NewsUtils.getFormattedTime(article.time.create))
} else {
releaseTimeTv.text = String.format("修改于 %s", NewsUtils.getFormattedTime(article.time.edit))
releaseTimeTv.text = String.format("发布于%s 最后编辑于%s", NewsUtils.getFormattedTime(article.time.create), NewsUtils.getFormattedTime(article.time.edit))
}
richEditor.visibility = View.VISIBLE
@ -199,9 +120,6 @@ class ArticleDetailContentViewHolder(var binding: ItemArticleDetailContentBindin
}
}
badgeTv.setOnClickListener { badgeIv.performClick() }
updateLikeView(article.me.isCommunityArticleVote, article.count.vote)
updateDislikeView(article.me.isCommunityArticleOppose)
}
}
}
@ -213,16 +131,6 @@ class ArticleDetailContentViewHolder(var binding: ItemArticleDetailContentBindin
followBtn.text = "已关注"
followBtn.background = null
followBtn.setTextColor(ContextCompat.getColor(root.context, R.color.text_999999))
followBtn.postDelayed({
followBtn.animate()
.alpha(0f)
.setInterpolator(LinearInterpolator())
.doOnEnd {
tryWithDefaultCatch { followBtn.visibility = View.GONE }
}
.setDuration(500L)
.start()
}, 2000L)
} else {
followBtn.setRoundedColorBackground(R.color.text_EEF5FB, 14F)
followBtn.setTextColor(ContextCompat.getColor(root.context, R.color.theme_font))
@ -231,40 +139,6 @@ class ArticleDetailContentViewHolder(var binding: ItemArticleDetailContentBindin
}
}
@SuppressLint("SetTextI18n")
fun updateLikeView(alreadyLiked: Boolean, likeCount: Int) {
binding.run {
likeTv.text = if (likeCount == 0) "赞同" else likeCount.toString()
if (alreadyLiked) {
likeIv.setImageResource(R.drawable.ic_article_detail_liked)
} else {
likeIv.setImageResource(R.drawable.ic_article_detail_like)
}
}
}
fun updateDislikeView(disliked: Boolean) {
binding.run {
if (disliked) {
dislikeIv.setImageResource(R.drawable.ic_article_detail_disliked)
} else {
dislikeIv.setImageResource(R.drawable.ic_article_detail_dislike)
}
}
}
fun updateCollectView(isCollected: Boolean) {
binding.run {
if (isCollected) {
starTv.text = "已收藏"
starIv.setImageResource(R.drawable.ic_article_detail_stared)
} else {
starTv.text = "收藏"
starIv.setImageResource(R.drawable.ic_article_detail_star)
}
}
}
private fun addTag(tag: String, isCommunityName: Boolean = false) {
binding.run {
if (isCommunityName) {

View File

@ -3,15 +3,14 @@ package com.gh.gamecenter.qa.article.detail
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.lifecycle.Lifecycle
@ -37,6 +36,7 @@ import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.personal.PersonalFragment
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
import com.gh.gamecenter.qa.comment.CommentActivity
import com.gh.gamecenter.qa.dialog.MoreFunctionPanelDialog
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
import com.gh.gamecenter.suggest.SuggestType
import com.halo.assistant.HaloApp
@ -183,11 +183,29 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment<CommentItemData,
bottomLikeContainer.setOnClickListener {
MtaHelper.onEvent("帖子详情", "底部", "点赞")
mAdapter?.articleDetailVH?.binding?.likeContainer?.performClick()
requireContext().ifLogin("帖子详情-赞同") {
if (mViewModel.detailEntity?.me?.isCommunityArticleVote == false) {
mViewModel.likeArticle()
if (EntranceUtils.ENTRANCE_WELCOME == mEntrance) {
LogUtils.uploadLikeFromWelcomeDialog()
}
} else {
mViewModel.cancelLikeArticle()
}
}
}
bottomStarContainer.setOnClickListener {
MtaHelper.onEvent("帖子详情", "内容区域", "收藏")
requireContext().ifLogin(entrance = "帖子详情-收藏") {
mViewModel.collectionCommand(mViewModel.detailEntity?.me?.isCommunityArticleFavorite == false, callback = {
mViewModel.detailEntity?.me?.isCommunityArticleFavorite = it
updateCollectView(it)
})
}
}
replyTv.text = "说点什么吧"
replyTv.setRoundedColorBackground(R.color.text_F0F0F0, 19F)
replyTv.setRoundedColorBackground(R.color.text_F5F5F5, 19F)
replyTv.setDebouncedClickListener {
MtaHelper.onEvent("帖子详情", "底部", "评论输入框")
startCommentActivity()
@ -281,7 +299,6 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment<CommentItemData,
mViewModel.like.observeNonNull(this) {
if (mViewModel.detailEntity!!.me.isCommunityArticleVote) {
ToastUtils.showToast("已赞同")
mAdapter?.articleDetailVH?.updateDislikeView(false)
} else {
ToastUtils.showToast("取消赞同")
}
@ -323,7 +340,6 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment<CommentItemData,
} else {
toast("取消反对")
}
mAdapter?.articleDetailVH?.updateDislikeView(disliked)
}
}
@ -346,55 +362,57 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment<CommentItemData,
private fun showMoreItemDialog() {
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) || mViewModel.detailEntity != null) {
val view = LayoutInflater.from(requireContext()).inflate(R.layout.menu_answer_detail_more_new, null)
val popupWindow = PopupWindow(view,
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
setBackgroundDrawable(ColorDrawable(0))
isTouchable = true
isFocusable = true
isOutsideTouchable = true
}
view.measure(0, 0)
val viewWidth = view.measuredWidth
popupWindow.showAsDropDown(toolbar.menu.getItem(2).actionView, -viewWidth - 10, 10)
val container = view.findViewById<LinearLayout>(R.id.container)
val entities = ArrayList<MenuItemEntity>()
if (mViewModel.detailEntity?.user?.id == UserManager.getInstance().userId) {
entities.add(MenuItemEntity("修改", R.drawable.menu_more_edit))
entities.add(MenuItemEntity("修改", R.drawable.icon_more_panel_edit))
}
if (mViewModel.detailEntity?.user?.id != UserManager.getInstance().userId) {
entities.add(MenuItemEntity("投诉", R.drawable.ic_menu_gamedetail_feedback))
entities.add(MenuItemEntity("投诉", R.drawable.icon_gamedetail_copyright))
}
if (mViewModel.detailEntity?.me!!.isModerator) {
entities.add(MenuItemEntity("加精", R.drawable.menu_more_essence_enable,
if (mViewModel.detailEntity?.isHighlighted == true || mViewModel.detailEntity?.user?.id == UserManager.getInstance().userId)
R.drawable.menu_more_essence_unenable else 0))
val isEnable = mViewModel.detailEntity?.isHighlighted == true || mViewModel.detailEntity?.user?.id == UserManager.getInstance().userId
entities.add(MenuItemEntity("加精", if (isEnable)
R.drawable.icon_more_panel_essence_unenable else R.drawable.icon_more_panel_essence, isEnable = !isEnable))
}
if (mViewModel.detailEntity?.me!!.isModerator || mViewModel.detailEntity?.user?.id == UserManager.getInstance().userId) {
entities.add(MenuItemEntity("删除", R.drawable.menu_more_delete))
entities.add(MenuItemEntity("删除", R.drawable.icon_more_panel_delete))
}
entities.forEachIndexed { index, item ->
val menuItem = createMenuItem(index, item)
container.addView(menuItem)
menuItem.setOnClickListener {
popupWindow.dismiss()
when (item.text) {
"修改" -> {
startActivityForResult(ArticleEditActivity.getPatchIntent(requireContext(), mViewModel.detailEntity!!), ArticleDetailActivity.ARTICLE_PATCH_REQUEST)
}
"投诉" -> {
SuggestionActivity.startSuggestionActivity(requireContext(), SuggestType.normal, "report",
"帖子投诉(" + mViewModel.articleId + "")
}
"加精" -> addEssenceForum(mViewModel.detailEntity!!)
"删除" -> {
DialogUtils.showNewAlertDialog(requireContext(), "提示", "删除帖子后,其中的所有评论及回复都将被删除", "取消", "删除", {}, {
mViewModel.doHideThisArticle(mViewModel.detailEntity!!.community.id, mViewModel.articleId)
})
}
val shareUrl = if (isPublishEnv()) {
requireContext().getString(R.string.share_community_article_url, mViewModel.communityId, mViewModel.detailEntity?.id)
} else {
requireContext().getString(R.string.share_community_article_url_dev, mViewModel.communityId, mViewModel.detailEntity?.id)
}
val shareIcon: String = if (mViewModel.detailEntity?.images?.isNotEmpty() == true) {
mViewModel.detailEntity?.images!![0]
} else {
requireContext().getString(R.string.share_ghzs_logo)
}
val title = requireContext().getString(R.string.share_community_article_title, mViewModel.detailEntity?.user?.name,
mViewModel.detailEntity?.title, mViewModel.detailEntity?.count?.vote ?: 0)
val shareUtils = ShareUtils.getInstance(requireContext())
shareUtils.shareParamsDetail(requireActivity(),
shareUrl,
shareIcon,
title,
HtmlUtils.stripHtml(mViewModel.detailEntity?.content),
ShareUtils.ShareEntrance.communityArticle,
mViewModel.detailEntity?.id, null)
MoreFunctionPanelDialog.showMoreDialog(requireActivity() as AppCompatActivity, entities, mViewModel.detailEntity?.title
?: "", shareUtils) {
when (it.text) {
"修改" -> {
startActivityForResult(ArticleEditActivity.getPatchIntent(requireContext(), mViewModel.detailEntity!!), ArticleDetailActivity.ARTICLE_PATCH_REQUEST)
}
"投诉" -> {
SuggestionActivity.startSuggestionActivity(requireContext(), SuggestType.normal, "report",
"帖子投诉(" + mViewModel.articleId + "")
}
"加精" -> addEssenceForum(mViewModel.detailEntity!!)
"删除" -> {
DialogUtils.showNewAlertDialog(requireContext(), "提示", "删除帖子后,其中的所有评论及回复都将被删除", "取消", "删除", {}, {
mViewModel.doHideThisArticle(mViewModel.detailEntity!!.community.id, mViewModel.articleId)
})
}
}
}
@ -465,17 +483,31 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment<CommentItemData,
})
updateLikeView(articleDetail.me.isCommunityArticleVote, articleDetail.count.vote)
updateCollectView(articleDetail.me.isCommunityArticleFavorite)
}
@SuppressLint("SetTextI18n")
private fun updateLikeView(alreadyLiked: Boolean, likeCount: Int) {
mAdapter?.articleDetailVH?.updateLikeView(alreadyLiked, likeCount)
bottomLikeTv.text = mViewModel.getLikeText(likeCount)
mViewModel.detailEntity?.me?.isCommunityArticleVote = alreadyLiked
if (alreadyLiked) {
bottomLikeIv.setImageResource(R.drawable.ic_article_detail_liked)
bottomLikeTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.theme_font))
} else {
bottomLikeIv.setImageResource(R.drawable.ic_article_detail_like_bottom_bar)
bottomLikeTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_666666))
}
}
private fun updateCollectView(isCollected: Boolean) {
if (isCollected) {
bottomStarTv.text = "已收藏"
bottomStarIv.setImageResource(R.drawable.ic_article_detail_stared)
bottomStarTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.theme_font))
} else {
bottomStarTv.text = "收藏"
bottomStarIv.setImageResource(R.drawable.ic_article_detail_star)
bottomStarTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_666666))
}
}

View File

@ -23,6 +23,7 @@ import com.gh.gamecenter.databinding.ItemArticleDetailCommentFooterBinding
import com.gh.gamecenter.databinding.PieceArticleDetailCommentFilterBinding
import com.gh.gamecenter.entity.CommentEntity
import com.gh.gamecenter.qa.article.detail.comment.ArticleDetailCommentActivity
import com.gh.gamecenter.qa.comment.CommentActivity
import com.gh.gamecenter.qa.comment.OnCommentOptionClickListener
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
@ -245,13 +246,13 @@ abstract class BaseArticleDetailCommentAdapter(context: Context,
?: "")
binding.floorHintTv.text = if (comment.floor != 0) "${comment.floor}" else ""
binding.root.setOnClickListener {
ArticleDetailCommentActivity.getIntent(binding.root.context, comment.id!!, viewModel.communityId, viewModel.articleId, false, comment.floor, entrance, PATH_ARTICLE_DETAIL).apply {
CommentActivity.getArticleDetailCommentIntent(binding.root.context, comment.id!!, viewModel.communityId, viewModel.articleId, false, comment.floor, entrance, PATH_ARTICLE_DETAIL).apply {
binding.root.context.startActivity(this)
}
MtaHelper.onEvent("帖子详情", "全部评论", "评论正文")
}
binding.commentCountTv.setOnClickListener {
ArticleDetailCommentActivity.getIntent(binding.root.context, comment.id!!, viewModel.communityId, viewModel.articleId, true, comment.floor, entrance, PATH_ARTICLE_DETAIL).apply {
CommentActivity.getArticleDetailCommentIntent(binding.root.context, comment.id!!, viewModel.communityId, viewModel.articleId, false, comment.floor, entrance, PATH_ARTICLE_DETAIL).apply {
binding.root.context.startActivity(this)
}
MtaHelper.onEvent("帖子详情", "全部评论", "回复")
@ -397,12 +398,12 @@ abstract class BaseArticleDetailCommentAdapter(context: Context,
})
}
"置顶" -> {
DialogUtils.showNewAlertDialog(binding.root.context, "提示", "是否将此条评论置顶?", "取消", "确认", null, Gravity.CENTER, {}, {
DialogUtils.showNewAlertDialog(binding.root.context, "提示", "是否将此条评论置顶?", "取消", "确认", null, Gravity.CENTER, false, {}, {
commentTop(binding.root.context, viewModel, comment, false)
})
}
"取消置顶" -> {
DialogUtils.showNewAlertDialog(binding.root.context, "提示", "是否将此条评论取消置顶?", "取消", "确认", null, Gravity.CENTER, {}, {
DialogUtils.showNewAlertDialog(binding.root.context, "提示", "是否将此条评论取消置顶?", "取消", "确认", null, Gravity.CENTER, false, {}, {
viewModel.updateCommentTop(comment.id
?: "", top = false, isAgain = false) { isSuccess, errorCode ->
if (isSuccess) {
@ -432,7 +433,7 @@ abstract class BaseArticleDetailCommentAdapter(context: Context,
viewModel.load(LoadType.REFRESH)
} else {
if (errorCode == 403095) {
DialogUtils.showNewAlertDialog(context, "提示", "当前已有置顶评论,\n是否将此条评论覆盖展示?", "取消", "确认", null, Gravity.CENTER, {}, {
DialogUtils.showNewAlertDialog(context, "提示", "当前已有置顶评论,\n是否将此条评论覆盖展示?", "取消", "确认", null, Gravity.CENTER, false, {}, {
commentTop(context, viewModel, comment, true)
})
}

View File

@ -4,6 +4,7 @@ import android.graphics.drawable.InsetDrawable
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.annotation.Nullable
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
@ -12,6 +13,9 @@ import com.gh.common.view.CustomDividerItemDecoration
import com.gh.common.view.vertical_recycler.SnappingLinearLayoutManager
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.ListFragment
import com.gh.gamecenter.qa.article.detail.comment.ArticleDetailCommentFragment
import com.gh.gamecenter.qa.comment.CommentActivity
import kotterknife.bindView
abstract class BaseArticleDetailCommentFragment<T, VM : BaseArticleDetailCommentViewModel> : ListFragment<T, VM>() {
@ -39,6 +43,7 @@ abstract class BaseArticleDetailCommentFragment<T, VM : BaseArticleDetailComment
mListRv.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (this@BaseArticleDetailCommentFragment is ArticleDetailCommentFragment) return
val firstCompletelyVisiblePosition = mLayoutManager.findFirstCompletelyVisibleItemPosition()
if (RecyclerView.NO_POSITION == firstCompletelyVisiblePosition) return
@ -60,11 +65,11 @@ abstract class BaseArticleDetailCommentFragment<T, VM : BaseArticleDetailComment
}
})
filterLatestTv.setOnClickListener {
filterLatestTv?.setOnClickListener {
getFilterVH()?.binding?.filterLatestTv?.performClick()
updateFilterView()
}
filterOldestTv.setOnClickListener {
filterOldestTv?.setOnClickListener {
getFilterVH()?.binding?.filterOldestTv?.performClick()
updateFilterView()
}
@ -95,7 +100,12 @@ abstract class BaseArticleDetailCommentFragment<T, VM : BaseArticleDetailComment
override fun getItemDecoration(): RecyclerView.ItemDecoration {
val insetDivider = InsetDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.divider_article_detail_comment), DisplayUtils.dip2px(20F), 0, DisplayUtils.dip2px(20F), 0)
val itemDecoration = CustomDividerItemDecoration(requireContext(), notDecorateTheFirstTwoItems = true, notDecorateTheLastItem = true)
val itemDecoration = if (this !is ArticleDetailCommentFragment) {
CustomDividerItemDecoration(requireContext(), notDecorateTheFirstTwoItems = true, notDecorateTheLastItem = true)
} else {
CustomDividerItemDecoration(requireContext(), notDecorateTheFirstItem = true, notDecorateTheLastItem = true)
}
itemDecoration.setDrawable(insetDivider)
return itemDecoration
}

View File

@ -75,16 +75,20 @@ abstract class BaseArticleDetailCommentViewModel(application: Application, var a
}
}
fun mergeListData(commentList: List<CommentEntity>?, displayFloor: Boolean = false) {
fun mergeListData(commentList: List<CommentEntity>?, displayFloor: Boolean = false, hasFilter: Boolean = true) {
topItemData?.let {
val mergedList = arrayListOf<CommentItemData>().apply {
if (mResultLiveData.value?.firstOrNull() != null) {
// 保持头两个 item 的内存地址不变
add(mResultLiveData.value!![0])
add(mResultLiveData.value!![1])
if (hasFilter) {
if (mResultLiveData.value?.firstOrNull() != null) {
// 保持头两个 item 的内存地址不变
add(mResultLiveData.value!![0])
add(mResultLiveData.value!![1])
} else {
add(topItemData!!)
add(CommentItemData(filter = true))
}
} else {
add(topItemData!!)
add(CommentItemData(filter = true))
}
if (commentList.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_EMPTY) {
add(CommentItemData(errorEmpty = true))

View File

@ -65,9 +65,9 @@ class ArticleDetailCommentAdapter(context: Context,
binding.comment = comment
binding.moreIv.visibility = View.GONE
binding.divider.visibility = View.VISIBLE
binding.commentCountTv.visibility = View.GONE
binding.floorHintTv.text = if (comment.floor != 0) "${comment.floor}" else ""
binding.commentCountTv.text = mViewModel.getCommentText(comment.reply, "回复")
binding.commentCountTv.setOnClickListener { commentClosure?.invoke(comment) }
binding.contentTv.text = comment.content
binding.contentTv.maxLines = Int.MAX_VALUE

View File

@ -8,6 +8,7 @@ import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat
import com.ethanhua.skeleton.Skeleton
import com.gh.common.AppExecutor
import com.gh.common.util.*
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.ListAdapter
@ -45,7 +46,6 @@ class ArticleDetailCommentFragment : BaseArticleDetailCommentFragment<CommentIte
super.onViewCreated(view, savedInstanceState)
initView()
initToolbar()
initObserver()
}
@ -55,8 +55,8 @@ class ArticleDetailCommentFragment : BaseArticleDetailCommentFragment<CommentIte
if (commentCount != null && commentCount != 0) {
mViewModel.commentDetail?.reply = commentCount
mViewModel.commentCount = commentCount
updateFilterView()
mViewModel.load(LoadType.REFRESH)
replyCountTv.text = "${mViewModel.commentDetail?.reply}条回复"
}
}
@ -81,14 +81,10 @@ class ArticleDetailCommentFragment : BaseArticleDetailCommentFragment<CommentIte
}
private fun initView() {
ViewCompat.setOnApplyWindowInsetsListener(toolbarContainer) { _, insets ->
(toolbarContainer.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop
insets.consumeSystemWindowInsets()
}
mSkeletonScreen = Skeleton.bind(skeletonView).shimmer(false).load(R.layout.fragment_article_detail_comment_skeleton).show()
bottomLikeContainer.visibility = View.GONE
bottomStarContainer.visibility = View.GONE
bottomCommentContainer.visibility = View.GONE
val lp = replyTv.layoutParams as ConstraintLayout.LayoutParams
@ -99,46 +95,7 @@ class ArticleDetailCommentFragment : BaseArticleDetailCommentFragment<CommentIte
replyTv.setDebouncedClickListener {
mViewModel.commentDetail?.let { startCommentActivity(it) }
}
}
private fun initToolbar() {
toolbar.inflateMenu(R.menu.menu_answer)
toolbar.setNavigationOnClickListener { onBackPressed() }
toolbar.menu.findItem(R.id.menu_more).setOnMenuItemClickListener {
consume { mViewModel.commentDetail?.let { showMoreItemDialog(it, mViewModel.articleId, mViewModel.communityId) } }
}
toolbar.menu.findItem(R.id.menu_question_and_answer).isVisible = false
}
private fun showMoreItemDialog(commentDetail: CommentEntity, articleId: String, communityId: String) {
if (isAdded) {
CommentHelper.showCommunityArticleCommentOptions(
toolbar.menu.getItem(2).actionView,
commentDetail,
false,
articleId,
communityId,
isShowTop = false,
ignoreModerator = false,
listener = object : OnCommentOptionClickListener {
override fun onCommentOptionClick(entity: CommentEntity, option: String) {
when (option) {
"删除评论" -> {
DialogUtils.showNewAlertDialog(requireContext(), "提示", "删除评论后,评论下所有的回复都将被删除", "取消", "删除", null, {
mViewModel.hideCommunityArticleComment(entity.id ?: "") {
EventBus.getDefault().post(EBDeleteCommentDetail(entity.id))
requireActivity().finish()
}
})
}
else -> {
//do nothing
}
}
}
})
}
closeIv.setOnClickListener { requireActivity().finish() }
}
override fun onBackPressed(): Boolean {
@ -170,18 +127,18 @@ class ArticleDetailCommentFragment : BaseArticleDetailCommentFragment<CommentIte
}
replyTv.text = "回复:${mViewModel.commentDetail?.user?.name}"
replyCountTv.text = "${mViewModel.commentDetail?.reply}条回复"
AppExecutor.uiExecutor.executeWithDelay(Runnable {
if ((mAdapter?.entityList?.size ?: 0) > 2) {
mListRv.smoothScrollToPosition(1)
}
}, 100)
}
else -> {
if (it == BaseArticleDetailCommentViewModel.LoadResult.DELETED) {
mReuseNoConn?.visibility = View.GONE
mReuseNoData?.visibility = View.VISIBLE
toast(R.string.content_delete_toast)
toolbar.menu?.run {
for (i in 0 until size()) {
getItem(i).isVisible = false
}
}
} else {
mReuseNoConn?.visibility = View.VISIBLE
mReuseNoData?.visibility = View.GONE

View File

@ -26,7 +26,7 @@ class ArticleDetailCommentViewModel(application: Application,
override fun provideDataObservable(page: Int): Observable<List<CommentEntity>>? = null
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData) { mergeListData(it) }
mResultLiveData.addSource(mListLiveData) { mergeListData(it, hasFilter = false) }
}
override fun provideDataSingle(page: Int): Single<MutableList<CommentEntity>> {
@ -46,7 +46,7 @@ class ArticleDetailCommentViewModel(application: Application,
commentCount = data.reply
topItemData = CommentItemData(commentTop = data)
loadResultLiveData.postValue(LoadResult.SUCCESS)
mergeListData(mListLiveData.value)
mergeListData(mListLiveData.value, hasFilter = false)
}
override fun onFailure(exception: Exception) {

View File

@ -3,10 +3,13 @@ package com.gh.gamecenter.qa.article.draft
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.util.EntranceUtils
import com.gh.common.util.UrlFilterUtils
import com.gh.common.util.checkStoragePermissionBeforeAction
import com.gh.common.view.CustomDividerItemDecoration
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.ListFragment
import com.gh.gamecenter.baselist.LoadType
@ -89,6 +92,12 @@ class ArticleDraftFragment : ListFragment<ArticleDraftEntity, NormalListViewMode
}
}
override fun getItemDecoration(): RecyclerView.ItemDecoration? {
val insetDivider = ContextCompat.getDrawable(requireContext(), R.drawable.divider_item_line_space_16)
val itemDecoration = CustomDividerItemDecoration(requireContext(), notDecorateTheLastItem = true)
itemDecoration.setDrawable(insetDivider!!)
return itemDecoration
}
private fun deleteDraft(entity: ArticleDraftEntity) {
mApi.deleteArticleDrafts(entity.community.id, entity.id)
@ -96,7 +105,15 @@ class ArticleDraftFragment : ListFragment<ArticleDraftEntity, NormalListViewMode
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
mListViewModel.load(LoadType.REFRESH)
val index = mAdapter?.entityList?.indexOf(entity) ?: -1
if (index >= 0) {
mAdapter?.entityList?.remove(entity)
if (mAdapter?.entityList.isNullOrEmpty()) {
mListViewModel.load(LoadType.REFRESH)
} else {
mAdapter?.notifyItemRemoved(index)
}
}
}
override fun onFailure(e: HttpException?) {

View File

@ -12,16 +12,11 @@ import android.text.TextUtils
import android.view.Gravity
import android.view.MenuItem
import android.view.View
import android.view.Window
import android.view.inputmethod.InputMethodManager
import android.widget.*
import androidx.core.content.ContextCompat
import androidx.core.widget.NestedScrollView
import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import butterknife.OnClick
import com.gh.base.BaseRichEditorActivity
import com.gh.base.fragment.WaitingDialogFragment
@ -32,9 +27,9 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.entity.CommunityEntity
import com.gh.gamecenter.eventbus.EBReuse
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.mvvm.Status
import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity
import com.gh.gamecenter.qa.article.draft.ArticleDraftActivity
import com.gh.gamecenter.qa.dialog.ChooseForumDialogFragment
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
import com.gh.gamecenter.qa.entity.ArticleDraftEntity
import com.gh.gamecenter.qa.questions.edit.TagsSelectFragment
@ -44,6 +39,8 @@ import com.zhihu.matisse.Matisse
import com.zhihu.matisse.MimeType
import kotterknife.bindView
import org.greenrobot.eventbus.EventBus
import org.json.JSONArray
import org.json.JSONObject
class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
@ -81,7 +78,8 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
if (mViewModel.mSelectCommunityData != null
&& !TextUtils.isEmpty(mEditTitle.text.trim())
&& (!TextUtils.isEmpty(mRichEditor.text)
|| mRichEditor.html.contains("<img src"))) {
|| mRichEditor.html.contains("<img src"))
&& !mRichEditor.hasPlaceholderImage()) {
mViewModel.title = mEditTitle.text.toString()
mViewModel.content = getReplaceRealContent()
mViewModel.postArticleDrafts(SaveDraftType.AUTO)
@ -140,8 +138,6 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
//setEditTextInputSpace()
mEditTitle.filters = arrayOf(TextHelper.getFilter(50, "标题最多50个字"))
mEditTitle.requestFocus()
mEditTitle.doOnTextChanged { text, start, count, after ->
if (text?.contains("\n") == true) {
mEditTitle.setText(text.toString().replace("\n", ""))
@ -160,25 +156,31 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
}
private fun observeData() {
// 上传图片完成后的回调(本地图片)
mViewModel.postImageLiveData.observe(this, Observer {
if (it?.status == Status.SUCCESS) {
val imageUrl = it.data!!
for (sourceImage in imageUrl.keys) {
mViewModel.mapImg[TextUtils.htmlEncode(sourceImage).decodeURI()] = imageUrl[sourceImage]!!
mRichEditor.focusEditor()
mRichEditor.insertImage(AnswerEditActivity.FILE_HOST + sourceImage.decodeURI())
closeExtendedKeyboard()
AppExecutor.uiExecutor.executeWithDelay(Runnable {
Util_System_Keyboard.showSoftKeyboard(this)
},100)
AppExecutor.uiExecutor.executeWithDelay(Runnable {
mRichEditor.scrollTo(0,1000000)
},500)
}
checkPostButtonEnable()
mViewModel.chooseImagesUpload.observe(this, Observer {
for (key in it.keys) {
mRichEditor.focusEditor()
mRichEditor.insertPlaceholderImage(key)
}
})
mViewModel.chooseImagesUploadSuccess.observe(this, Observer {
val jsonArray = JSONArray()
mRichEditor.focusEditor()
for (key in it.keys) {
val jsonObject = JSONObject()
jsonObject.put("id", key)
jsonObject.put("url", it[key])
jsonArray.put(jsonObject)
}
mRichEditor.replacePlaceholderImage(jsonArray.toString())
closeExtendedKeyboard()
AppExecutor.uiExecutor.executeWithDelay(Runnable {
Util_System_Keyboard.showSoftKeyboard(this)
}, 100)
AppExecutor.uiExecutor.executeWithDelay(Runnable {
mRichEditor.scrollTo(0, 1000000)
}, 500)
checkPostButtonEnable()
})
// 保存草稿
mViewModel.postArticleDrafts.observe(this, Observer { pair ->
@ -190,7 +192,7 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
EventBus.getDefault().post(EBReuse(ARTICLE_DRAFT_CHANGE_TAG))
finish()
} else {
showDraftFailureDialog()
//showDraftFailureDialog()
}
}
SaveDraftType.AUTO -> {
@ -279,14 +281,16 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
setNavigationTitle("发布帖子")
mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong())
mViewModel.mSelectCommunityData = intent.getParcelableExtra(CommunityEntity::class.java.simpleName)
if (mViewModel.mSelectCommunityData == null) {
showSelectGameDialog()
} else {
if (mViewModel.mSelectCommunityData != null) {
setGameName()
mGameName.isEnabled = false
mGameName.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
}
mEditTitle.requestFocus()
}
mViewModel.notSelectForum.observe(this, Observer {
if (it) showSelectGameDialog()
})
}
override fun getSelectedLabel(): Int = mViewModel.selectedTags.size
@ -356,7 +360,6 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mViewModel.mSelectCommunityData?.icon = mViewModel.draftEntity?.community?.game?.getIcon()
mViewModel.mSelectCommunityData?.iconSubscript = mViewModel.draftEntity?.community?.game?.iconSubscript
mEditTitle.setText(mViewModel.draftEntity?.title)
mMenuDraft.isVisible = false
mGameName.isEnabled = true
setGameName()
mViewModel.getArticleDraftsContent(mViewModel.draftEntity?.id!!)
@ -368,7 +371,8 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mViewModel.mSelectCommunityData?.iconSubscript = mViewModel.detailEntity?.community?.game?.iconSubscript
setGameName()
mMenuDraft.isVisible = true
//编辑帖子草稿箱入口不存在
mMenuDraft.isVisible = false
mGameName.isEnabled = false
mGameName.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
@ -386,9 +390,6 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mRichEditor.setHtml(html, false)
try {
mRichEditor.scrollTo(0, 10000000)
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(mRichEditor, InputMethodManager.SHOW_IMPLICIT)
mRichEditor.postDelayed({ mRichEditor.focusEditor() }, 800)
} catch (e: Exception) {
e.printStackTrace()
}
@ -413,69 +414,57 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
return false
}
if (mViewModel.detailEntity != null) {
return if (!TextUtils.isEmpty(mEditTitle.text)
|| mRichEditor.html.contains("<img src")
|| !TextUtils.isEmpty(mRichEditor.text)) {
showPatchBackDialog()
true
} else {
false
}
}
return !checkDraft(SaveDraftType.EXIT)
}
private fun checkDraft(saveType: SaveDraftType): Boolean {
if (mViewModel.detailEntity != null) return true
val draftEntity = mViewModel.draftEntity
// 内容为空直接弹窗
if (saveType == SaveDraftType.EXIT
&& TextUtils.isEmpty(mEditTitle.text)
&& !mRichEditor.html.contains("<img src")
&& TextUtils.isEmpty(mRichEditor.text)
&& mViewModel.draftEntity != null) {
DialogUtils.showAlertDialog(
this,
"提示",
"当前内容为空,退出即会删除该草稿,确定退出吗?",
"确定",
"取消",
{
mViewModel.deleteDraft()
finish()
},
null)
return false
}
// 检查草稿内容是否有变动
if (draftEntity != null) {
if (draftEntity.community.id == mViewModel.mSelectCommunityData?.id &&
draftEntity.title == mEditTitle.text.toString() &&
mViewModel.articleDraftsContent.value == mRichEditor.html) {
//帖子发布,需判断是否输入了内容,输入了则弹窗提示
if (mViewModel.detailEntity == null && mViewModel.draftEntity == null) {
if (mViewModel.mSelectCommunityData != null
&& (mEditTitle.text.toString().isNotEmpty()
|| mRichEditor.html.isNotEmpty())) {
DialogUtils.showNewAlertDialog(this, "提示", "是否保存内容再退出?", "不保存", "保存并退出", Gravity.CENTER, true, {
finish()
}, {
mViewModel.title = mEditTitle.text.toString()
mViewModel.content = getReplaceRealContent()
mViewModel.postArticleDrafts(SaveDraftType.EXIT)
})
return true
}
}
if (mRichEditor.html.contains("<img src")
|| !TextUtils.isEmpty(mRichEditor.text)
|| !TextUtils.isEmpty(mEditTitle.text.trim())) {
if (mViewModel.mSelectCommunityData == null) {
if (saveType == SaveDraftType.EXIT) {
showDraftFailureDialog()
} else {
toast("请选择论坛")
}
//帖子编辑,需要判断是否修改过
if (mViewModel.detailEntity != null) {
return if (mViewModel.detailEntity?.community?.id != mViewModel.mSelectCommunityData?.id
|| mViewModel.detailEntity?.title != mEditTitle.text.toString()
|| mViewModel.detailEntity?.content != mRichEditor.html) {
showBackDialog()
true
} else false
}
//检查草稿数据
return !checkDraft(SaveDraftType.EXIT)
}
private fun checkDraft(saveType: SaveDraftType): Boolean {
val draftEntity = mViewModel.draftEntity ?: return true
if (draftEntity.title.isEmpty() && draftEntity.content.isEmpty()) return true
if (saveType == SaveDraftType.SKIP) {
//判断是否修改了草稿,修改了需自动保存无需提示
if (draftEntity.community.id != mViewModel.mSelectCommunityData?.id
|| draftEntity.title != mEditTitle.text.toString()
|| mViewModel.articleDraftsContent.value != mRichEditor.html) {
mViewModel.title = mEditTitle.text.toString()
mViewModel.content = getReplaceRealContent()
mViewModel.postArticleDrafts(SaveDraftType.AUTO)
return true
}
} else if (saveType == SaveDraftType.EXIT) {
//退出页面需判断是否修改了草稿,修改了需弹窗提示
if (draftEntity.community.id != mViewModel.mSelectCommunityData?.id
|| draftEntity.title != mEditTitle.text.toString()
|| mViewModel.articleDraftsContent.value != mRichEditor.html) {
showBackDialog()
return false
}
mViewModel.title = mEditTitle.text.toString()
mViewModel.content = getReplaceRealContent()
mViewModel.postArticleDrafts(saveType)
return false
}
return true
}
@ -489,41 +478,22 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
}, 100)
}
private fun showPatchBackDialog() {
DialogUtils.showCancelAlertDialog(this, "提示",
"即将退出修改,是否需要将此次编辑保存到草稿箱?",
"保存草稿",
" 继续退出",
{
mViewModel.title = mEditTitle.text.toString()
mViewModel.content = getReplaceRealContent()
mViewModel.postArticleDrafts(SaveDraftType.EXIT)
},
{ finish() })
private fun showBackDialog() {
if (mRichEditor.hasPlaceholderImage()) return
DialogUtils.showNewAlertDialog(this, "提示", "是否保存修改内容用于下次编辑?", "不保存", "保存并退出", Gravity.CENTER, true, {
finish()
}, {
mViewModel.title = mEditTitle.text.toString()
mViewModel.content = getReplaceRealContent()
mViewModel.postArticleDrafts(SaveDraftType.EXIT)
})
}
private fun showSelectGameDialog() {
val selectGameDialog = Dialog(this)
val view = View.inflate(this, R.layout.dialog_article_game, null)
val recyclerView = view.findViewById<RecyclerView>(R.id.dialog_game_list)
val back = view.findViewById<View>(R.id.dialog_back)
val loading = view.findViewById<ProgressBar>(R.id.dialog_loading)
back.setOnClickListener {
selectGameDialog.cancel()
if (mViewModel.mSelectCommunityData == null) finish()
}
recyclerView.layoutManager = GridLayoutManager(this, 4)
recyclerView.adapter = ArticleSelectGameAdapter(this, loading) {
mViewModel.mSelectCommunityData = CommunityEntity(it.id, it.name, icon = it.game.icon, iconSubscript = it.game.iconSubscript)
ChooseForumDialogFragment.show(this) {
mViewModel.mSelectCommunityData = it
setGameName()
selectGameDialog.cancel()
}
selectGameDialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
selectGameDialog.setCanceledOnTouchOutside(false)
selectGameDialog.setContentView(view)
selectGameDialog.show()
}
private fun setGameName() {
@ -552,6 +522,10 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mViewModel.title = mEditTitle.text.toString()
mViewModel.content = getReplaceRealContent()
mRichEditor.showLinkStyle()
if (mRichEditor.hasPlaceholderImage()) {
ToastUtils.showToast("图片上传ing")
return@postDelayed
}
if (mViewModel.checkDataAndLoadTitleTag(mIsKeyBoardShow)) {
mArticleTagsSelectFragment?.publishArticle()
}

View File

@ -14,6 +14,7 @@ import com.gh.gamecenter.entity.CommunityEntity
import com.gh.gamecenter.eventbus.EBReuse
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.mvvm.Resource
import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
import com.gh.gamecenter.qa.entity.ArticleDraftEntity
import com.gh.gamecenter.qa.questions.edit.QuestionEditViewModel
@ -34,6 +35,8 @@ import org.json.JSONObject
import retrofit2.HttpException
import java.io.File
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
class ArticleEditViewModel(application: Application) : AndroidViewModel(application) {
@ -43,11 +46,15 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat
val MAX_ARTICLE_TEXT_LENGTH = 10000
val processDialog = MediatorLiveData<WaitingDialogFragment.WaitingDialogData>()
val postImageLiveData = MediatorLiveData<Resource<LinkedHashMap<String, String>>>()
val postArticle = MediatorLiveData<String>()
val postArticleDrafts = MediatorLiveData<Pair<ArticleEditActivity.SaveDraftType, Boolean>>()
val articleDraftsContent = MediatorLiveData<String>()
val error = MutableLiveData<String>()
val notSelectForum = MutableLiveData<Boolean>()
val uploadingImage = ArrayList<HashMap<String, String>>()
val chooseImagesUpload = MutableLiveData<HashMap<String, String>>()
val chooseImagesUploadSuccess = MutableLiveData<HashMap<String, String>>()
var uploadImageSubscription: Disposable? = null
@ -88,25 +95,17 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat
* 根据问题标题获取相应标签(问题编辑无需获取)
*/
fun checkDataAndLoadTitleTag(isKeyBoardShow: Boolean = false): Boolean {
if (mSelectCommunityData == null) {
ToastUtils.showToast("请选择论坛", if (isKeyBoardShow) Gravity.CENTER else -1)
return false
}
if (TextUtils.isEmpty(title)) {
ToastUtils.showToast("标题不能为空", if (isKeyBoardShow) Gravity.CENTER else -1)
return false
}
// 检查标题长度限制
title?.trim()
// title = title?.replace(" ", "")?.replace("\n", "")
title = title?.replace("\n", "")
if (title!!.length < QuestionEditViewModel.QUESTION_TITLE_MIN_LENGTH) {
ToastUtils.showToast("标题至少${QuestionEditViewModel.QUESTION_TITLE_MIN_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1)
return false
}
val articleContent = HtmlUtils.stripHtml(content).length
if (articleContent < MIN_ARTICLE_TEXT_LENGTH) {
ToastUtils.showToast("正文至少${QuestionEditViewModel.QUESTION_TITLE_MIN_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1)
@ -116,6 +115,12 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat
ToastUtils.showToast("帖子最多输入${MAX_ARTICLE_TEXT_LENGTH}个字", if (isKeyBoardShow) Gravity.CENTER else -1)
return false
}
if (detailEntity != null && detailEntity?.title == title && HtmlUtils.stripHtml(detailEntity?.content) == HtmlUtils.stripHtml(content)) return false
if (mSelectCommunityData == null) {
ToastUtils.showToast("请选择论坛", if (isKeyBoardShow) Gravity.CENTER else -1)
notSelectForum.postValue(true)
return false
}
return true
}
@ -127,32 +132,10 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat
val articleContent = HtmlUtils.stripHtml(content).length
if (articleContent < MIN_ARTICLE_TEXT_LENGTH) return false
if (articleContent > MAX_ARTICLE_TEXT_LENGTH) return false
if (detailEntity != null && detailEntity?.title == title && HtmlUtils.stripHtml(detailEntity?.content) == HtmlUtils.stripHtml(content)) return false
return true
}
/**
* 根据问题标题获取相应标签(标签默认选中)
*/
fun loadTitleTags() {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true))
mApi
.getQuestionTagsByTitle(mSelectCommunityData?.id, UrlFilterUtils.getFilterQuery("title", title))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<List<String>>() {
override fun onResponse(response: List<String>?) {
titleTags.postValue(response)
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", false))
}
override fun onFailure(e: HttpException?) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", false))
Utils.toast(getApplication(), R.string.request_failure_normal_hint)
}
})
}
/**
* 检查图片是否符合规则并上传图片
* @param picPath 图片本地路径
@ -177,19 +160,32 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat
}
if (pictureList.size == 0) return
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("上传中...", true))
uploadImageSubscription = UploadImageUtils.compressAndUploadImageList(UploadImageUtils.UploadType.answer, pictureList
, false, object : UploadImageUtils.OnUploadImageListListener {
override fun onProgress(total: Long, progress: Long) {
var percent = (100 * (progress / total.toFloat())).toInt()
if (percent >= 100) percent = 99
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("图片上传中 $percent%", true))
}
override fun onCompressSuccess(imageUrls: List<String>) {
val chooseImageMd5Map = HashMap<String, String>()
imageUrls.forEach {
chooseImageMd5Map[MD5Utils.getUrlMD5(it)] = ""
}
uploadingImage.add(chooseImageMd5Map)
chooseImagesUpload.postValue(chooseImageMd5Map)
}
override fun onSuccess(imageUrl: LinkedHashMap<String, String>, errorMap: Map<String, Exception>) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("上传中...", false))
postImageLiveData.postValue(Resource.success(imageUrl))
val uploadMap = uploadingImage.find { it.containsKey(MD5Utils.getUrlMD5(imageUrl.entries.iterator().next().key)) }
uploadMap?.let {
for (key in imageUrl.keys) {
uploadMap[MD5Utils.getUrlMD5(key)] = AnswerEditActivity.FILE_HOST + key.decodeURI()
mapImg[TextUtils.htmlEncode(key).decodeURI()] = imageUrl[key] ?: ""
}
chooseImagesUploadSuccess.postValue(uploadMap)
uploadingImage.remove(uploadMap)
}
val errorSize = pictureList.size - imageUrl.size
if (errorSize > 0) {
for (error in errorMap.values) {
@ -203,7 +199,6 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat
}
override fun onError(errorMap: Map<String, Exception>) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("上传中...", false))
val errorSize = pictureList.size
for (error in errorMap.values) {
@ -217,7 +212,6 @@ class ArticleEditViewModel(application: Application) : AndroidViewModel(applicat
} else {
Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败")
}
postImageLiveData.postValue(Resource.error(null))
}
})
}

View File

@ -8,10 +8,14 @@ import android.view.View
import butterknife.OnClick
import com.gh.base.BaseActivity
import com.gh.common.util.DisplayUtils
import com.gh.common.util.EntranceUtils
import com.gh.common.util.doOnEnd
import com.gh.gamecenter.NormalActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.CommentEntity
import com.gh.gamecenter.qa.answer.detail.AnswerDetailFragment
import com.gh.gamecenter.qa.article.detail.comment.ArticleDetailCommentActivity
import com.gh.gamecenter.qa.article.detail.comment.ArticleDetailCommentFragment
import com.lightgame.utils.Util_System_Keyboard
import kotlinx.android.synthetic.main.activity_comment.*
@ -39,6 +43,7 @@ class CommentActivity : BaseActivity() {
val commentEntity: CommentEntity? = intent.getParcelableExtra(COMMENT_ENTITY) ?: null
val useReplyApi: Boolean = intent.getBooleanExtra(USE_REPLY_API, false)
val articleCommentId = intent.getStringExtra(EntranceUtils.KEY_ARTICLE_COMMENT_ID)
mShowInputOnly = intent.getBooleanExtra(SHOW_INPUT_ONLY, false)
@ -68,16 +73,18 @@ class CommentActivity : BaseActivity() {
} else if (!articleId.isNullOrEmpty()) {
NewCommentFragment.getCommunityArticleCommentInstance(
articleId,
communityId?:"",
communityId,
showKeyboard,
commentCount,
mShowInputOnly,
useReplyApi,
commentEntity,
commentCallback)
} else if (!articleCommentId.isNullOrEmpty()) {
ArticleDetailCommentFragment().with(intent.extras)
} else {
NewCommentFragment.getVideoCommentInstance(
videoId?:"",
videoId,
showKeyboard,
commentCount,
isVideoAuthor,
@ -86,7 +93,7 @@ class CommentActivity : BaseActivity() {
}
}
supportFragmentManager.beginTransaction().replace(R.id.answerCommentPlaceholderView, commentFragment, NewCommentFragment::class.java.simpleName).commitNowAllowingStateLoss()
supportFragmentManager.beginTransaction().replace(R.id.answerCommentPlaceholderView, commentFragment!!, NewCommentFragment::class.java.simpleName).commitNowAllowingStateLoss()
maskView.alpha = 0f
if (videoId.isNullOrEmpty()) {
@ -158,6 +165,26 @@ class CommentActivity : BaseActivity() {
return intent
}
@JvmStatic
fun getArticleDetailCommentIntent(context: Context,
commentId: String,
communityId: String,
articleId: String,
showKeyboard: Boolean = false,
position: Int = -1,
entrance: String,
path: String): Intent {
val intent = Intent(context, CommentActivity::class.java)
intent.putExtra(EntranceUtils.KEY_ENTRANCE, NormalActivity.mergeEntranceAndPath(entrance, path))
intent.putExtra(EntranceUtils.KEY_COMMUNITY_ARTICLE_ID, articleId)
intent.putExtra(EntranceUtils.KEY_COMMUNITY_ID, communityId)
intent.putExtra(EntranceUtils.KEY_POSITION, position)
intent.putExtra(EntranceUtils.KEY_ARTICLE_COMMENT_ID, commentId)
intent.putExtra(EntranceUtils.KEY_SHOW_KEYBOARD_IF_NEEDED, showKeyboard)
intent.putExtra(EntranceUtils.KEY_PATH, path)
return intent
}
@JvmStatic
fun getArticleCommentIntent(context: Context,
articleId: String,

View File

@ -1,6 +1,7 @@
package com.gh.gamecenter.qa.comment
import android.content.Context
import android.text.SpannableStringBuilder
import android.text.TextUtils
import android.view.View
import android.view.ViewGroup
@ -14,6 +15,7 @@ import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
import com.gh.gamecenter.baselist.ListAdapter
import com.gh.gamecenter.entity.CommentEntity
import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity
import com.gh.gamecenter.qa.article.detail.BaseArticleDetailCommentAdapter
class NewCommentAdapter(context: Context,
var mViewModel: NewCommentViewModel,
@ -27,7 +29,7 @@ class NewCommentAdapter(context: Context,
val view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)
FooterViewHolder(view)
} else {
val view = mLayoutInflater.inflate(R.layout.comment_item, parent, false)
val view = mLayoutInflater.inflate(R.layout.new_comment_item, parent, false)
AnswerCommentViewHolder(view)
}
}
@ -55,11 +57,29 @@ class NewCommentAdapter(context: Context,
val commentEntity = mEntityList[position]
CommentUtils.setCommentUserView(mContext, holder, commentEntity)
CommentUtils.setCommentTime(holder.commentTimeTv, commentEntity.time)
if (commentEntity.parentUser != null) {
val prefix = "回复"
val colon = " "
val parentUserName = " ${commentEntity.parentUser?.name} "
if (!TextUtils.isEmpty(mViewModel.videoId)) {
holder.commentContentTv.setTextWithHighlightedTextWrappedInsideWrapper(
text = commentEntity.content ?: "",
copyClickedText = true)
val prefixSpan = SpanBuilder(prefix).color(holder.itemView.context, 0, prefix.length, R.color.text_999999).build()
val parentUserNameSpan = SpanBuilder(parentUserName)
.click(0, parentUserName.length, R.color.text_666666) {
DirectUtils.directToHomeActivity(holder.itemView.context, commentEntity.user.id, 1, mEntrance, "")
}.build()
val colonSpan = SpanBuilder(colon).color(holder.itemView.context, 0, colon.length, R.color.text_999999).build()
val authorSpan = if (commentEntity.parentUser?.me?.isCommentOwner == true) {
SpanBuilder("作者").image(0, "作者".length, R.drawable.ic_hint_author).build()
} else {
""
}
holder.commentContentTv.text = SpannableStringBuilder()
.append(prefixSpan)
.append(parentUserNameSpan)
.append(authorSpan)
.append(colonSpan)
.append(commentEntity.content)
} else {
holder.commentContentTv.text = commentEntity.content
}
@ -69,43 +89,6 @@ class NewCommentAdapter(context: Context,
return@OnLongClickListener true
})
val parentUser = commentEntity.parentUser
if (parentUser != null && !TextUtils.isEmpty(parentUser.name)) {
holder.quoteContainer.visibility = View.VISIBLE
holder.quoteAuthorTv.text = String.format("@%s", parentUser.name)
val comment: String?
if (parentUser.active) {
comment = parentUser.comment
holder.quoteContentTv.setTextColor(mContext.resources.getColor(R.color.text_5d5d5d))
} else {
comment = mContext.getString(R.string.comment_hide_hint)
holder.quoteContentTv.setTextColor(mContext.resources.getColor(R.color.text_d5d5d5))
}
if (!TextUtils.isEmpty(mViewModel.videoId)) {
holder.quoteContentTv.setTextWithHighlightedTextWrappedInsideWrapper(
text = comment ?: "",
copyClickedText = true)
} else {
holder.quoteContentTv.text = comment
}
} else {
holder.quoteContainer.visibility = View.GONE
}
if (parentUser?.active == true) {
holder.quoteContentTv.setOnLongClickListener(View.OnLongClickListener {
isChildLongClick = true
parentUser.comment?.replace(RatingEditActivity.LABEL_REPLACE_REGEX.toRegex(), "")?.copyTextAndToast()
return@OnLongClickListener true
})
}
if (parentUser?.badge != null) {
holder.quoteAuthorBadgeSdv.visibility = View.VISIBLE
ImageUtils.display(holder.quoteAuthorBadgeSdv, parentUser.badge!!.icon)
} else {
holder.quoteAuthorBadgeSdv.visibility = View.GONE
}
val key = when (mEntrance) {
"(答案详情-评论列表)" -> "回答详情-评论管理"
"(文章详情-评论列表)" -> "社区文章详情-评论管理"

View File

@ -0,0 +1,74 @@
package com.gh.gamecenter.qa.dialog
import android.app.Dialog
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentTransaction
import com.gh.base.fragment.BaseDialogFragment
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.DialogChooseForumBinding
import com.gh.gamecenter.entity.CommunityEntity
import com.gh.gamecenter.forum.select.ForumSelectFragment
import com.halo.assistant.HaloApp
class ChooseForumDialogFragment : BaseDialogFragment() {
private lateinit var binding: DialogChooseForumBinding
var onSelectCallback: ((entity: CommunityEntity) -> Unit)? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DialogChooseForumBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val beginTransaction = childFragmentManager.beginTransaction()
val forumSelectFragment = childFragmentManager.findFragmentByTag(ForumSelectFragment::class.java.simpleName) as? ForumSelectFragment
?: ForumSelectFragment()
forumSelectFragment.onSelectCallback = {
onSelectCallback?.invoke(it)
dismissAllowingStateLoss()
}
beginTransaction.replace(binding.container.id, forumSelectFragment, ForumSelectFragment::class.java.simpleName)
beginTransaction.commitAllowingStateLoss()
binding.closeIv.setOnClickListener { dismissAllowingStateLoss() }
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val createDialog = super.onCreateDialog(savedInstanceState)
createDialog.setCanceledOnTouchOutside(true)
val window = createDialog.window
window?.setGravity(Gravity.BOTTOM)
window?.setWindowAnimations(R.style.community_publication_animation)
return createDialog
}
override fun onStart() {
super.onStart()
val width = HaloApp.getInstance().application.resources.displayMetrics.widthPixels
val height = dialog?.window?.attributes?.height ?: ViewGroup.LayoutParams.WRAP_CONTENT
dialog?.window?.setLayout(width, height)
}
companion object {
fun show(activity: AppCompatActivity, onSelectCallback: ((entity: CommunityEntity) -> Unit)) {
var fragment = activity.supportFragmentManager.findFragmentByTag(ChooseForumDialogFragment::class.java.name) as? ChooseForumDialogFragment
if (fragment == null) {
fragment = ChooseForumDialogFragment()
fragment.onSelectCallback = onSelectCallback
fragment.show(activity.supportFragmentManager, ChooseForumDialogFragment::class.java.name)
} else {
fragment.onSelectCallback = onSelectCallback
val transaction: FragmentTransaction = activity.supportFragmentManager.beginTransaction()
transaction.show(fragment)
transaction.commit()
}
}
}
}

View File

@ -0,0 +1,193 @@
package com.gh.gamecenter.qa.dialog
import android.app.Dialog
import android.os.Bundle
import android.view.*
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentTransaction
import com.gh.base.fragment.BaseDialogFragment
import com.gh.common.util.MtaHelper
import com.gh.common.util.ShareUtils
import com.gh.common.util.dip2px
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.DialogGameDetailMoreBinding
import com.gh.gamecenter.entity.MenuItemEntity
import com.halo.assistant.HaloApp
class MoreFunctionPanelDialog : BaseDialogFragment(), View.OnTouchListener {
private lateinit var binding: DialogGameDetailMoreBinding
private lateinit var mGestureDetector: GestureDetector
private var mInitPositionY = 0f
var menuItems: ArrayList<MenuItemEntity> = arrayListOf()
var title: String = ""
var shareUrl: String = ""
var shareUtils: ShareUtils? = null
var onItemClickCallback: ((menuItem: MenuItemEntity) -> Unit)? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DialogGameDetailMoreBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.gameNameTv.text = title
binding.gameNameTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_666666))
binding.gameIconView.visibility = View.GONE
binding.feedbackTv.visibility = View.GONE
binding.copyrightTv.visibility = View.GONE
mGestureDetector = GestureDetector(requireContext(), SingleTapConfirm())
binding.dragClose.setOnTouchListener(this)
binding.shareWechatTv.setOnClickListener {
shareUtils?.wechatShare()
MtaHelper.onEvent("内容分享", "微信好友", shareUtils?.title)
}
binding.sharePyquanTv.setOnClickListener {
shareUtils?.wechatMomentsShare()
MtaHelper.onEvent("内容分享", "微信朋友圈", shareUtils?.title)
}
binding.shareQqTv.setOnClickListener {
shareUtils?.qqShare()
MtaHelper.onEvent("内容分享", "QQ好友", shareUtils?.title)
}
binding.shareQqzoneTv.setOnClickListener {
shareUtils?.qZoneShare()
MtaHelper.onEvent("内容分享", "QQ空间", shareUtils?.title)
}
binding.shareWeiboTv.setOnClickListener {
shareUtils?.sinaWeiboShare()
MtaHelper.onEvent("内容分享", "新浪微博", shareUtils?.title)
}
binding.shareSmsTv.setOnClickListener {
shareUtils?.shortMessageShare()
MtaHelper.onEvent("内容分享", "短信", shareUtils?.title)
}
binding.copyLinkTv.setOnClickListener {
shareUtils?.copyLink(shareUrl)
MtaHelper.onEvent("内容分享", "复制链接", shareUtils?.title)
}
binding.cancelTv.setOnClickListener {
dismissAllowingStateLoss()
}
addActionItem()
}
private fun addActionItem() {
menuItems.forEachIndexed { _, menuItemEntity ->
val itemView = createItemView(menuItemEntity)
itemView.setOnClickListener {
onItemClickCallback?.invoke(menuItemEntity)
dismissAllowingStateLoss()
}
binding.actionContainer.addView(itemView)
}
}
private fun createItemView(itemEntity: MenuItemEntity): View {
val params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
params.leftMargin = 16f.dip2px()
return TextView(requireContext()).apply {
textSize = 11f
text = itemEntity.text
setTextColor(ContextCompat.getColor(requireContext(), if (itemEntity.isEnable) R.color.text_666666 else R.color.text_999999))
includeFontPadding = false
gravity = Gravity.CENTER
layoutParams = params
compoundDrawablePadding = 8f.dip2px()
setCompoundDrawablesWithIntrinsicBounds(null, ContextCompat.getDrawable(requireContext(), itemEntity.normalIcon), null, null)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val createDialog = super.onCreateDialog(savedInstanceState)
createDialog.setCanceledOnTouchOutside(true)
val window = createDialog.window
window?.setGravity(Gravity.BOTTOM)
window?.setWindowAnimations(R.style.community_publication_animation)
return createDialog
}
override fun onTouch(v: View, event: MotionEvent): Boolean {
if (mGestureDetector.onTouchEvent(event) && binding.root.y == 0F) {
v.performClick()
return true
}
when (event.action) {
MotionEvent.ACTION_DOWN -> {
mInitPositionY = binding.root.y - event.rawY
}
MotionEvent.ACTION_MOVE -> {
val offsetY = event.rawY + mInitPositionY
val dialogY = binding.root.y
if (dialogY + offsetY > 0) {
binding.root.animate()
.y(offsetY)
.setDuration(0)
.start()
} else {
resetDialogPosition()
}
}
MotionEvent.ACTION_CANCEL,
MotionEvent.ACTION_UP,
MotionEvent.ACTION_OUTSIDE -> {
if (binding.root.y >= binding.root.height / 2) {
dismissAllowingStateLoss()
} else {
resetDialogPosition(300)
}
}
else -> return false
}
return true
}
private fun resetDialogPosition(duration: Long = 0) {
binding.root.animate()
.y(0F)
.setDuration(duration)
.start()
}
override fun onStart() {
super.onStart()
val width = HaloApp.getInstance().application.resources.displayMetrics.widthPixels
val height = dialog?.window?.attributes?.height ?: ViewGroup.LayoutParams.WRAP_CONTENT
dialog?.window?.setLayout(width, height)
}
private class SingleTapConfirm : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(event: MotionEvent): Boolean {
return true
}
}
companion object {
@JvmStatic
fun showMoreDialog(activity: AppCompatActivity, menuItems: ArrayList<MenuItemEntity>, title: String, shareUtils: ShareUtils, onItemClickCallback: (menuItem: MenuItemEntity) -> Unit) {
if (menuItems.isNullOrEmpty()) return
var fragment = activity.supportFragmentManager.findFragmentByTag(MoreFunctionPanelDialog::class.java.name) as? MoreFunctionPanelDialog
if (fragment == null) {
fragment = MoreFunctionPanelDialog()
fragment.menuItems = menuItems
fragment.title = title
fragment.shareUtils = shareUtils
fragment.onItemClickCallback = onItemClickCallback
fragment.show(activity.supportFragmentManager, MoreFunctionPanelDialog::class.java.name)
} else {
fragment.menuItems = menuItems
fragment.title = title
fragment.shareUtils = shareUtils
fragment.onItemClickCallback = onItemClickCallback
val transaction: FragmentTransaction = activity.supportFragmentManager.beginTransaction()
transaction.show(fragment)
transaction.commit()
}
}
}
}

View File

@ -7,6 +7,7 @@ import androidx.fragment.app.Fragment
import com.gh.base.BaseActivity_TabLayout
import com.gh.gamecenter.qa.article.draft.ArticleDraftFragment
import com.gh.gamecenter.qa.answer.draft.AnswerDraftFragment
import com.gh.gamecenter.qa.questions.draft.QuestionDraftFragment
class CommunityDraftWrapperActivity : BaseActivity_TabLayout() {
@ -16,13 +17,13 @@ class CommunityDraftWrapperActivity : BaseActivity_TabLayout() {
}
override fun initFragmentList(fragments: MutableList<Fragment>) {
fragments.add(AnswerDraftFragment())
fragments.add(ArticleDraftFragment())
fragments.add(QuestionDraftFragment())
}
override fun initTabTitleList(tabTitleList: MutableList<String>) {
tabTitleList.add("回答草稿")
tabTitleList.add("帖子草稿")
tabTitleList.add("问题草稿")
}
companion object {

View File

@ -1,14 +1,38 @@
package com.gh.gamecenter.qa.editor
import android.os.Bundle
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
import com.gh.common.util.viewModelProvider
import com.gh.common.view.CustomDividerItemDecoration
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.ListFragment
import com.gh.gamecenter.qa.entity.AnswerEntity
class AnswerFragment : ListFragment<AnswerEntity, AnswerViewModel>() {
private var mAdapter: AnswerAdapter? = null
private var mAnswerType: InsertAnswerWrapperActivity.AnswerType = InsertAnswerWrapperActivity.AnswerType.MINE_ANSWER
override fun onCreate(savedInstanceState: Bundle?) {
mAnswerType = arguments?.getSerializable(InsertAnswerWrapperActivity.KEY_ANSWER_TYPE) as InsertAnswerWrapperActivity.AnswerType
super.onCreate(savedInstanceState)
}
override fun provideListViewModel(): AnswerViewModel {
return viewModelProvider(AnswerViewModel.Factory(mAnswerType))
}
override fun provideListAdapter(): AnswerAdapter {
if (mAdapter == null) mAdapter = AnswerAdapter(requireContext(), mEntrance)
return mAdapter!!
}
override fun getItemDecoration(): ItemDecoration? {
val insetDivider = ContextCompat.getDrawable(requireContext(), R.drawable.divider_item_line)
val itemDecoration = CustomDividerItemDecoration(requireContext(), notDecorateTheLastItem = true)
itemDecoration.setDrawable(insetDivider!!)
return itemDecoration
}
}

View File

@ -2,16 +2,29 @@ package com.gh.gamecenter.qa.editor
import android.app.Application
import androidx.annotation.Keep
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.baselist.ListViewModel
import com.gh.gamecenter.entity.UserEntity
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 com.lightgame.utils.Utils
import io.reactivex.Observable
@Keep
class AnswerViewModel(application: Application) : ListViewModel<AnswerEntity, AnswerEntity>(application) {
class AnswerViewModel(application: Application, private val answerType: InsertAnswerWrapperActivity.AnswerType) : ListViewModel<AnswerEntity, AnswerEntity>(application) {
override fun provideDataObservable(page: Int): Observable<List<AnswerEntity>> {
return RetrofitManager.getInstance(getApplication()).api.getCollectionAnswer(UserManager.getInstance().userId, page)
return if (answerType == InsertAnswerWrapperActivity.AnswerType.MINE_ANSWER) {
RetrofitManager.getInstance(getApplication()).api.getMyAnswers(UserManager.getInstance().userId, page,
HaloApp.getInstance().channel, Utils.getTime(getApplication()))
} else {
RetrofitManager.getInstance(getApplication()).api.getCollectionAnswer(UserManager.getInstance().userId, page)
}
}
override fun mergeResultLiveData() {
@ -28,6 +41,20 @@ class AnswerViewModel(application: Application) : ListViewModel<AnswerEntity, An
}
i++
}
if (answerType == InsertAnswerWrapperActivity.AnswerType.MINE_ANSWER) {
list.forEach {
val userInfoEntity = UserManager.getInstance().userInfoEntity
it.user = UserEntity(userInfoEntity?.icon, userInfoEntity?.name, userInfoEntity?.userId,
auth = userInfoEntity?.auth, badge = userInfoEntity?.badge)
}
}
return list
}
class Factory(private val answerType: InsertAnswerWrapperActivity.AnswerType) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return AnswerViewModel(HaloApp.getInstance().application, answerType) as T
}
}
}

View File

@ -1,14 +1,37 @@
package com.gh.gamecenter.qa.editor
import android.os.Bundle
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.util.viewModelProvider
import com.gh.common.view.CustomDividerItemDecoration
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.ListFragment
import com.gh.gamecenter.qa.entity.ArticleEntity
class ArticleFragment : ListFragment<ArticleEntity, ArticleViewModel>() {
private var mAdapter: ArticleAdapter? = null
private var mArticleType: InsertArticleWrapperActivity.ArticleType = InsertArticleWrapperActivity.ArticleType.MINE_ARTICLE
override fun onCreate(savedInstanceState: Bundle?) {
mArticleType = arguments?.getSerializable(InsertArticleWrapperActivity.KEY_ARTICLE_TYPE) as InsertArticleWrapperActivity.ArticleType
super.onCreate(savedInstanceState)
}
override fun provideListViewModel(): ArticleViewModel {
return viewModelProvider(ArticleViewModel.Factory(mArticleType))
}
override fun provideListAdapter(): ArticleAdapter {
if (mAdapter == null) mAdapter = ArticleAdapter(context!!, mEntrance)
if (mAdapter == null) mAdapter = ArticleAdapter(requireContext(), mEntrance)
return mAdapter!!
}
override fun getItemDecoration(): RecyclerView.ItemDecoration? {
val insetDivider = ContextCompat.getDrawable(requireContext(), R.drawable.divider_item_line)
val itemDecoration = CustomDividerItemDecoration(requireContext(), notDecorateTheLastItem = true)
itemDecoration.setDrawable(insetDivider!!)
return itemDecoration
}
}

View File

@ -2,17 +2,25 @@ package com.gh.gamecenter.qa.editor
import android.app.Application
import androidx.annotation.Keep
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.baselist.ListViewModel
import com.gh.gamecenter.entity.UserEntity
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.qa.entity.ArticleEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.Observable
@Keep
class ArticleViewModel(application: Application) : ListViewModel<ArticleEntity, ArticleEntity>(application) {
class ArticleViewModel(application: Application, private val articleType: InsertArticleWrapperActivity.ArticleType) : ListViewModel<ArticleEntity, ArticleEntity>(application) {
override fun provideDataObservable(page: Int): Observable<List<ArticleEntity>> {
return RetrofitManager.getInstance(getApplication()).api.getCollectionCommunityArticle(UserManager.getInstance().userId, page)
return if (articleType == InsertArticleWrapperActivity.ArticleType.MINE_ARTICLE) {
RetrofitManager.getInstance(HaloApp.getInstance().application).api.getMyArticle(UserManager.getInstance().userId, page)
} else {
RetrofitManager.getInstance(getApplication()).api.getCollectionCommunityArticle(UserManager.getInstance().userId, page)
}
}
override fun mergeResultLiveData() {
@ -29,7 +37,21 @@ class ArticleViewModel(application: Application) : ListViewModel<ArticleEntity,
}
i++
}
if (articleType == InsertArticleWrapperActivity.ArticleType.MINE_ARTICLE) {
val userInfoEntity = UserManager.getInstance().userInfoEntity
list.forEach {
it.user = UserEntity(userInfoEntity?.icon, userInfoEntity?.name, userInfoEntity?.userId,
auth = userInfoEntity?.auth, badge = userInfoEntity?.badge)
}
}
return list
}
class Factory(private val articleType: InsertArticleWrapperActivity.ArticleType) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ArticleViewModel(HaloApp.getInstance().application, articleType) as T
}
}
}

View File

@ -3,37 +3,35 @@ package com.gh.gamecenter.qa.editor
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import com.gh.base.BaseActivity_TabLayout
import com.gh.gamecenter.R
class InsertAnswerWrapperActivity : BaseActivity_TabLayout() {
private lateinit var mFragment: LinkFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setNavigationTitle("插入回答")
}
override fun getLayoutId(): Int = R.layout.activity_tablayout_no_title_viewpager
override fun initFragmentList(fragments: MutableList<Fragment>?) {
mFragment = LinkFragment()
fragments?.add(mFragment)
fragments?.add(AnswerFragment())
fragments?.add(AnswerFragment().with(bundleOf(KEY_ANSWER_TYPE to AnswerType.MINE_ANSWER)))
fragments?.add(AnswerFragment().with(bundleOf(KEY_ANSWER_TYPE to AnswerType.COLLECTION_ANSWER)))
}
override fun initTabTitleList(tabTitleList: MutableList<String>?) {
tabTitleList?.add("输入链接")
tabTitleList?.add("我的回答")
tabTitleList?.add("收藏回答")
}
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
mFragment.onPageChanged(position)
}
companion object {
const val KEY_ANSWER_TYPE = "AnswerType"
fun getIntent(context: Context): Intent {
return Intent(context, InsertAnswerWrapperActivity::class.java)
}
}
enum class AnswerType {
MINE_ANSWER,
COLLECTION_ANSWER
}
}

View File

@ -3,37 +3,36 @@ package com.gh.gamecenter.qa.editor
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import com.gh.base.BaseActivity_TabLayout
import com.gh.gamecenter.R
class InsertArticleWrapperActivity : BaseActivity_TabLayout() {
private lateinit var mFragment: LinkFragment
override fun getLayoutId(): Int = R.layout.activity_tablayout_no_title_viewpager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setNavigationTitle("插入帖子")
}
override fun initFragmentList(fragments: MutableList<androidx.fragment.app.Fragment>?) {
mFragment = LinkFragment()
fragments?.add(mFragment)
fragments?.add(ArticleFragment())
override fun initFragmentList(fragments: MutableList<Fragment>?) {
fragments?.add(ArticleFragment().with(bundleOf(KEY_ARTICLE_TYPE to ArticleType.MINE_ARTICLE)))
fragments?.add(ArticleFragment().with(bundleOf(KEY_ARTICLE_TYPE to ArticleType.COLLECTION_ARTICLE)))
}
override fun initTabTitleList(tabTitleList: MutableList<String>?) {
tabTitleList?.add("输入链接")
tabTitleList?.add("我的帖子")
tabTitleList?.add("收藏帖子")
}
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
mFragment.onPageChanged(position)
}
companion object {
const val KEY_ARTICLE_TYPE = "ArticleType"
fun getIntent(context: Context): Intent {
return Intent(context, InsertArticleWrapperActivity::class.java)
}
}
enum class ArticleType {
MINE_ARTICLE,
COLLECTION_ARTICLE
}
}

View File

@ -40,6 +40,7 @@ class AnswerEntity() : Parcelable {
@SerializedName("sequence_id")
var sequenceId: String? = null
@SerializedName("brief", alternate = ["content"])
var brief: String? = null
@SerializedName("title")
@ -108,6 +109,10 @@ class AnswerEntity() : Parcelable {
@Ignore
var description: String? = ""
@Ignore
@SerializedName("popular_answer")
var popularAnswer: AnswerEntity? = null
fun getPassVideos(): List<CommunityVideoEntity> {
val passVideos = arrayListOf<CommunityVideoEntity>()
for (video in videos) {

View File

@ -0,0 +1,20 @@
package com.gh.gamecenter.qa.entity
import android.os.Parcelable
import com.gh.gamecenter.entity.CommunityEntity
import com.google.gson.annotations.SerializedName
import kotlinx.android.parcel.Parcelize
@Parcelize
data class QuestionDraftEntity(
@SerializedName("_id")
var id: String = "",
@SerializedName("community_id")
var communityId:String="",
var bbs: CommunityEntity? = null,
var title: String = "",
var description: String = "",
var images: ArrayList<String> = arrayListOf(),
var videos: ArrayList<CommunityVideoEntity> = arrayListOf(),
var tags: List<String> = arrayListOf()
) : Parcelable

View File

@ -29,7 +29,11 @@ data class QuestionsDetailEntity(var id: String? = null,
var me: MeEntity = MeEntity(),
@SerializedName("follow_count")
private var followCount: Int = 0,
var user: UserEntity = UserEntity()) : Parcelable {
var user: UserEntity = UserEntity(),
//提交问题用
@SerializedName("draft_id")
var draftId: String = ""
) : Parcelable {
fun getFollowCount(): Int {
return if (followCount == 0) 1 else {

View File

@ -127,10 +127,10 @@ public class AnswerViewHolder extends BaseRecyclerViewHolder<AnswerEntity> {
mContent.setText(entity.getBrief());
mQuestionTitle.setVisibility(View.VISIBLE);
mQuestionTitle.setText(entity.getQuestions().getTitle());
mVotecount.setText(mVotecount.getContext().getString(R.string.ask_vote_count, NumberUtils.transSimpleCount(entity.getVote())));
// 3.6.3 产品说把右下角的社区名字换成时间, 评论隐藏掉
String commentText = String.format("%s评论 · %s点赞 · %s", NumberUtils.transSimpleCount(entity.getCommentCount()), NumberUtils.transSimpleCount(entity.getVote()), NewsUtils.getFormattedTime(entity.getTime()));
mVotecount.setText(commentText);
mCommunityName.setVisibility(View.VISIBLE);
mCommunityName.setText(NewsUtils.getFormattedTime(entity.getTime()));
mCommunityName.setText(entity.getCommunityName());
mCommentCount.setVisibility(View.GONE);
// mCommentCount.setText(String.format("%s 评论", NumberUtils.transSimpleCount(entity.getCommentCount())));
// mCommunityName.setText(entity.getCommunityName());

View File

@ -4,7 +4,6 @@ import android.app.Activity
import android.app.Dialog
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.os.Parcelable
import android.text.TextUtils
@ -14,15 +13,14 @@ import android.view.View
import android.view.Window
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import com.ethanhua.skeleton.Skeleton
import com.gh.base.ToolBarActivity
import com.gh.base.fragment.BaseDialogWrapperFragment
import com.gh.base.fragment.BaseFragment
import com.gh.common.TimeElapsedHelper
@ -46,6 +44,7 @@ import com.gh.gamecenter.personal.PersonalFragment.LOGIN_TAG
import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity
import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity
import com.gh.gamecenter.qa.answer.fold.AnswerFoldActivity
import com.gh.gamecenter.qa.dialog.MoreFunctionPanelDialog
import com.gh.gamecenter.qa.entity.AnswerDraftEntity
import com.gh.gamecenter.qa.entity.Questions
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
@ -418,88 +417,55 @@ class QuestionsDetailFragment :
private fun showMoreItemDialog() {
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
mQuestionsDetailEntity?.let { questionEntity ->
val view = LayoutInflater.from(context).inflate(R.layout.menu_answer_detail_more_new, null)
val popupWindow = PopupWindow(
view,
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
popupWindow.apply {
setBackgroundDrawable(ColorDrawable(0))
isTouchable = true
isFocusable = true
isOutsideTouchable = true
}
view.measure(0, 0)
val viewWidth = view.measuredWidth
popupWindow.showAsDropDown((activity as ToolBarActivity).menu.getItem(2).actionView, -viewWidth - 10, 10)
val container = view.findViewById<LinearLayout>(R.id.container)
val entities = ArrayList<MenuItemEntity>()
entities.add(MenuItemEntity("首页", R.drawable.menu_more_home))
if (questionEntity.user.id != UserManager.getInstance().userId) {
entities.add(MenuItemEntity("投诉", R.drawable.ic_menu_gamedetail_feedback))
entities.add(MenuItemEntity("投诉", R.drawable.icon_gamedetail_copyright))
}
if (questionEntity.me.isModerator) {
entities.add(MenuItemEntity("编辑", R.drawable.menu_more_edit))
entities.add(MenuItemEntity("编辑", R.drawable.icon_more_panel_edit))
}
entities.add(MenuItemEntity("分享", R.drawable.icon_share_gray))
if (questionEntity.me.isModerator || questionEntity.user.id == UserManager.getInstance().userId) {
entities.add(MenuItemEntity("删除", R.drawable.menu_more_delete))
entities.add(MenuItemEntity("删除", R.drawable.icon_more_panel_delete))
}
val shareIcon = if (questionEntity.images.isNotEmpty()) {
questionEntity.images[0]
} else {
getString(R.string.share_ghzs_logo)
}
var description = questionEntity.description
if (TextUtils.isEmpty(description)) {
description = getString(R.string.ask_share_default_summary)
}
val shareUrl = if (isPublishEnv()) {
getString(R.string.share_questions_url, questionEntity.id)
} else {
getString(R.string.share_questions_url_dev, questionEntity.id)
}
val shareUtils = ShareUtils.getInstance(activity)
shareUtils.shareParamsDetail(
activity,
shareUrl,
shareIcon,
getString(R.string.ask_share_questions_title, questionEntity.title, questionEntity.answersCount),
description,
ShareUtils.ShareEntrance.askNormal,
questionEntity.id, null)
MoreFunctionPanelDialog.showMoreDialog(requireActivity() as AppCompatActivity, entities, questionEntity.title
?: "",shareUtils) {
when (it.text) {
"投诉" -> {
SuggestionActivity.startSuggestionActivity(context, SuggestType.normal, "report",
"问题投诉(" + questionEntity.id + "")
}
"编辑" -> {
val intent = QuestionEditActivity.getManagerIntent(requireContext(), mQuestionsDetailEntity!!)
startActivityForResult(intent, QUESTIONS_EDIT_REQUEST)
}
entities.forEachIndexed { index, item ->
val menuItem = createMenuItem(index, item)
container.addView(menuItem)
menuItem.setOnClickListener {
popupWindow.dismiss()
when (item.text) {
"首页" -> {
DirectUtils.directToCommunity(requireContext(), questionEntity.community)
MtaHelper.onEvent("回到首页", "问题详情", questionEntity.community.name + "+" + StringUtils.combineTwoString(questionEntity.title, questionEntity.id))
}
"投诉" -> {
SuggestionActivity.startSuggestionActivity(context, SuggestType.normal, "report",
"问题投诉(" + questionEntity.id + "")
}
"编辑" -> {
val intent = QuestionEditActivity.getManagerIntent(requireContext(), mQuestionsDetailEntity!!)
startActivityForResult(intent, QUESTIONS_EDIT_REQUEST)
}
"分享" -> {
// GdtHelper.logAction(ActionType.SHARE,
// GdtHelper.CONTENT_TYPE, "QUESTION",
// GdtHelper.CONTENT_ID, mQuestionsId)
popupWindow.dismiss()
val shareIcon = if (questionEntity.images.isNotEmpty()) {
questionEntity.images[0]
} else {
getString(R.string.share_ghzs_logo)
}
var description = questionEntity.description
if (TextUtils.isEmpty(description)) {
description = getString(R.string.ask_share_default_summary)
}
val shareUrl = if (isPublishEnv()) {
getString(R.string.share_questions_url, questionEntity.id)
} else {
getString(R.string.share_questions_url_dev, questionEntity.id)
}
ShareUtils.getInstance(activity).showShareWindows(
activity,
view,
shareUrl,
shareIcon,
getString(R.string.ask_share_questions_title, questionEntity.title, questionEntity.answersCount),
description,
ShareUtils.ShareEntrance.askNormal,
questionEntity.id)
}
"删除" -> {
DialogUtils.showNewAlertDialog(requireContext(), "提示", "删除问题后,其中的所有回答都将被删除", "取消", "删除", {}, {
mListViewModel.moderatorsHideQuestion()
})
}
"删除" -> {
DialogUtils.showNewAlertDialog(requireContext(), "提示", "删除问题后,其中的所有回答都将被删除", "取消", "删除", {}, {
mListViewModel.moderatorsHideQuestion()
})
}
}
}

View File

@ -0,0 +1,17 @@
package com.gh.gamecenter.qa.questions.draft
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.common.util.EntranceUtils
import com.gh.gamecenter.NormalActivity
class QuestionDraftActivity : NormalActivity() {
companion object {
fun getIntent(context: Context): Intent {
val bundle = Bundle()
return getTargetIntent(context, QuestionDraftActivity::class.java, QuestionDraftFragment::class.java, bundle)
}
}
}

View File

@ -0,0 +1,71 @@
package com.gh.gamecenter.qa.questions.draft
import android.content.Context
import android.text.TextUtils
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.gh.base.BaseRecyclerViewHolder
import com.gh.common.constant.ItemViewType
import com.gh.common.util.DialogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
import com.gh.gamecenter.baselist.ListAdapter
import com.gh.gamecenter.databinding.CommunityQuestionDraftItemBinding
import com.gh.gamecenter.qa.entity.QuestionDraftEntity
class QuestionDraftAdapter(val context: Context, val mViewModel: QuestionDraftViewModel?,private val selectCallback: (QuestionDraftEntity) -> Unit) : ListAdapter<QuestionDraftEntity>(context) {
override fun areItemsTheSame(oldItem: QuestionDraftEntity, newItem: QuestionDraftEntity): Boolean {
return !TextUtils.isEmpty(oldItem.id) && oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: QuestionDraftEntity, newItem: QuestionDraftEntity): Boolean {
return oldItem == newItem
}
override fun getItemViewType(position: Int): Int {
return if (position == itemCount - 1) {
ItemViewType.ITEM_FOOTER
} else 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_question_draft_item, parent, false)
QuestionDraftViewHolder(CommunityQuestionDraftItemBinding.bind(view))
}
}
}
override fun getItemCount(): Int {
return if (mEntityList == null || mEntityList.isEmpty()) 0 else mEntityList.size + FOOTER_ITEM_COUNT
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is QuestionDraftViewHolder) {
val entity = mEntityList[position]
holder.binding.data = entity
holder.binding.articleDraftDelete.setOnClickListener {
DialogUtils.showAlertDialog(mContext, "警告", "确定要删除问题草稿吗?删除之后不可恢复",
"确定", "取消", DialogUtils.ConfirmListener {
mViewModel?.deleteDraft(entity.id)
}, null)
}
holder.itemView.setOnClickListener {
selectCallback.invoke(entity)
}
} else if (holder is FooterViewHolder) {
holder.initItemPadding()
holder.initFooterViewHolder(mIsLoading, isNetworkError, mIsOver, R.string.ask_loadover_hint)
}
}
class QuestionDraftViewHolder(val binding: CommunityQuestionDraftItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
}

View File

@ -0,0 +1,94 @@
package com.gh.gamecenter.qa.questions.draft
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.util.ToastUtils
import com.gh.common.util.checkStoragePermissionBeforeAction
import com.gh.common.util.viewModelProvider
import com.gh.common.view.CustomDividerItemDecoration
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.ListAdapter
import com.gh.gamecenter.baselist.ListFragment
import com.gh.gamecenter.baselist.LoadType
import com.gh.gamecenter.eventbus.EBReuse
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
import com.gh.gamecenter.qa.draft.CommunityDraftWrapperActivity
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
import com.gh.gamecenter.qa.entity.ArticleDraftEntity
import com.gh.gamecenter.qa.entity.QuestionDraftEntity
import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class QuestionDraftFragment : ListFragment<QuestionDraftEntity, QuestionDraftViewModel>() {
var mAdapter: QuestionDraftAdapter? = null
lateinit var mViewModel: QuestionDraftViewModel
override fun provideListAdapter(): ListAdapter<*> {
return mAdapter ?: QuestionDraftAdapter(requireContext(), mViewModel) {
if (activity is CommunityDraftWrapperActivity) {
checkStoragePermissionBeforeAction {
val intent = QuestionEditActivity.getDraftIntent(requireContext(), it)
startActivity(intent)
}
} else {
checkStoragePermissionBeforeAction {
val intent = Intent()
intent.putExtra(QuestionDraftEntity::class.java.simpleName, it)
requireActivity().setResult(Activity.RESULT_OK, intent)
requireActivity().finish()
}
}
}.apply {
mAdapter = this
}
}
override fun provideListViewModel(): QuestionDraftViewModel {
mViewModel = viewModelProvider()
return mViewModel
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (activity is QuestionDraftActivity) {
setNavigationTitle("问题草稿")
}
mViewModel.deleteDraftSuccess.observe(this, Observer { pair ->
if (pair.second) {
val draftId = pair.first
val index = mAdapter?.entityList?.indexOfFirst { it.id == draftId } ?: -1
if (index >= 0) {
mAdapter?.entityList?.removeAt(index)
if (mAdapter?.entityList.isNullOrEmpty()) {
mListViewModel.load(LoadType.REFRESH)
} else {
mAdapter?.notifyItemRemoved(index)
}
ToastUtils.showToast("删除成功")
}
}
})
}
override fun getItemDecoration(): RecyclerView.ItemDecoration? {
val insetDivider = ContextCompat.getDrawable(requireContext(), R.drawable.divider_item_line_space_16)
val itemDecoration = CustomDividerItemDecoration(requireContext(), notDecorateTheLastItem = true)
itemDecoration.setDrawable(insetDivider!!)
return itemDecoration
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(reuse: EBReuse) {
if (QuestionEditActivity.QUESTION_DRAFT_CHANGE_TAG == reuse.type) {
mBaseHandler.postDelayed({ mListViewModel.load(LoadType.REFRESH) }, 100)
}
}
}

View File

@ -0,0 +1,46 @@
package com.gh.gamecenter.qa.questions.draft
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.MutableLiveData
import com.gh.common.util.observableToMain
import com.gh.gamecenter.baselist.ListViewModel
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.qa.entity.QuestionDraftEntity
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.Observable
import okhttp3.ResponseBody
import retrofit2.HttpException
class QuestionDraftViewModel(application: Application) : ListViewModel<QuestionDraftEntity, QuestionDraftEntity>(application) {
val deleteDraftSuccess = MutableLiveData<Pair<String, Boolean>>()
private val api = RetrofitManager.getInstance(HaloApp.getInstance().application).api
override fun provideDataObservable(page: Int): Observable<MutableList<QuestionDraftEntity>> {
return api.getQuestionDrafts(UserManager.getInstance().userId)
}
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) }
}
@SuppressLint("CheckResult")
fun deleteDraft(draftId: String) {
api.deleteQuestionDraft(UserManager.getInstance().userId, draftId)
.compose(observableToMain())
.subscribe(object :Response<ResponseBody>(){
override fun onResponse(response: ResponseBody?) {
super.onResponse(response)
deleteDraftSuccess.postValue(Pair(draftId, true))
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
deleteDraftSuccess.postValue(Pair(draftId, false))
}
})
}
}

View File

@ -9,24 +9,18 @@ import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Shader
import android.os.Bundle
import android.os.Message
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.MenuItem
import android.view.View
import android.view.Window
import android.view.*
import android.widget.EditText
import android.widget.ProgressBar
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.base.ToolBarActivity
import com.gh.base.fragment.BaseDialogWrapperFragment
import com.gh.base.fragment.WaitingDialogFragment
import com.gh.common.AppExecutor
import com.gh.common.util.*
@ -39,16 +33,23 @@ import com.gh.gamecenter.entity.CommunityEntity
import com.gh.gamecenter.entity.MyVideoEntity
import com.gh.gamecenter.entity.NotificationUgc
import com.gh.gamecenter.entity.Permissions
import com.gh.gamecenter.eventbus.EBReuse
import com.gh.gamecenter.mvvm.Status
import com.gh.gamecenter.qa.article.edit.ArticleSelectGameAdapter
import com.gh.gamecenter.qa.article.edit.ArticleTagsSelectFragment
import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity
import com.gh.gamecenter.qa.article.draft.ArticleDraftActivity
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
import com.gh.gamecenter.qa.dialog.ChooseForumDialogFragment
import com.gh.gamecenter.qa.editor.VideoActivity
import com.gh.gamecenter.qa.entity.CommunityVideoEntity
import com.gh.gamecenter.qa.entity.QuestionDraftEntity
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
import com.gh.gamecenter.qa.questions.draft.QuestionDraftActivity
import com.gh.gamecenter.qa.questions.edit.pic.QuestionsEditPicAdapter
import com.gh.gamecenter.qa.questions.edit.tip.QuestionTitleTipAdapter
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import com.zhihu.matisse.Matisse
import org.greenrobot.eventbus.EventBus
import org.json.JSONObject
import kotlin.math.abs
@ -65,10 +66,12 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
private var mUploadImageCancelDialog: Dialog? = null
private var mKeyboardHeightProvider: KeyboardHeightProvider? = null
private lateinit var picAdapter: QuestionsEditPicAdapter
private lateinit var mMenuDraft: MenuItem
private lateinit var mMenuPost: MenuItem
private var mIsExtendedKeyboardShow = false
private var mOffset = 0
private var mTagsSelectFragment: TagsSelectFragment? = null
private var mPostDraftsCount: Int = 0
private val mSaveTitleKey = "title"
private val mSaveContentKey = "content"
@ -95,6 +98,25 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
duration = RichEditor.formatVideoDuration(videoEntity.length),
status = videoEntity.status))
}
} else if (requestCode == QUESTION_DRAFT_REQUEST_CODE && resultCode == RESULT_OK) {
val draftEntity = data?.getParcelableExtra<QuestionDraftEntity>(QuestionDraftEntity::class.java.simpleName)
if (draftEntity != null) {
mViewModel.questionDraftEntity = draftEntity
setQuestionDraft(draftEntity)
mViewModel.getQuestionDraftContent(draftEntity.id)
}
}
}
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg.what == 1) {
if (mViewModel.communityEntity != null
&& !mViewModel.title.isNullOrEmpty()
&& !mViewModel.content.isNullOrEmpty()) {
mViewModel.saveQuestionDraft(SaveDraftType.AUTO)
}
mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong())
}
}
@ -103,6 +125,7 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
setToolbarMenu(R.menu.menu_question_post)
mToolbar.navigationIcon = null
mMenuDraft = mToolbar.menu.findItem(R.id.menu_draft)
mMenuPost = mToolbar.menu.findItem(R.id.menu_question_post)
mViewModel = ViewModelProviders.of(this).get(QuestionEditViewModel::class.java)
if (savedInstanceState != null) {
@ -126,25 +149,25 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
if (intent != null) {
val communityEntity = intent.getParcelableExtra<CommunityEntity>(CommunityEntity::class.java.simpleName)
val detailEntity = intent.getParcelableExtra<QuestionsDetailEntity>(QuestionsDetailEntity::class.java.simpleName)
if (detailEntity != null) { // 问题编辑
mViewModel.questionEntity = detailEntity
mViewModel.communityEntity = detailEntity.community
mViewModel.communityEntity?.icon = detailEntity.community.game?.getIcon()
mViewModel.communityEntity?.iconSubscript = detailEntity.community.game?.iconSubscript
setForumName()
mViewModel.content = detailEntity.description
mViewModel.picList.postValue(ArrayList(detailEntity.images))
mViewModel.isModeratorPatch = intent.getBooleanExtra(EntranceUtils.KEY_QUESTION_MODERATOR_PATCH, false)
val videos = detailEntity.videos
if (videos.isNotEmpty()) mViewModel.videoLiveData.postValue(detailEntity.videos[0])
if (mViewModel.title.isNullOrEmpty()) mViewModel.title = detailEntity.title
} else { // 新增问题
var searchKey = intent.getStringExtra(EntranceUtils.KEY_QUESTIONS_SEARCH_KEY)
if (!searchKey.isNullOrEmpty() && searchKey.length > QuestionEditViewModel.QUESTION_TITLE_MAX_LENGTH)
searchKey = searchKey.substring(0, QuestionEditViewModel.QUESTION_TITLE_MAX_LENGTH)
if (mViewModel.title.isNullOrEmpty()) mViewModel.title = searchKey
mViewModel.isFromSearch = intent.getBooleanExtra(QuestionEditViewModel.QUESTION_FORM_SEARCH, false)
if (communityEntity == null) showSelectGameDialog()
val draftEntity = intent.getParcelableExtra<QuestionDraftEntity>(QuestionDraftEntity::class.java.simpleName)
when {
detailEntity != null -> { // 问题编辑
setPatchContent(detailEntity)
}
draftEntity != null -> { //草稿编辑
mViewModel.questionDraftEntity = draftEntity
setQuestionDraft(draftEntity)
mViewModel.getQuestionDraftContent(draftEntity.id)
mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong())
}
else -> { // 新增问题
var searchKey = intent.getStringExtra(EntranceUtils.KEY_QUESTIONS_SEARCH_KEY)
if (!searchKey.isNullOrEmpty() && searchKey.length > QuestionEditViewModel.QUESTION_TITLE_MAX_LENGTH)
searchKey = searchKey.substring(0, QuestionEditViewModel.QUESTION_TITLE_MAX_LENGTH)
if (mViewModel.title.isNullOrEmpty()) mViewModel.title = searchKey
mViewModel.isFromSearch = intent.getBooleanExtra(QuestionEditViewModel.QUESTION_FORM_SEARCH, false)
mBaseHandler.sendEmptyMessageDelayed(1, SAVE_DRAFTS_INTERVAL_TIME.toLong())
}
}
if (communityEntity != null) {
@ -152,7 +175,6 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
setForumName()
mBinding.chooseForumTv.isEnabled = false
mBinding.chooseForumTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
if (!mViewModel.isFromSearch) mViewModel.checkQuestionDraft()
}
}
@ -195,6 +217,7 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
mBinding.questionseditContent.filters = arrayOf(TextHelper.getFilter(300, "内容最多300个字"))
mBinding.questionseditContent.addTextChangedListener {
mBinding.editorTextNumTv.text = "${mBinding.questionseditContent.text.length}/300"
checkPostButtonEnable()
}
mBinding.questionseditContent.setOnTouchListener { v, event ->
closeExtendedKeyboard()
@ -229,10 +252,42 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
mBinding.suggestPicRv.addItemDecoration(SpacingItemDecoration(bottom = DisplayUtils.dip2px(10f)))
mBinding.suggestPicRv.adapter = picAdapter
initEditorInsertContainer()
// 增加提问时, 如果searchKey不为空 光标跳到最后
mBaseHandler.postDelayed({
mBinding.questionseditTitle.setSelection(mBinding.questionseditTitle.text.toString().length)
}, 50)
observeData()
}
private fun setPatchContent(detailEntity: QuestionsDetailEntity) {
mMenuDraft.isVisible = false
mViewModel.questionEntity = detailEntity
mViewModel.communityEntity = detailEntity.community
mViewModel.communityEntity?.icon = detailEntity.community.game?.getIcon()
mViewModel.communityEntity?.iconSubscript = detailEntity.community.game?.iconSubscript
mViewModel.content = detailEntity.description
mViewModel.picList.postValue(ArrayList(detailEntity.images))
mViewModel.isModeratorPatch = intent.getBooleanExtra(EntranceUtils.KEY_QUESTION_MODERATOR_PATCH, false)
val videos = detailEntity.videos
if (videos.isNotEmpty()) mViewModel.videoLiveData.postValue(detailEntity.videos[0])
if (mViewModel.title.isNullOrEmpty()) mViewModel.title = detailEntity.title
setForumName()
}
private fun setQuestionDraft(draftEntity: QuestionDraftEntity) {
mViewModel.communityEntity = draftEntity.bbs
mViewModel.communityEntity?.icon = draftEntity.bbs?.game?.getIcon()
mViewModel.communityEntity?.iconSubscript = draftEntity.bbs?.game?.iconSubscript
mViewModel.title = draftEntity.title
mViewModel.content = draftEntity.description
mViewModel.picList.postValue(draftEntity.images)
val videos = draftEntity.videos
if (videos.isNotEmpty()) mViewModel.videoLiveData.postValue(videos[0])
mBinding.questionseditTitle.setText(mViewModel.title)
mBinding.questionseditContent.setText(mViewModel.content)
setForumName()
}
private fun observeData() {
mViewModel.moderatorPostLiveData.observe(this, Observer {
if (it?.status == Status.SUCCESS) {
@ -266,6 +321,41 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
toast(R.string.post_failure_hint)
}
})
mViewModel.postQuestionDrafts.observe(this, Observer { pair ->
if (pair != null) {
when (pair.first) {
SaveDraftType.EXIT -> {
if (pair.second) {
Utils.toast(this, "问题已保存到草稿箱")
EventBus.getDefault().post(EBReuse(ArticleEditActivity.ARTICLE_DRAFT_CHANGE_TAG))
finish()
}
}
SaveDraftType.AUTO -> {
if (pair.second) {
if (mPostDraftsCount >= AnswerEditActivity.SAVE_DRAFTS_TOAST_COUNT) {
mPostDraftsCount = 0
Utils.toast(this, "问题已保存到草稿箱")
} else {
mPostDraftsCount++
}
}
}
SaveDraftType.SKIP -> {
if (pair.second) {
Utils.toast(this, "问题已保存到草稿箱")
startActivityForResult(ArticleDraftActivity.getIntent(this), ArticleEditActivity.ARTICLE_DRAFT_REQUEST_CODE)
} else {
Utils.toast(this, "问题草稿保存失败")
}
}
}
}
})
mViewModel.questionDraftsContent.observe(this, Observer {
mViewModel.questionDraftEntity = it
setQuestionDraft(it)
})
// Process dialog
mViewModel.processDialog.observe(this, Observer { it ->
@ -327,10 +417,9 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
picAdapter.notifyDataSetChanged()
})
// 增加提问时, 如果searchKey不为空 光标跳到最后
mBaseHandler.postDelayed({
mBinding.questionseditTitle.setSelection(mBinding.questionseditTitle.text.toString().length)
}, 50)
mViewModel.notSelectForum.observe(this, Observer {
if (it) showSelectGameDialog()
})
}
private fun changeAddLabel(isLabelContainerShow: Boolean) {
@ -417,23 +506,30 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
}
override fun onMenuItemClick(menuItem: MenuItem?): Boolean {
if (mViewModel.isModeratorPatch) {
if (checkSameFromQuestionData()) {
toast("内容没有变化")
if (menuItem?.itemId == R.id.menu_question_post) {
if (mViewModel.isModeratorPatch) {
if (checkSameFromQuestionData()) {
toast("内容没有变化")
} else {
mViewModel.selectedTags.addAll(mViewModel.questionEntity?.tags!!)
DialogUtils.showAlertDialog(this, "修改问题",
if (mViewModel.questionEntity!!.me.moderatorPermissions.updateQuestion == Permissions.REPORTER)
"你的操作将提交给小编审核,确定提交吗?" else "你的操作将立即生效,确定提交吗?(你的管理权限为:高级)",
"确定", "取消", DialogUtils.ConfirmListener {
mViewModel.uploadPicAndPatchQuestion(false)
}, null)
}
} else {
mViewModel.selectedTags.addAll(mViewModel.questionEntity?.tags!!)
DialogUtils.showAlertDialog(this, "修改问题",
if (mViewModel.questionEntity!!.me.moderatorPermissions.updateQuestion == Permissions.REPORTER)
"你的操作将提交给小编审核,确定提交吗?" else "你的操作将立即生效,确定提交吗?(你的管理权限为:高级)",
"确定", "取消", DialogUtils.ConfirmListener {
mViewModel.uploadPicAndPatchQuestion(false)
}, null)
if (mViewModel.checkTitleAndLoadTitleTag()) {
mTagsSelectFragment?.postQuestion()
}
}
} else {
if (mViewModel.checkTitleAndLoadTitleTag()) {
mTagsSelectFragment?.postQuestion()
} else if (menuItem?.itemId == R.id.menu_draft) {
if (checkDraft(SaveDraftType.SKIP)) {
startActivityForResult(QuestionDraftActivity.getIntent(this), QUESTION_DRAFT_REQUEST_CODE)
}
}
return false
}
@ -457,32 +553,15 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
}
private fun showSelectGameDialog() {
val selectGameDialog = Dialog(this)
val view = View.inflate(this, R.layout.dialog_article_game, null)
val recyclerView = view.findViewById<RecyclerView>(R.id.dialog_game_list)
val back = view.findViewById<View>(R.id.dialog_back)
val loading = view.findViewById<ProgressBar>(R.id.dialog_loading)
back.setOnClickListener {
selectGameDialog.cancel()
}
recyclerView.layoutManager = GridLayoutManager(this, 4)
recyclerView.adapter = ArticleSelectGameAdapter(this, loading) {
mViewModel.communityEntity = CommunityEntity(it.id, it.name, icon = it.game.icon, iconSubscript = it.game.iconSubscript)
ChooseForumDialogFragment.show(this) {
mViewModel.communityEntity = it
if (mViewModel.questionEntity != null) {
mViewModel.questionEntity?.community?.id = it.id
mViewModel.questionEntity?.community?.name = it.name
}
setForumName()
if (!mViewModel.isFromSearch) mViewModel.checkQuestionDraft()
mBinding.vm = mViewModel
selectGameDialog.cancel()
}
selectGameDialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
selectGameDialog.setCanceledOnTouchOutside(false)
selectGameDialog.setContentView(view)
selectGameDialog.show()
}
// Limits of EditText
@ -527,8 +606,6 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
}
// 需要检查的内容,其中任意一个不为空都要打开提示弹窗
val imgList = mViewModel.picList.value
val title = mBinding.questionseditTitle.text.toString().trim()
val content = mBinding.questionseditContent.text.toString().trim()
if (mViewModel.isModeratorPatch) {
if (checkSameFromQuestionData()) {
@ -538,21 +615,62 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
, "确定退出修改?已编辑的内容将丢失"
, "继续编辑", " 退出", null) { finish() }
return true
} else if (imgList != null && imgList.size > 0 || title.isNotEmpty() || content.isNotEmpty()) {
if (mViewModel.questionEntity == null && !mViewModel.isFromSearch) {
mViewModel.saveQuestionDraft()
toast("问题草稿已保存")
}
//问题发布
if (mViewModel.questionEntity == null && mViewModel.questionDraftEntity == null) {
if (mViewModel.communityEntity != null && (!imgList.isNullOrEmpty() || !mViewModel.title.isNullOrEmpty() || !mViewModel.content.isNullOrEmpty())) {
DialogUtils.showNewAlertDialog(this, "提示", "是否保存内容再退出?", "不保存", "保存并退出", Gravity.CENTER, true, {
finish()
}, {
mViewModel.saveQuestionDraft(SaveDraftType.EXIT)
})
return true
}
}
//问题编辑,需要判断是否修改过
if (mViewModel.questionEntity != null) {
return if (mViewModel.questionEntity?.community?.id != mViewModel.communityEntity?.id
|| mViewModel.questionEntity?.title != mViewModel.title
|| mViewModel.questionEntity?.description != mViewModel.content) {
showBackDialog()
true
} else false
}
return !checkDraft(SaveDraftType.EXIT)
}
private fun checkDraft(saveType: SaveDraftType): Boolean {
val draftEntity = mViewModel.questionDraftEntity ?: return true
if (draftEntity.title.isEmpty() && draftEntity.description.isEmpty()) return true
if (saveType == SaveDraftType.SKIP) {
//判断是否修改了草稿,修改了需自动保存无需提示
if (draftEntity.bbs?.id != mViewModel.communityEntity?.id
|| draftEntity.title != mViewModel.title
|| draftEntity.description != mViewModel.content) {
mViewModel.saveQuestionDraft(SaveDraftType.AUTO)
return true
}
} else if (saveType == SaveDraftType.EXIT) {
//退出页面需判断是否修改了草稿,修改了需弹窗提示
if (draftEntity.bbs?.id != mViewModel.communityEntity?.id
|| draftEntity.title != mViewModel.title
|| draftEntity.description != mViewModel.content) {
showBackDialog()
return false
}
DialogUtils.showCancelAlertDialog(this, "提示"
, if (mViewModel.questionEntity == null) "确定放弃提问吗?" else "确定放弃修改吗?"
, "再想想", " 放弃", null) { finish() }
return true
} else if (mViewModel.questionEntity == null && !mViewModel.isFromSearch) {
mViewModel.cleanCurrentCommunityDraft()
}
return false
return true
}
private fun showBackDialog() {
DialogUtils.showNewAlertDialog(this, "提示", "是否保存修改内容用于下次编辑?", "不保存", "保存并退出", Gravity.CENTER, true,{
finish()
}, {
mViewModel.saveQuestionDraft(SaveDraftType.EXIT)
})
}
private fun checkSameFromQuestionData(): Boolean {
@ -608,6 +726,9 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
companion object {
const val QUESTION_POSTED_TAG = "QUESTION_POSTED_TAG"
const val QUESTION_DRAFT_REQUEST_CODE = 105
const val QUESTION_DRAFT_CHANGE_TAG = "ANSWER_DRAFT_CHANGE_TAG"
const val SAVE_DRAFTS_INTERVAL_TIME = 15000
// searchKey 补充到标题(新增问题)
fun getIntent(context: Context, searchKey: String?): Intent {
@ -644,5 +765,19 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
return intent
}
//编辑草稿
@JvmStatic
fun getDraftIntent(context: Context, questionDraftEntity: QuestionDraftEntity): Intent {
val intent = Intent(context, QuestionEditActivity::class.java)
intent.putExtra(QuestionDraftEntity::class.java.simpleName, questionDraftEntity)
return intent
}
}
enum class SaveDraftType {
EXIT, // 退出时保存
AUTO, // 自动保存
SKIP // 跳转至草稿箱时保存
}
}

View File

@ -1,5 +1,6 @@
package com.gh.gamecenter.qa.questions.edit
import android.annotation.SuppressLint
import android.app.Application
import android.net.Uri
import android.provider.MediaStore
@ -12,11 +13,16 @@ import com.gh.common.util.*
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.CommunityEntity
import com.gh.gamecenter.entity.Permissions
import com.gh.gamecenter.entity.User
import com.gh.gamecenter.eventbus.EBReuse
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.mvvm.Resource
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
import com.gh.gamecenter.qa.entity.ArticleDraftEntity
import com.gh.gamecenter.qa.entity.CommunityVideoEntity
import com.gh.gamecenter.qa.entity.QuestionDraftEntity
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
import com.gh.gamecenter.retrofit.BiResponse
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.retrofit.service.ApiService
@ -49,6 +55,9 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica
val postLiveData = MediatorLiveData<Resource<String>>()
val moderatorPostLiveData = MediatorLiveData<Resource<String>>()
val videoLiveData = MutableLiveData<CommunityVideoEntity>()
val notSelectForum = MutableLiveData<Boolean>()
val postQuestionDrafts = MediatorLiveData<Pair<QuestionEditActivity.SaveDraftType, Boolean>>()
val questionDraftsContent = MutableLiveData<QuestionDraftEntity>()
var uploadImageSubscription: Disposable? = null
@ -57,9 +66,10 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica
var content: String? = ""
// picList 纯粹用于展示,可以包含本地和网络地址的图片
val picList = MediatorLiveData<MutableList<String>>()
val picList = MediatorLiveData<ArrayList<String>>()
var defaultTags: MutableList<String> = ArrayList()
var questionEntity: QuestionsDetailEntity? = null
var questionDraftEntity: QuestionDraftEntity? = null
val selectedTags: MutableList<String> = ArrayList()
val selectedTagsChange = MediatorLiveData<Boolean>()
@ -105,11 +115,6 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica
* 根据问题标题获取相应标签(问题编辑无需获取)
*/
fun checkTitleAndLoadTitleTag(): Boolean {
if (TextUtils.isEmpty(communityEntity?.id) || TextUtils.isEmpty(communityEntity?.name)) {
Utils.toast(getApplication(), "论坛不能为空")
return false
}
if (TextUtils.isEmpty(title)) {
Utils.toast(getApplication(), "标题不能为空")
return false
@ -122,6 +127,13 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica
Utils.toast(getApplication(), "标题至少${QUESTION_TITLE_MIN_LENGTH}个字")
return false
}
if (questionEntity != null && questionEntity?.title == title && questionEntity?.description == content) return false
if (TextUtils.isEmpty(communityEntity?.id) || TextUtils.isEmpty(communityEntity?.name)) {
Utils.toast(getApplication(), "论坛不能为空")
notSelectForum.postValue(true)
return false
}
// 检查标题结尾是否存在问号,没则主动加上
if (title!!.length <= QUESTION_TITLE_MAX_LENGTH) {
@ -139,31 +151,10 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica
// 检查标题长度限制
title?.trim()
if (title!!.length < QUESTION_TITLE_MIN_LENGTH) return false
if (questionEntity != null && questionEntity?.title == title && questionEntity?.description == content) return false
return true
}
/**
* 根据问题标题获取相应标签(标签默认选中)
*/
private fun loadTitleTags() {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true))
mApiService
.getQuestionTagsByTitle(communityEntity?.id, UrlFilterUtils.getFilterQuery("title", title))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<List<String>>() {
override fun onResponse(response: List<String>?) {
titleTags.postValue(response)
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", false))
}
override fun onFailure(e: HttpException?) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", false))
Utils.toast(getApplication(), R.string.request_failure_normal_hint)
}
})
}
/**
* 选择动作(选中标签/移除已选中的标签)
*/
@ -352,7 +343,9 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica
entity.images = successfullyUploadedPicList
val video: CommunityVideoEntity? = videoLiveData.value
if (video != null) entity.videos = arrayListOf(video)
if (!questionDraftEntity?.id.isNullOrEmpty()) {
entity.draftId = questionDraftEntity?.id ?: ""
}
val observable = if (questionEntity != null) {
if (!isModeratorPatch) {
questionEntity?.images = entity.images
@ -383,16 +376,18 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica
override fun onResponse(response: ResponseBody?) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", false))
MtaHelper.onEvent("发表问题", "提交成功", communityEntity?.name)
val data = response?.string()
val data = response?.string() ?: ""
postLiveData.postValue(Resource.success(data))
EventBus.getDefault().post(EBReuse(QuestionEditActivity.QUESTION_POSTED_TAG))
if (questionEntity == null && !isFromSearch) cleanCurrentCommunityDraft()
if (questionEntity == null) {
tryWithDefaultCatch {
EnergyTaskHelper.postEnergyTask("post_question", JSONObject(data).optString("_id"))
}
}
if (!questionDraftEntity?.id.isNullOrEmpty()) {
EventBus.getDefault().post(EBReuse(QuestionEditActivity.QUESTION_DRAFT_CHANGE_TAG))
}
}
override fun onFailure(e: HttpException?) {
@ -439,65 +434,63 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica
})
}
fun cleanCurrentCommunityDraft() {
val draftKey = getDraftKey()
if (draftKey.isNullOrEmpty()) {
//Utils.toast(getApplication(), "删除草稿失败")
return
@SuppressLint("CheckResult")
fun saveQuestionDraft(saveType: QuestionEditActivity.SaveDraftType) {
val body = getQuestionBody()
val observable = if (!questionDraftEntity?.id.isNullOrEmpty()) {
mApiService.updateQuestionDraft(UserManager.getInstance().userId, questionDraftEntity?.id, body)
} else {
mApiService.addQuestionDraft(UserManager.getInstance().userId, body)
}
SPUtils.remove(draftKey)
observable
.compose(singleToMain())
.subscribe(object : BiResponse<ResponseBody>() {
override fun onSuccess(data: ResponseBody) {
val string = data.string()
if (!string.isNullOrEmpty() && questionDraftEntity?.id.isNullOrEmpty()) {
if (questionDraftEntity == null) questionDraftEntity = QuestionDraftEntity()
questionDraftEntity?.id = JSONObject(string).getString("_id")
}
postQuestionDrafts.postValue(Pair(saveType, true))
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
postQuestionDrafts.postValue(Pair(saveType, false))
}
})
}
fun saveQuestionDraft() {
val draftKey = getDraftKey()
if (draftKey.isNullOrEmpty()) {
Utils.toast(getApplication(), "保存草稿失败")
return
}
val draftJson = JSONObject()
draftJson.put("title", title)
draftJson.put("content", content)
draftJson.put("images", JSONArray(picList.value))
SPUtils.setString(draftKey, draftJson.toString())
private fun getQuestionBody(): RequestBody {
val draftEntity = QuestionDraftEntity(
communityId = communityEntity?.id ?: "",
tags = selectedTags.toList(),
images = picList.value ?: arrayListOf(),
videos = if (videoLiveData.value != null) arrayListOf(videoLiveData.value!!) else arrayListOf(),
title = title ?: "",
description = content ?: ""
)
return draftEntity.toRequestBody()
}
fun checkQuestionDraft() {
val draftKey = getDraftKey()
if (draftKey.isNullOrEmpty()) {
Utils.toast(getApplication(), "获取草稿失败")
return
}
@SuppressLint("CheckResult")
fun getQuestionDraftContent(draftId: String) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("加载中...", true))
mApiService.getQuestionDraft(UserManager.getInstance().userId, draftId)
.compose(singleToMain())
.subscribe(object : BiResponse<QuestionDraftEntity>() {
override fun onSuccess(data: QuestionDraftEntity) {
questionDraftsContent.postValue(data)
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("加载中...", false))
}
val draft = SPUtils.getString(draftKey, "")
if (draft.isNullOrEmpty()) return
val draftJson = JSONObject(draft)
val draftTitle = draftJson.optString("title")
if (draftTitle.isNotEmpty()) title = draftTitle
val draftContent = draftJson.optString("content")
if (draftContent.isNotEmpty()) content = draftContent
val draftImages = draftJson.optJSONArray("images")
if (draftImages != null) {
val images = ArrayList<String>()
for (i in 0 until draftImages.length()) {
val img = draftImages.getString(i)
if (img.isNotEmpty() && File(img).exists()) {
images.add(img)
}
}
if (images.isNotEmpty()) picList.postValue(images)
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("加载中...", false))
}
})
}
private fun getDraftKey(): String? {
if (communityEntity?.id.isNullOrEmpty()) return null
return QUESTION_DRAFT_KEY + communityEntity?.id
}
companion object {
const val QUESTION_TAG_MAX_COUNT = 5
const val PIC_MAX_AMOUNT = 30

View File

@ -223,28 +223,25 @@ class TagsSelectFragment : BaseFragment<String>() {
// 添加标签
val mDefaultTag = mViewModel?.defaultTags ?: ArrayList()
if (mViewModel?.questionEntity == null) {
val mTitleTag = (mViewModel?.titleTags?.value ?: ArrayList()) as MutableList
// 添加默认标签(如果标题标签与默认标签重复则选中)
for (s in mDefaultTag.reversed()) {
val contains = mTitleTag.contains(s)
addTag(s, contains)
if (contains) mTitleTag.remove(s)
val tags = when {
mViewModel?.questionEntity != null -> {
mViewModel?.questionEntity?.tags ?: arrayListOf()
}
// 未匹配到默认标签的默认选中在前排
for (s in mTitleTag) {
addTag(s, true)
mViewModel?.questionDraftEntity != null -> {
mViewModel?.questionDraftEntity?.tags ?: arrayListOf()
}
} else {
val tags = mViewModel?.questionEntity?.tags ?: ArrayList()
for (s in mDefaultTag) {
if (!tags.contains(s))
addTag(s, false)
}
for (s in tags.reversed()) {
addTag(s, true)
else -> {
arrayListOf()
}
}
for (s in mDefaultTag) {
if (!tags.contains(s))
addTag(s, false)
}
for (s in tags.reversed()) {
addTag(s, true)
}
}
private fun showAddTagDialog() {

View File

@ -100,6 +100,7 @@ import com.gh.gamecenter.qa.entity.CommunitySelectEntity;
import com.gh.gamecenter.qa.entity.CommunitySelectOpenEntity;
import com.gh.gamecenter.qa.entity.EditorInsertDefaultEntity;
import com.gh.gamecenter.qa.entity.InviteEntity;
import com.gh.gamecenter.qa.entity.QuestionDraftEntity;
import com.gh.gamecenter.qa.entity.QuestionHistoryDetailEntity;
import com.gh.gamecenter.qa.entity.QuestionHistoryEntity;
import com.gh.gamecenter.qa.entity.Questions;
@ -108,6 +109,7 @@ import com.gh.gamecenter.qa.entity.QuestionsIndexEntity;
import com.gh.gamecenter.qa.entity.SearchHottestEntity;
import com.gh.gamecenter.qa.entity.SearchNewestEntity;
import com.gh.gamecenter.qa.entity.SuggestedFollowEntity;
import com.gh.gamecenter.retrofit.Response;
import com.google.gson.JsonObject;
import java.util.ArrayList;
@ -2808,4 +2810,34 @@ public interface ApiService {
*/
@GET("shop/orders/roll_notices")
Single<List<RollNoticeEntity>> getRollNotices();
/**
* 新增一个问题草稿
*/
@POST("users/{user_id}/question_drafts")
Single<ResponseBody> addQuestionDraft(@Path("user_id") String userId, @Body RequestBody body);
/**
* 获取单个问题草稿
*/
@GET("users/{user_id}/question_drafts/{draft_id}")
Single<QuestionDraftEntity> getQuestionDraft(@Path("user_id") String userId, @Path("draft_id") String draftId);
/**
* 修改一个问题草稿
*/
@POST("users/{user_id}/question_drafts/{draft_id}")
Single<ResponseBody> updateQuestionDraft(@Path("user_id") String userId, @Path("draft_id") String draftId, @Body RequestBody body);
/**
* 获取问题草稿列表
*/
@GET("users/{user_id}/question_drafts")
Observable<List<QuestionDraftEntity>> getQuestionDrafts(@Path("user_id") String userId);
/**
* 删除单个问题草稿
*/
@DELETE("users/{user_id}/question_drafts/{draft_id}")
Observable<ResponseBody> deleteQuestionDraft(@Path("user_id") String userId, @Path("draft_id") String draftId);
}