增加问题草稿功能

This commit is contained in:
张玉久
2021-01-15 16:30:06 +08:00
parent baa1c9f9e5
commit a42bcbd160
33 changed files with 973 additions and 346 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

@ -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

@ -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;
@ -662,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
*

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

@ -54,7 +54,7 @@ class ForumAdapter(val context: Context, val mViewModel: ForumSelectViewModel?,
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

@ -31,8 +31,6 @@ import com.gh.gamecenter.qa.entity.Questions
import com.gh.gamecenter.video.VideoVerifyItemViewHolder
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.MimeType
import org.json.JSONArray
@ -44,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
@ -84,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)
@ -108,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 ->
@ -134,6 +135,7 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mKeyboardHeightProvider = KeyboardHeightProvider(this)
mBinding.root.post { mKeyboardHeightProvider?.start() }
checkPostButtonEnable()
}
override fun getLayoutId(): Int {
@ -277,6 +279,7 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mViewModel.draftsLiveData.observe(this, Observer {
mRichEditor.setHtml(it, false)
requestFocusAndMoveCursorToEnd()
checkPostButtonEnable()
})
mViewModel.saveDraftsLiveData.observeNonNull(this) {
@ -375,23 +378,32 @@ class AnswerEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
val answerContent = getReplaceRealContent()
mRichEditor.showLinkStyle()
if (mRichEditor.hasPlaceholderImage()) {
ToastUtils.showToast("图片正在上传中")
ToastUtils.showToast("图片上传ing")
return@postDelayed
}
// filter rule
val answerLength = HtmlUtils.stripHtml(answerContent).length
if (answerLength < MIN_ANSWER_TEXT_LENGTH) {
ToastUtils.showToast(getString(R.string.answer_beneath_length_limit), if (mIsKeyBoardShow) Gravity.CENTER else -1)
return@postDelayed
} else if (answerLength > MAX_ANSWER_TEXT_LENGTH) {
ToastUtils.showToast("回答最多输入10000个字", if (mIsKeyBoardShow) Gravity.CENTER else -1)
return@postDelayed
if (checkData(answerContent)) {
mViewModel.postAnswer(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) {
@ -403,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
@ -417,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) {
@ -443,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)
})
}
/**

View File

@ -492,8 +492,10 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment<CommentItemData,
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))
}
}
@ -501,9 +503,11 @@ class ArticleDetailFragment : BaseArticleDetailCommentFragment<CommentItemData,
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

@ -398,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) {
@ -433,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

@ -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

@ -9,9 +9,9 @@ import android.os.Bundle
import android.os.Message
import android.text.InputFilter
import android.text.TextUtils
import android.view.Gravity
import android.view.MenuItem
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.*
import androidx.core.content.ContextCompat
import androidx.core.widget.doOnTextChanged
@ -138,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", ""))
@ -194,7 +192,7 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
EventBus.getDefault().post(EBReuse(ARTICLE_DRAFT_CHANGE_TAG))
finish()
} else {
showDraftFailureDialog()
//showDraftFailureDialog()
}
}
SaveDraftType.AUTO -> {
@ -288,6 +286,7 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mGameName.isEnabled = false
mGameName.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
}
mEditTitle.requestFocus()
}
mViewModel.notSelectForum.observe(this, Observer {
if (it) showSelectGameDialog()
@ -361,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!!)
@ -373,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)
@ -391,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()
}
@ -418,70 +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
if (mRichEditor.hasPlaceholderImage()) 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
}
@ -495,18 +478,15 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
}, 100)
}
private fun showPatchBackDialog() {
private fun showBackDialog() {
if (mRichEditor.hasPlaceholderImage()) return
DialogUtils.showCancelAlertDialog(this, "提示",
"即将退出修改,是否需要将此次编辑保存到草稿箱?",
"保存草稿",
" 继续退出",
{
mViewModel.title = mEditTitle.text.toString()
mViewModel.content = getReplaceRealContent()
mViewModel.postArticleDrafts(SaveDraftType.EXIT)
},
{ finish() })
DialogUtils.showNewAlertDialog(this, "提示", "是否保存修改内容用于下次编辑?", "不保存", "保存并退出", Gravity.CENTER, true, {
finish()
}, {
mViewModel.title = mEditTitle.text.toString()
mViewModel.content = getReplaceRealContent()
mViewModel.postArticleDrafts(SaveDraftType.EXIT)
})
}
private fun showSelectGameDialog() {
@ -543,7 +523,7 @@ class ArticleEditActivity : BaseRichEditorActivity(), KeyboardHeightObserver {
mViewModel.content = getReplaceRealContent()
mRichEditor.showLinkStyle()
if (mRichEditor.hasPlaceholderImage()) {
ToastUtils.showToast("图片正在上传中")
ToastUtils.showToast("图片上传ing")
return@postDelayed
}
if (mViewModel.checkDataAndLoadTitleTag(mIsKeyBoardShow)) {

View File

@ -115,6 +115,7 @@ 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)
@ -131,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 图片本地路径

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

@ -5,6 +5,7 @@ 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
@ -36,6 +37,14 @@ class ArticleViewModel(application: Application, private val articleType: Insert
}
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
}

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

@ -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,17 +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
@ -66,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"
@ -96,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())
}
}
@ -104,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) {
@ -127,24 +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)
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()
@ -236,6 +259,35 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
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) {
@ -269,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 ->
@ -419,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
}
@ -466,7 +560,6 @@ class QuestionEditActivity : ToolBarActivity(), KeyboardHeightObserver {
mViewModel.questionEntity?.community?.name = it.name
}
setForumName()
if (!mViewModel.isFromSearch) mViewModel.checkQuestionDraft()
mBinding.vm = mViewModel
}
}
@ -513,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()) {
@ -524,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 {
@ -594,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 {
@ -630,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
@ -50,6 +56,8 @@ class QuestionEditViewModel(application: Application) : AndroidViewModel(applica
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
@ -58,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>()
@ -118,6 +127,8 @@ 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)
@ -140,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)
}
})
}
/**
* 选择动作(选中标签/移除已选中的标签)
*/
@ -353,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
@ -384,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?) {
@ -440,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);
}