基于react-native-cn-quill
开发,实现先在本地编辑图片和视频富文本,在某一个时刻将富文本数据上传到云端
那需要解决以下几个问题:
- webview需要能够显示本地文件
- 在图片或视频的html标签中能够添加唯一标识文件的id属性
- 在上传富文本数据到云端时,能检查到文本在编辑时删除了,如果是一个服务器文件,那么要删除服务器文件,避免服务器过多冗余文件,如果是本地文件,可以直接删除本地文件
- 第一次从服务器取获取富文本数据去显示图片或视频,如果本地没有文件,需要先显示占位符,下载完文件后再显示真实的文件
- 增量更新富文本数据,实现查看历史版本和多人协作编辑(非实时),以及避免全量更新所产生的流量
1. webview需要能够显示本地文件
首先排除base64直接加载文件的方式,因为富文本中的图片和视频可能都不小,所以还是还是用本地文件路径显示比较合理;但是iOS的WKWebView
加强了安全性,不再允许跨域访问,所有跨域地址都失效了,包括不再同一文件夹下的CSS
、JS
等文件引用,所以直接加载本地文件路径肯定显示不了。
解决办法: stackoverflow.com/questions/3…
我选择了修改baseURL
的方式,因为这比较符合react-native-cn-quill
的情况,
只需要修改QuillEditor.js
文件中的source={{ html: content }}
为source={{...props.source, html: content }}
再在创建组件时传入baseUrl
<QuillEditor
webview={{
allowFileAccess: true,
scrollEnabled: true,
nestedScrollEnabled: true,
source: {
baseUrl: RNFS.TemporaryDirectoryPath,
},
}} />
这样只会只需要把图片缓存在tmp文件中,WKWebView
就可以正常访问了
2. 在图片或视频的html标签中能够添加唯一标识文件的id属性
这个只需找到显示和的format类,重写它创建node和返回node的实现,再注入重写的js代码
如下,修改图片的format类
<QuillEditor
customJS={
const Image = Quill.import('formats/image');
class ImageBlot extends Image {
static create(value) {
var node = super.create();
Object.getOwnPropertyNames(value).forEach((attribute_name) => {
node.setAttribute(attribute_name, value[attribute_name]);
});
return node;
}
static value(domNode) {
var blot = {};
domNode.getAttributeNames().forEach((attribute_name) => {
blot[attribute_name] = domNode.getAttribute(attribute_name);
});
return blot;
}
}
Quill.register(ImageBlot);
}
/>
3.在上传富文本数据到云端时,能检查到文本在编辑时删除了
通过新delta和delta的diff,可以得到本次需要提交的变化,检查变化里面的delete是否为文件
eg: 比如diff结果为(从第5个索引开始,删除了3个长度的内容):
{"ops":[{"retain":5},{"delete":3}]}
invert后,得到结果为
{"ops":[{"retain":5},{"insert":{"image":{"id":"xxxx"}}},{"insert":"Wo"}]}
再更加id删除图片
5. 增量更新富文本数据
quill
提供了一个表达富文本的数据结构的库quill-delta,该库最终通过json输出数据,而且为处理数据提供了很多方法,可以直接通过操作数据来处理富文本,比如合并2个富文本(contact方法),合并diff(compose方法)数据到富文本等等
版本 | 改变内容 | 改变人 | 时间 | 操作 |
---|---|---|---|---|
v1 | {"ops":[{"attributes":{"bold":true},"insert":"Hello"},{"insert":{"image":{"id":"xxxxx"}]} | tommy | 2023-1-12 9:00 | 第一次创建内容 |
v2 | {"ops":[{"retain":16},{"insert":"\n\n"},{"attributes":{"underline":true,"italic":true,"bold":true},"insert":"Earth’s Day"},{"insert":"\n"}]} | tommy | 2023-1-12 9:05 | 添加Earth's Day的文本,并加对其粗斜体下划线 |
v3 | {"ops":[{"retain":5},{"insert":"\n"},{"delete":1}]} | tommy | 2023-1-12 10:00 | 删除里面图片 |
Graphql设计:
var RichTextType = new GraphQLObjectType({
name: "RichText",
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLString)
},
version: {
type: new GraphQLNonNull(GraphQLInt)
},
editor: {
type: GraphQLString
},
content: {
type: GraphQLString
},
des: {
type: GraphQLString,
},
props: {
type: GraphQLString,
},
delete_at: {
type: GraphQLLong,
},
create_at: {
type: GraphQLLong,
},
update_at: {
type: GraphQLLong,
},
})
})
客户端第一次需要把所有数据都拉下来,再解析里面的文件id,下载文件,动态更新富文本;之后客户端根据富文本版本增量更新变化数据