Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
b86ed5b356
12 changed files with 189 additions and 45 deletions
4
app/stage/protyle/js/lute/lute.min.js
vendored
4
app/stage/protyle/js/lute/lute.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -135,7 +135,49 @@ func getAttributeViewPrimaryKeyValues(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func addAttributeViewValues(c *gin.Context) {
|
||||
func appendAttributeViewDetachedBlocksWithValues(c *gin.Context) {
|
||||
// Add an internal kernel API `/api/av/appendAttributeViewDetachedBlocksWithValues` https://github.com/siyuan-note/siyuan/issues/11608
|
||||
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, _ := util.JsonArg(c, ret)
|
||||
if nil == arg {
|
||||
return
|
||||
}
|
||||
|
||||
avID := arg["avID"].(string)
|
||||
var values [][]*av.Value
|
||||
for _, blocksVals := range arg["blocksValues"].([]interface{}) {
|
||||
vals := blocksVals.([]interface{})
|
||||
var rowValues []*av.Value
|
||||
for _, val := range vals {
|
||||
data, marshalErr := gulu.JSON.MarshalJSON(val)
|
||||
if nil != marshalErr {
|
||||
ret.Code = -1
|
||||
ret.Msg = marshalErr.Error()
|
||||
return
|
||||
}
|
||||
value := av.Value{}
|
||||
if unmarshalErr := gulu.JSON.UnmarshalJSON(data, &value); nil != unmarshalErr {
|
||||
ret.Code = -1
|
||||
ret.Msg = unmarshalErr.Error()
|
||||
return
|
||||
}
|
||||
rowValues = append(rowValues, &value)
|
||||
}
|
||||
values = append(values, rowValues)
|
||||
}
|
||||
|
||||
err := model.AppendAttributeViewDetachedBlocksWithValues(avID, values)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func addAttributeViewBlocks(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
|
@ -173,7 +215,7 @@ func addAttributeViewValues(c *gin.Context) {
|
|||
util.PushReloadAttrView(avID)
|
||||
}
|
||||
|
||||
func removeAttributeViewValues(c *gin.Context) {
|
||||
func removeAttributeViewBlocks(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
|
|
|
@ -19,10 +19,12 @@ package api
|
|||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/treenode"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
|
@ -222,6 +224,35 @@ func openNotebook(c *gin.Context) {
|
|||
}
|
||||
evt.Callback = arg["callback"]
|
||||
util.PushEvent(evt)
|
||||
|
||||
if isUserGuide {
|
||||
appArg := arg["app"]
|
||||
app := ""
|
||||
if nil != appArg {
|
||||
app = appArg.(string)
|
||||
}
|
||||
|
||||
go func() {
|
||||
var startID string
|
||||
i := 0
|
||||
for ; i < 70; i++ {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
guideStartID := map[string]string{
|
||||
"20210808180117-czj9bvb": "20200812220555-lj3enxa",
|
||||
"20211226090932-5lcq56f": "20211226115423-d5z1joq",
|
||||
"20210808180117-6v0mkxr": "20200923234011-ieuun1p",
|
||||
"20240530133126-axarxgx": "20240530101000-4qitucx",
|
||||
}
|
||||
startID = guideStartID[notebook]
|
||||
if nil != treenode.GetBlockTree(startID) {
|
||||
util.BroadcastByTypeAndApp("main", app, "openFileById", 0, "", map[string]interface{}{
|
||||
"id": startID,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func closeNotebook(c *gin.Context) {
|
||||
|
|
|
@ -419,13 +419,14 @@ func ServeAPI(ginServer *gin.Engine) {
|
|||
ginServer.Handle("POST", "/api/av/removeAttributeViewKey", model.CheckAuth, model.CheckReadonly, removeAttributeViewKey)
|
||||
ginServer.Handle("POST", "/api/av/sortAttributeViewViewKey", model.CheckAuth, model.CheckReadonly, sortAttributeViewViewKey)
|
||||
ginServer.Handle("POST", "/api/av/sortAttributeViewKey", model.CheckAuth, model.CheckReadonly, sortAttributeViewKey)
|
||||
ginServer.Handle("POST", "/api/av/addAttributeViewValues", model.CheckAuth, model.CheckReadonly, addAttributeViewValues)
|
||||
ginServer.Handle("POST", "/api/av/removeAttributeViewValues", model.CheckAuth, model.CheckReadonly, removeAttributeViewValues)
|
||||
ginServer.Handle("POST", "/api/av/addAttributeViewBlocks", model.CheckAuth, model.CheckReadonly, addAttributeViewBlocks)
|
||||
ginServer.Handle("POST", "/api/av/removeAttributeViewBlocks", model.CheckAuth, model.CheckReadonly, removeAttributeViewBlocks)
|
||||
ginServer.Handle("POST", "/api/av/getAttributeViewPrimaryKeyValues", model.CheckAuth, model.CheckReadonly, getAttributeViewPrimaryKeyValues)
|
||||
ginServer.Handle("POST", "/api/av/setDatabaseBlockView", model.CheckAuth, model.CheckReadonly, setDatabaseBlockView)
|
||||
ginServer.Handle("POST", "/api/av/getMirrorDatabaseBlocks", model.CheckAuth, model.CheckReadonly, getMirrorDatabaseBlocks)
|
||||
ginServer.Handle("POST", "/api/av/getAttributeViewKeysByAvID", model.CheckAuth, model.CheckReadonly, getAttributeViewKeysByAvID)
|
||||
ginServer.Handle("POST", "/api/av/duplicateAttributeViewBlock", model.CheckAuth, model.CheckReadonly, duplicateAttributeViewBlock)
|
||||
ginServer.Handle("POST", "/api/av/appendAttributeViewDetachedBlocksWithValues", model.CheckAuth, model.CheckReadonly, appendAttributeViewDetachedBlocksWithValues)
|
||||
|
||||
ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, chatGPT)
|
||||
ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, chatGPTWithAction)
|
||||
|
|
|
@ -62,6 +62,26 @@ func (kValues *KeyValues) GetValue(blockID string) (ret *Value) {
|
|||
return
|
||||
}
|
||||
|
||||
func (kValues *KeyValues) GetBlockValue() (ret *Value) {
|
||||
for _, v := range kValues.Values {
|
||||
if KeyTypeBlock != v.Type {
|
||||
ret = v
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetKeyBlockValue(blockKeyValues []*KeyValues) (ret *Value) {
|
||||
for _, kv := range blockKeyValues {
|
||||
if KeyTypeBlock == kv.Key.Type && 0 < len(kv.Values) {
|
||||
ret = kv.Values[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type KeyType string
|
||||
|
||||
const (
|
||||
|
|
|
@ -8,7 +8,7 @@ require (
|
|||
github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48
|
||||
github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7
|
||||
github.com/88250/gulu v1.2.3-0.20240505150113-bc43bd50f866
|
||||
github.com/88250/lute v1.7.7-0.20240529025122-c2b54f422dc4
|
||||
github.com/88250/lute v1.7.7-0.20240531135614-85cb33ff053b
|
||||
github.com/88250/pdfcpu v0.3.14-0.20230401044135-c7369a99720c
|
||||
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
|
||||
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4
|
||||
|
|
|
@ -12,8 +12,8 @@ github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950 h1:Pa5hMiBceT
|
|||
github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/88250/gulu v1.2.3-0.20240505150113-bc43bd50f866 h1:RFfNFS0hv6TbOuwET6xZAfGlV4hNlXiWTnfbLN1eF6k=
|
||||
github.com/88250/gulu v1.2.3-0.20240505150113-bc43bd50f866/go.mod h1:MUfzyfmbPrRDZLqxc7aPrVYveatTHRfoUa5TynPS0i8=
|
||||
github.com/88250/lute v1.7.7-0.20240529025122-c2b54f422dc4 h1:5/vRlAP/3C7YG7KMd35OlHIgNwMx3s6QLBwcjx8YABw=
|
||||
github.com/88250/lute v1.7.7-0.20240529025122-c2b54f422dc4/go.mod h1:VDAzL8b+oCh+e3NAlmwwLzC53ten0rZlS8NboB7ljtk=
|
||||
github.com/88250/lute v1.7.7-0.20240531135614-85cb33ff053b h1:zOpPknjyTDYchi+kQtWJoGfqE3kfKfZbij+cYNdjta0=
|
||||
github.com/88250/lute v1.7.7-0.20240531135614-85cb33ff053b/go.mod h1:VDAzL8b+oCh+e3NAlmwwLzC53ten0rZlS8NboB7ljtk=
|
||||
github.com/88250/pdfcpu v0.3.14-0.20230401044135-c7369a99720c h1:Dl/8S9iLyPMTElnWIBxmjaLiWrkI5P4a21ivwAn5pU0=
|
||||
github.com/88250/pdfcpu v0.3.14-0.20230401044135-c7369a99720c/go.mod h1:S5YT38L/GCjVjmB4PB84PymA1qfopjEhfhTNQilLpv4=
|
||||
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY=
|
||||
|
|
|
@ -42,6 +42,59 @@ import (
|
|||
"github.com/xrash/smetrics"
|
||||
)
|
||||
|
||||
func AppendAttributeViewDetachedBlocksWithValues(avID string, blocksValues [][]*av.Value) (err error) {
|
||||
attrView, err := av.ParseAttributeView(avID)
|
||||
if nil != err {
|
||||
logging.LogErrorf("parse attribute view [%s] failed: %s", avID, err)
|
||||
return
|
||||
}
|
||||
|
||||
now := util.CurrentTimeMillis()
|
||||
var blockIDs []string
|
||||
for _, blockValues := range blocksValues {
|
||||
blockID := ast.NewNodeID()
|
||||
blockIDs = append(blockIDs, blockID)
|
||||
for _, v := range blockValues {
|
||||
keyValues, _ := attrView.GetKeyValues(v.KeyID)
|
||||
if nil == keyValues {
|
||||
err = fmt.Errorf("key [%s] not found", v.KeyID)
|
||||
return
|
||||
}
|
||||
|
||||
v.ID = ast.NewNodeID()
|
||||
v.BlockID = blockID
|
||||
v.Type = keyValues.Key.Type
|
||||
if av.KeyTypeBlock == v.Type {
|
||||
v.Block.ID = blockID
|
||||
v.Block.Created = now
|
||||
v.Block.Updated = now
|
||||
}
|
||||
v.IsDetached = true
|
||||
v.CreatedAt = now
|
||||
v.UpdatedAt = now
|
||||
|
||||
keyValues.Values = append(keyValues.Values, v)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range attrView.Views {
|
||||
switch v.LayoutType {
|
||||
case av.LayoutTypeTable:
|
||||
for _, addingBlockID := range blockIDs {
|
||||
v.Table.RowIDs = append(v.Table.RowIDs, addingBlockID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = av.SaveAttributeView(attrView); nil != err {
|
||||
logging.LogErrorf("save attribute view [%s] failed: %s", avID, err)
|
||||
return
|
||||
}
|
||||
|
||||
util.PushReloadAttrView(avID)
|
||||
return
|
||||
}
|
||||
|
||||
func DuplicateDatabaseBlock(avID string) (newAvID, newBlockID string, err error) {
|
||||
storageAvDir := filepath.Join(util.DataDir, "storage", "av")
|
||||
oldAvPath := filepath.Join(storageAvDir, avID+".json")
|
||||
|
@ -574,7 +627,7 @@ func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) {
|
|||
case av.KeyTypeTemplate:
|
||||
if 0 < len(kv.Values) {
|
||||
ial := map[string]string{}
|
||||
block := getRowBlockValue(keyValues)
|
||||
block := av.GetKeyBlockValue(keyValues)
|
||||
if nil != block && !block.IsDetached {
|
||||
ial = GetBlockAttrsWithoutWaitWriting(block.BlockID)
|
||||
}
|
||||
|
@ -915,16 +968,6 @@ func renderAttributeView(attrView *av.AttributeView, viewID, query string, page,
|
|||
return
|
||||
}
|
||||
|
||||
func getRowBlockValue(keyValues []*av.KeyValues) (ret *av.Value) {
|
||||
for _, kv := range keyValues {
|
||||
if av.KeyTypeBlock == kv.Key.Type && 0 < len(kv.Values) {
|
||||
ret = kv.Values[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tx *Transaction) doUnbindAttrViewBlock(operation *Operation) (ret *TxErr) {
|
||||
err := unbindAttributeViewBlock(operation, tx)
|
||||
if nil != err {
|
||||
|
|
|
@ -271,6 +271,11 @@ func RollbackDocHistory(boxID, historyPath string) (err error) {
|
|||
|
||||
FullReindex()
|
||||
IncSync()
|
||||
go func() {
|
||||
sql.WaitForWritingDatabase()
|
||||
// 刷新关联的动态锚文本 https://github.com/siyuan-note/siyuan/issues/11575
|
||||
refreshDynamicRefText(tree.Root, tree)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -250,29 +250,6 @@ func Mount(boxID string) (alreadyMount bool, err error) {
|
|||
treenode.SaveBlockTree(false)
|
||||
util.ClearPushProgress(100)
|
||||
|
||||
if isUserGuide {
|
||||
go func() {
|
||||
var startID string
|
||||
i := 0
|
||||
for ; i < 70; i++ {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
guideStartID := map[string]string{
|
||||
"20210808180117-czj9bvb": "20200812220555-lj3enxa",
|
||||
"20211226090932-5lcq56f": "20211226115423-d5z1joq",
|
||||
"20210808180117-6v0mkxr": "20200923234011-ieuun1p",
|
||||
"20240530133126-axarxgx": "20240530101000-4qitucx",
|
||||
}
|
||||
startID = guideStartID[boxID]
|
||||
if nil != treenode.GetBlockTree(startID) {
|
||||
util.BroadcastByType("main", "openFileById", 0, "", map[string]interface{}{
|
||||
"id": startID,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if reMountGuide {
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ var (
|
|||
operationQueue []*dbQueueOperation
|
||||
dbQueueLock = sync.Mutex{}
|
||||
txLock = sync.Mutex{}
|
||||
isWriting = false
|
||||
)
|
||||
|
||||
type dbQueueOperation struct {
|
||||
|
@ -77,7 +78,7 @@ func isWritingDatabase() bool {
|
|||
time.Sleep(util.SQLFlushInterval + 50*time.Millisecond)
|
||||
dbQueueLock.Lock()
|
||||
defer dbQueueLock.Unlock()
|
||||
if 0 < len(operationQueue) {
|
||||
if 0 < len(operationQueue) || isWriting {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -103,7 +104,11 @@ func FlushQueue() {
|
|||
}
|
||||
|
||||
txLock.Lock()
|
||||
defer txLock.Unlock()
|
||||
isWriting = true
|
||||
defer func() {
|
||||
isWriting = false
|
||||
txLock.Unlock()
|
||||
}()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
|
|
|
@ -32,6 +32,26 @@ var (
|
|||
sessions = sync.Map{} // {appId, {sessionId, session}}
|
||||
)
|
||||
|
||||
func BroadcastByTypeAndApp(typ, app, cmd string, code int, msg string, data interface{}) {
|
||||
appSessions, ok := sessions.Load(app)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
appSessions.(*sync.Map).Range(func(key, value interface{}) bool {
|
||||
session := value.(*melody.Session)
|
||||
if t, ok := session.Get("type"); ok && typ == t {
|
||||
event := NewResult()
|
||||
event.Cmd = cmd
|
||||
event.Code = code
|
||||
event.Msg = msg
|
||||
event.Data = data
|
||||
session.Write(event.Bytes())
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// BroadcastByType 广播所有实例上 typ 类型的会话。
|
||||
func BroadcastByType(typ, cmd string, code int, msg string, data interface{}) {
|
||||
typeSessions := SessionsByType(typ)
|
||||
|
|
Loading…
Add table
Reference in a new issue