siyuan/kernel/api/storage.go

308 lines
6.1 KiB
Go
Raw Normal View History

// SiYuan - Refactor your thinking
// Copyright (c) 2020-present, b3log.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package api
import (
"net/http"
"github.com/88250/gulu"
"github.com/gin-gonic/gin"
"github.com/siyuan-note/siyuan/kernel/model"
"github.com/siyuan-note/siyuan/kernel/util"
)
func getRecentDocs(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
// 获取排序参数
sortBy := "viewedAt" // 默认按浏览时间排序openAt按打开时间排序closedAt按关闭时间排序
// 兼容旧版接口,不能直接使用 util.JsonArg()
arg := map[string]interface{}{}
if err := c.ShouldBindJSON(&arg); err == nil {
if arg["sortBy"] != nil {
sortBy = arg["sortBy"].(string)
}
}
data, err := model.GetRecentDocs(sortBy)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
ret.Data = data
}
func removeCriterion(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
name := arg["name"].(string)
err := model.RemoveCriterion(name)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
}
func setCriterion(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
param, err := gulu.JSON.MarshalJSON(arg["criterion"])
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
criterion := &model.Criterion{}
if err = gulu.JSON.UnmarshalJSON(param, criterion); err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
err = model.SetCriterion(criterion)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
}
func getCriteria(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
data := model.GetCriteria()
ret.Data = data
}
func removeLocalStorageVals(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
var keys []string
keysArg := arg["keys"].([]interface{})
for _, key := range keysArg {
keys = append(keys, key.(string))
}
err := model.RemoveLocalStorageVals(keys)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
app := arg["app"].(string)
evt := util.NewCmdResult("removeLocalStorageVals", 0, util.PushModeBroadcastMainExcludeSelfApp)
evt.AppId = app
evt.Data = map[string]interface{}{"keys": keys}
util.PushEvent(evt)
}
func setLocalStorageVal(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
key := arg["key"].(string)
val := arg["val"].(interface{})
err := model.SetLocalStorageVal(key, val)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
app := arg["app"].(string)
evt := util.NewCmdResult("setLocalStorageVal", 0, util.PushModeBroadcastMainExcludeSelfApp)
evt.AppId = app
evt.Data = map[string]interface{}{"key": key, "val": val}
util.PushEvent(evt)
}
func setLocalStorage(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
val := arg["val"].(interface{})
err := model.SetLocalStorage(val)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
app := arg["app"].(string)
evt := util.NewCmdResult("setLocalStorage", 0, util.PushModeBroadcastMainExcludeSelfApp)
evt.AppId = app
evt.Data = val
util.PushEvent(evt)
}
func getLocalStorage(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
data := model.GetLocalStorage()
ret.Data = data
}
Improve the outline panel (#15814) * ✨ 实现大纲持久化 - 添加折叠状态变化的实时保存功能 - 在文档更新时恢复折叠状态 - 保存拖拽前的折叠状态并在拖拽后恢复 - 更新本地存储结构以支持折叠状态 * 🎨 clean code * ✨ 在`data/storage`文件夹下创建`outline/${docID}.json`文件,存储标题outline展开信息 * ✨ alt click折叠/展开统计标题 * ✨添加层级控制功能 - 新增层级控制滑条,允许用户展开指定层级 * 🌐 添加多语言支持的展开层级功能 * ✨ f添加层级重置显示功能 - 新增 resetLevelDisplay 方法以重置层级显示状态 - 更新层级控制的初始化逻辑,默认不显示层级 - 在文档切换时重置层级显示状态 * ✨优化层级控制功能 - 添加用户主动层级控制标记 - 修改层级显示重置逻辑,仅在非用户操作时重置 - 更新层级控制滑条的点击事件处理 * ♻️ 重构大纲存储逻辑 - 合并大纲存储为单一文件 outline.json * ♻️ outline.json 单行存储 * ♻️ outline.json参考recent-doc.json,只存储前1000个文档信息,每次新增信息会把数据放在最前面 * ♻️单行存储json * ♻️ 增加outline.json存储限制至2000个 * ✨ 新增`保持当前标题展开`按钮,`保持全部展开`改为`全部展开`按钮 * ✨ 保持当前标题展开优化 - 超过两级折叠,也能都展开 - 如果父节点折叠,展开时自动折叠兄弟节点,只展开当前节点路径,如果父节点是展开状态,则不影响兄弟节点折叠状态 * 🔥 移除层级文本 * ✨ 右键点击toggle时展开所有子标题 * ✨ 右键click点击折叠图标,会折叠/展开所有子标题 * ✨ 大纲支持筛选功能 * ✨ feat(大纲): 优化筛选功能以显示所有子标题 - 添加 showAllDescendants 函数以显示所有子标题 - 修改 processUL 函数以在父标题命中时显示所有子标题 - 确保未命中的子标题隐藏 * ✨ 优化大纲筛选 - ✅如果标题命中筛选,这个标题的所有父标题展开,以显示出这个标题位置 - ✅如果父标题命中筛选,子项都没有命中筛选,则折叠全部子项(依然可以展开显示) - ✅如果父标题命中筛选,子项也有命中筛选,则展开命中的子项,其他无关子项隐藏 * 💄 展开层级改为按钮,原先的圆点样式碍眼 * 💄 展开层级改为按钮,原先的圆点样式碍眼 * 💄 style(菜单): 优化展开层级菜单的样式和位置 - 添加图标以增强可视化 - 调整菜单弹出位置以改善用户体验 * ♻️ refactor(大纲): 优化标题级别获取逻辑 - 调整展开到指定层级的逻辑,使用标题级别进行判断 * 🎨 * ✨ feat(大纲): 添加右键菜单功能 - 实现右键点击标题时显示上下文菜单 - 增加标题升级、降级、插入、删除等操作 - 基于标题级别展开/折叠同级标题 * ✨ feat(大纲): 添加子标题功能 - 在右键菜单中添加“添加子标题”选项 - 实现子标题的添加逻辑,支持最大级别为H6 - 使用当前标题作为父标题,插入新子标题 * ✨ feat(大纲): "添加子标题"确保父标题展开状态 * ✨ feat(大纲): 使用expandIds方式保存父标题展开状态 - 确保父标题保持展开状态 - 保存展开状态到持久化存储 - 移除冗余的状态保存逻辑 * ✨ feat(大纲): 调整右键菜单顺序,将“全部折叠”功能移至“全部展开”之后 * 🌐 i18n optimization
2025-10-15 10:01:47 +08:00
func getOutlineStorage(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
docID := arg["docID"].(string)
data, err := model.GetOutlineStorage(docID)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
ret.Data = data
}
func setOutlineStorage(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
docID := arg["docID"].(string)
val := arg["val"].(interface{})
err := model.SetOutlineStorage(docID, val)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
}
func removeOutlineStorage(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
docID := arg["docID"].(string)
err := model.RemoveOutlineStorage(docID)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
}
func updateRecentDocViewTime(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
rootID := arg["rootID"].(string)
err := model.UpdateRecentDocViewTime(rootID)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
}
func updateRecentDocOpenTime(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
rootID := arg["rootID"].(string)
err := model.UpdateRecentDocOpenTime(rootID)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
}
func updateRecentDocCloseTime(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
if nil == arg["rootID"] {
return
}
rootID := arg["rootID"].(string)
err := model.UpdateRecentDocCloseTime(rootID)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
}