personal-website/apps/aboutme/personalprops.go
2023-07-24 03:27:53 +03:00

336 lines
7.9 KiB
Go

package aboutme
import (
"errors"
"html/template"
"net/http"
"path"
"personalwebsite/apps"
"personalwebsite/apps/appCtx"
"personalwebsite/errormessage"
"personalwebsite/libs"
"personalwebsite/wde"
"personalwebsite/webfilesystem"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type AboutMeApp struct {
fs *webfilesystem.WebFileSystem
appID string
mLib libs.MarkdownLib
titleBarConfig wde.TitleBarConfig //TODO to app manifest?
path string
manifest apps.ApplicationManifest
}
func NewAboutMeApp(webFs *webfilesystem.WebFileSystem) *AboutMeApp {
manifest := apps.ApplicationManifest{}
_, err := webFs.Read(path.Join("/Applications/AboutMe.app", ".appmanifest"), &manifest)
if err != nil {
panic(err)
}
newApp := AboutMeApp{
fs: webFs,
appID: "AboutMe",
path: "/Applications/AboutMe.app",
titleBarConfig: wde.TitleBarConfig{
Lable: "About Me",
CloseButton: true,
HasIcon: true,
IconPath: "/Icons/GenericApp.icn",
IconSize: "16",
},
manifest: manifest,
}
return &newApp
}
func (p *AboutMeApp) GetPath() string {
return p.path
}
func (p *AboutMeApp) GetManifest() apps.ApplicationManifest {
return p.manifest
}
func (p *AboutMeApp) PublicRoutes(route *gin.RouterGroup) {
route.POST("render", func(ctx *gin.Context) {
filePath := ctx.Query("path")
if filePath == "" {
ctx.Status(http.StatusBadRequest)
return
}
appCtx := appCtx.AppContext{}
err := ctx.BindJSON(&appCtx)
if err != nil {
ctx.Status(http.StatusBadRequest)
return
}
ginH, err := p.Render(appCtx, filePath)
if err != nil {
ctx.JSON(http.StatusInternalServerError, "TODO") //TODO
return
}
if appCtx.IsMobile {
ctx.HTML(http.StatusOK, "personal-properties/mobile-app.tmpl", ginH)
} else {
ctx.HTML(http.StatusOK, "personal-properties/app.tmpl", ginH)
}
})
}
func (p *AboutMeApp) PrivateRoutes(router *gin.RouterGroup) {
p.PublicRoutes(router)
router.GET("writeMock", func(ctx *gin.Context) {
err := p.WriteMock()
if err != nil {
ctx.Status(http.StatusInternalServerError)
}
ctx.Status(http.StatusOK)
})
router.GET("getMock", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, PropertiesFileData{
Header: HeaderIsland{
Name: "",
IconPath: "",
Value1: "",
Value2: "",
},
Links: []HeaderLink{
{
Text: "",
Url: "",
Icon: "",
},
},
Islands: []Island{
{
Header: "qq",
Properties: []IslandProperty{
{
Key: "22",
KeyComments: []string{
"45",
"12",
},
Values: []string{
"aaaa",
},
},
},
},
},
})
})
router.GET("read", func(ctx *gin.Context) {
filePath := ctx.Query("path")
if filePath == "" {
ctx.JSON(http.StatusBadRequest, errormessage.ErrorMessage{
Message: "File path is empty",
})
return
}
propData, err := p.Read(filePath)
if err != nil {
ctx.JSON(http.StatusInternalServerError, errormessage.ErrorMessage{
Message: err.Error(),
})
return
}
ctx.JSON(http.StatusOK, propData)
})
router.POST("edit", func(ctx *gin.Context) {
filePath := ctx.Query("path")
if filePath == "" {
ctx.JSON(http.StatusBadRequest, errormessage.ErrorMessage{
Message: "File path is empty",
})
return
}
propsData := PropertiesFileData{}
err := ctx.BindJSON(&propsData)
if err != nil {
ctx.JSON(http.StatusBadRequest, errormessage.ErrorMessage{
Message: err.Error(),
})
return
}
err = p.Edit(filePath, propsData)
if err != nil {
ctx.JSON(http.StatusInternalServerError, errormessage.ErrorMessage{
Message: err.Error(),
})
return
}
ctx.Status(http.StatusOK)
})
}
func (p *AboutMeApp) GetAppID() string {
return p.appID
}
func (p *AboutMeApp) WriteMock() error {
fileHeader := webfilesystem.FileHeader{
MongoId: primitive.NewObjectID(),
Name: "aboutme.props",
Type: "personal-properties",
Icon: "",
}
fileData := PropertiesFileData{
Header: HeaderIsland{
Name: "Test Name",
IconPath: "test icon path",
},
}
_, _, err := p.fs.Write("/Applications/AboutMe.app/aboutme.props", &fileHeader, fileData)
return err
}
func (p *AboutMeApp) Read(filePath string) (*PropertiesFileData, error) {
propData := PropertiesFileData{}
fileHeader, err := p.fs.Read(path.Join(filePath, "aboutme.props"), &propData)
if err != nil {
return nil, err
}
if fileHeader.Type != "personal-properties" {
return nil, errors.New("wrong file type")
}
return &propData, nil
}
func (p *AboutMeApp) Edit(filePath string, blogData PropertiesFileData) error {
propsPath := path.Join(filePath, "aboutme.props")
fileHeader, err := p.fs.Read(propsPath, nil)
if err != nil {
if err.Error() == "file not found" { //FIXME
p.WriteMock()
p.Edit(filePath, blogData)
} else {
return err
}
}
if fileHeader.Type != "personal-properties" {
return errors.New("wrong file type")
}
err = p.fs.Remove(propsPath)
if err != nil {
return err
}
fileHeader.MongoId = primitive.NewObjectID()
_, _, err = p.fs.Write(propsPath, fileHeader, blogData)
if err != nil {
return err
}
return nil
}
func (p *AboutMeApp) Render(appCtx appCtx.AppContext, filePath string) (gin.H, error) {
//Read file from WebFS
propsData := &PropertiesFileData{}
filePath = p.fs.RelativeToAbsolute(appCtx, filePath)
_, err := p.fs.Read(filePath, &propsData)
if err != nil {
return nil, err
}
//Render Markdown strings
renderedIslands := []RenderedIsland{}
for _, island := range propsData.Islands {
newRenderedIsland := RenderedIsland{
Header: island.Header,
Properties: []RenderedIslandProperty{},
}
for _, property := range island.Properties {
newRenderedIslandProperty := RenderedIslandProperty{
Key: property.Key,
KeyComments: property.KeyComments,
Values: []template.HTML{},
}
for _, value := range property.Values {
renderedValue := p.mLib.Render([]byte(value), true)
newRenderedIslandProperty.Values = append(newRenderedIslandProperty.Values, renderedValue)
}
newRenderedIsland.Properties = append(newRenderedIsland.Properties, newRenderedIslandProperty)
}
renderedIslands = append(renderedIslands, newRenderedIsland)
}
//Make icon path absolute
propsData.Header.IconPath = p.fs.RelativeToAbsolute(appCtx, propsData.Header.IconPath)
absoluteLinks := []HeaderLink{}
for _, link := range propsData.Links {
absoluteLinks = append(absoluteLinks, HeaderLink{
Text: link.Text,
Url: link.Url,
Icon: p.fs.RelativeToAbsolute(appCtx, link.Icon),
})
}
return gin.H{
"TitleBarConfig": p.titleBarConfig,
"HeaderProps": propsData.Header,
"Links": absoluteLinks,
"Islands": renderedIslands,
}, nil
}
type PropertiesFileData struct {
Header HeaderIsland `bson:"header" json:"header"`
Links []HeaderLink `bson:"links" json:"links"`
Islands []Island `bson:"islands" json:"islands"` //TODO rename
}
type HeaderIsland struct {
Name string `bson:"name" json:"name"`
IconPath string `bson:"iconpath" json:"iconpath"`
Value1 string `bson:"value1" json:"value1"` //TODO Rename to value
Value2 string `bson:"value2" json:"value2"`
}
type HeaderLink struct {
Text string `bson:"text" json:"text"`
Url string `bson:"url" json:"url"`
Icon string `bson:"icon" json:"icon"`
}
type Island struct {
Header string `bson:"header" json:"header"`
Properties []IslandProperty `bson:"properties" json:"properties"`
}
type IslandProperty struct {
Key string `bson:"key" json:"key"`
KeyComments []string `bson:"keycomment" json:"keycomment"`
Values []string `bson:"values" json:"values"`
}
type RenderedIsland struct {
Header string
Properties []RenderedIslandProperty
}
type RenderedIslandProperty struct {
Key string
KeyComments []string
Values []template.HTML
}
// type Value struct {
// Blocks []ValueBlock
// }
// type ValueBlock struct {
// Type string
// Data string
// }