Valine实现评论“撤回”重新编辑
温馨提示:这篇文章已超过667天没有更新,请注意相关的内容是否还可用!
说到消息“撤回”,应该能想到 qq 或者微信提供的消息撤回功能,应该算是互联网上的一剂后悔药,这样做不仅能减少评论错误,还能极大程度提升用户使用体验,这两全其美的事情何乐而不为,可我们修改的对象 Valine 一如既往的没有提供这些人性化的功能,所以我们还得和以前一样看看文档写写 bug 才能把功能捣鼓出来..(想法来自 @Zsedczy 的评论重新编辑和撤销删除)
非撤回再编辑
具体实现
既然又是 leancloud 那就先聊下,因为 leancloud 官方文档里有写到一个 revert() 方法可以撤销尚未保存的修改,不过我捣鼓半天没看懂这玩意咋能套进 Valine 实现保存数据前修改再保存,所以放弃了这个思路,转而像更简单粗暴的“数据更新”方法。
实现思路
同样的,利用 leancloud 数据储存的数据更新方法,对已经成功储存的指定数据进行修改再储存,简单来说就是更新数据(和之前更新点赞数据相似)当 Valine 提交评论后我们提供按钮对指定 id 的数据进行查询并修改的过程,而撤回评论则是使用 leancloud 提供的 destory() 方法进行云端数据的删除操作。
实践
了解完思路我们仍然从 Valine.js 下手,首先新增/修改按钮,定位到
<button type="button" title="Ctrl+Enter" class="vsubmit vbtn">' + e.locale.ctrl.reply + '</button>
修改为
<button type="button" title="Ctrl+Enter" id="pushBtn" class="vsubmit vbtn">' + e.locale.ctrl.reply + '</button><button type="button" id="repushBtn" class="vsubmit vbtn" style="display:none"> 重新提交 </button>
我们需要找到 valine 提交评论数据后的位置,定位到 n.setACL(I()),n.save().then(function(e){ 后添加
let input = _.comment.value, //输入框评论(已提交) objId = e.id, //评论id(已提交) redo = AV.Object.createWithoutData('Comment', objId), //更新当前评论数据方法 el = document.createElement("div"), div = '<p>评论<ins style="color:#fff"> '+input+' </ins>已成功提交!你还可以 <button id="redo"> 重新编辑 </button> 或 <button id="del"> 撤销 </button> 该评论!</p>', push = document.getElementById("pushBtn"), //旧按钮 repush = document.getElementById("repushBtn"), //新按钮 pushBack = (e)=>{ push.style.display="block"; repush.style.display="none"; document.getElementById(e).remove(); }; el.setAttribute("id","reEdit"); document.body.appendChild(el); el.innerHTML = div; //重新编辑点击事件 document.getElementById("redo").onclick=function(){ _.comment.focus(); //聚焦输入框 _.comment.value = input; //将已提交的评论内容重新写入到输入框 push.style.display="none"; //隐藏旧按钮 repush.style.display="block"; //显示新按钮 //重新编辑按钮点击事件 repush.onclick=function(){ redo.set("comment", "<p>"+_.comment.value+"</p>"); //更新数据为:当前输入框内容 //redo.set("isEdited", true); //可选(设定重复编辑判断规则,和之前的置顶实现一样在控制台新增指定列,然后更新数据做判断后写入元素,这里就不重复列了) //保存更新后的数据 redo.save().then(function(e){ pushBack("reEdit"); //隐藏新按钮 显示旧按钮 移除提示框 document.getElementById(objId).querySelector(".vcontent p").innerHTML = _.comment.value; //更新本地显示内容(伪即时更新) B() //执行预设函数 B()(数据清空) }).catch(function(e) { console.log("reEdit Err.") }) } }; //撤销点击事件 document.getElementById("del").onclick=function(){ //定义删除函数 let del = () =>{ let v = u.find(t.el, ".vnum"); //递减当前评论数量并写入本地显示 if(v!=null){ let n = Number(v.innerText); n--; v.innerText=n } redo.destroy(); //删除云端数据 pushBack("reEdit"); //隐藏新按钮 显示旧按钮 移除提示框 document.getElementById(objId).remove(); //更新本地显示内容(伪即时更新) B() //执行预设函数 B()(数据清空) }; let cf = confirm(" 即将删除评论(不可逆): "+input+",确定?"); //弹窗确认删除 cf==true ? del() : false; //删除逻辑 };
问题修复
以上代码是已经可以实现重复编辑和撤销评论的逻辑了,不过这时候发现更新数据提交后没有响应,打开控制台才发现报了 400 错误,一看是有关于 ACL 权限的就懂了,看了下 save 之前的 setACL,是一个定义好的函数 I() ,是这么写的(其实这里已经很明了了0!就是开权限了)
I = function() { var e = new AV.ACL; return e.setPublicReadAccess(!0), e.setPublicWriteAccess(!1), e }
两个读写权限设置,这一看报错就是因为没有权限写入更新数据,去 leancloud 控制台一看,果不其然
安全考虑都是可读不可写
然后我在 官方 ACL 文档 里找到了一些设置权限的方法,
I = function() { var e = new AV.ACL, admin = new AV.Role('admin'); return e.setPublicReadAccess(!0), e.setPublicWriteAccess(!1), //实际这里改为 !0 就可以了(不考虑安全因素 //e.setRoleWriteAccess(admin, true); //指定用户编辑 //e.setWriteAccess(AV.User.current(), true), //当前 id 可编辑(Owner 有 bug 用户首次评论点击重复编辑按钮会获取到上一个id,弃用了) e }
以上的当前用户可修改评论和指定用户可修改最终效果都不太理想,所以还是将 e.setPublicWriteAccess(!1) 改为 e.setPublicWriteAccess(!0) 全读写了。
拓展
要实现评论是否被重复编辑,一个是记录并判断评论提交时间,不过太麻烦,还有一个就和置顶 topset 的实现是一样的,在 leancloud 控制台新建一个 isEdited 列默认值 flase ,然后在重复编辑评论提交时 set 一个 isEdited 的参数就行了,剩下的就和上次笔记写的一样了(写个判断,在写入元素前新增个元素并将值作为判断 isEdited 的结果即可)
重复编辑显示是可选项
问题优化
ACL 权限全读写还没完全搞懂,虽然可以用不过还是不推荐这么搞,记得经常备份才行。
F12 400 报错:Error: 此电子邮箱已经被占用问题
参考链接 leancloud 官方文档
“因果关系”
因为长期对 valine 的修改,现在发现了一个很早就出现的 bug.. 页面没有评论时第一次在页面评论不会刷出评论数量(刷新后又好了)不知道是哪里出问题了,因为我有很大量备份索性回滚,无果。
后期有时间好好排查下什么情况引起的,改的东西太多就容易出错,还不容易察觉,就是这么蛋疼..
已知 bug
取消回复后,没有清除已@到的人
重新编辑提交后,没有@到人
以上,有问题评论区留言。