codeModal.go (5311B)
1 package streamModals 2 3 import ( 4 "bytes" 5 "dkforest/pkg/database" 6 "dkforest/pkg/utils" 7 "dkforest/pkg/web/handlers/interceptors/command" 8 "html/template" 9 "strconv" 10 "strings" 11 ) 12 13 const name = "code" 14 15 var langs = [][]string{ 16 {"", "Raw text"}, 17 {"go", "Golang"}, 18 {"rs", "Rust"}, 19 {"cpp", "C++"}, 20 {"c", "C"}, 21 {"py", "Python"}, 22 {"js", "Javascript"}, 23 {"php", "PHP"}, 24 {"css", "CSS"}, 25 {"sql", "SQL"}, 26 {"c#", "C#"}, 27 {"rb", "Ruby"}, 28 {"html", "HTML"}, 29 {"bash", "Bash"}, 30 } 31 32 type CodeModal struct { 33 StreamModal 34 } 35 36 func (m CodeModal) Show(userID database.UserID, roomID database.RoomID, payload database.ChatMessageType) { 37 database.MsgPubSub.Pub(m.showTopic(name, userID, roomID), payload) 38 } 39 40 func (m CodeModal) Hide(userID database.UserID, roomID database.RoomID) { 41 database.MsgPubSub.Pub(m.hideTopic(name, userID, roomID), database.ChatMessageType{}) 42 } 43 44 func NewCodeModal(userID database.UserID, room database.ChatRoom) *CodeModal { 45 m := &CodeModal{StreamModal{name: name, userID: userID, room: room}} 46 m.topics = append(m.topics, m.showTopic(name, userID, room.ID), m.hideTopic(name, userID, room.ID)) 47 return m 48 } 49 50 func (m *CodeModal) Css() string { 51 return getCss() 52 } 53 54 func (m *CodeModal) Handle(db *database.DkfDB, authUser database.IUserRenderMessage, topic, csrf string, msgTyp database.ChatMessageType, send func(string)) bool { 55 if topic == m.topics[0] { 56 send(getCodeModalHTML(m.idx, m.room.Name, csrf, msgTyp, authUser.GetSyntaxHighlightCode())) 57 return true 58 59 } else if topic == m.topics[1] { 60 send(`<style>.code-modal-` + strconv.Itoa(m.idx) + `{display:none;}</style>`) 61 m.idx++ 62 return true 63 } 64 65 return false 66 } 67 68 func getCss() string { 69 return strings.Join(strings.Split(` 70 .code-modal { 71 display: block; width: calc(100% - 185px - 100px); height: calc(100% - 50px); 72 position: fixed; top: 0; left: calc(50% - ((100% - 185px - 100px)/2) - 92px); 73 background-color: gray; z-index: 999; border-radius: 5px; 74 } 75 .code-modal .header { position: absolute; top: 0; right: 0; } 76 .code-modal .header .cancel { 77 border: 1px solid gray; 78 background-color: #ff7070; 79 color: #850000; 80 font-size: 18px; 81 height: 23px; 82 border: 1px solid #850000; 83 border-radius: 0 5px 0 5px; 84 cursor: pointer; 85 } 86 .code-modal .header .cancel:hover { 87 background-color: #ff6767; 88 } 89 .code-modal .wrapper { position: absolute; top: 25px; left: 10px; right: 10px; bottom: 30px; } 90 .code-modal .wrapper textarea { width: 100%; height: 100%; color: #fff; background-color: rgba(79,79,79,1); border: 1px solid rgba(90,90,90,1); } 91 .code-modal .controls { position: absolute; left: 10px; right: 10px; bottom: 5px; }`, "\n"), " ") 92 } 93 94 func getCodeModalHTML(codeModalIdx int, roomName, csrf string, msgTyp database.ChatMessageType, syntaxHighlightCode string) string { 95 htmlTmpl := `<div class="code-modal code-modal-{{ .CodeModalIdx }}"> 96 <form method="post" target="iframe1" action="/api/v1/chat/top-bar/{{ .RoomName }}"> 97 <input type="hidden" name="csrf" value="{{ .CSRF }}" /> 98 {{ if .IsMod }} 99 <input type="hidden" name="isMod" value="1" /> 100 {{ end }} 101 {{ if .ToUserUsername }} 102 <input type="hidden" name="pm" value="{{ .ToUserUsername }}" /> 103 {{ end }} 104 <input type="hidden" name="sender" value="codeModal" /> 105 <div class="header"> 106 <button class="cancel" type="submit" name="btn_cancel" value="1">×</button> 107 </div> 108 <div class=wrapper> 109 <textarea name="message" placeholder="Paste your code here..."></textarea> 110 </div> 111 <div class="controls"> 112 <button type="submit">send</button> 113 <select name="lang"> 114 {{ range .Langs }} 115 <option value="{{ index . 0 }}"{{ if eq $.SyntaxHighlightCode (index . 0) }} selected{{ end }}>{{ index . 1 }}</option> 116 {{ end }} 117 </select> 118 </div> 119 </form> 120 </div>` 121 data := map[string]any{ 122 "CSRF": csrf, 123 "RoomName": roomName, 124 "CodeModalIdx": codeModalIdx, 125 "IsMod": msgTyp.IsMod, 126 "ToUserUsername": msgTyp.ToUserUsername, 127 "SyntaxHighlightCode": syntaxHighlightCode, 128 "Langs": langs, 129 } 130 var buf bytes.Buffer 131 _ = utils.Must(template.New("").Parse(htmlTmpl)).Execute(&buf, data) 132 return buf.String() 133 } 134 135 func (_ CodeModal) InterceptMsg(cmd *command.Command) { 136 sender := cmd.C.Request().PostFormValue("sender") 137 lang := cmd.C.Request().PostFormValue("lang") 138 isMod := utils.DoParseBool(cmd.C.Request().PostFormValue("isMod")) 139 pm := cmd.C.Request().PostFormValue("pm") 140 btnCancel := cmd.C.Request().PostFormValue("btn_cancel") 141 142 if !cmd.AuthUser.CanUseMultiline || sender != "codeModal" { 143 return 144 } 145 146 CodeModal{}.Hide(cmd.AuthUser.ID, cmd.Room.ID) 147 148 if !isValidLang(lang) { 149 lang = "" 150 } 151 cmd.AuthUser.SetSyntaxHighlightCode(cmd.DB, lang) 152 153 cmd.ModMsg = isMod 154 if pm != "" { 155 if err := cmd.SetToUser(database.Username(pm)); err != nil { 156 cmd.Err = command.ErrRedirect 157 return 158 } 159 cmd.RedirectQP.Set(command.RedirectPmQP, string(cmd.ToUser.Username)) 160 } 161 162 if cmd.OrigMessage == "" || btnCancel == "1" { 163 cmd.Err = command.ErrRedirect 164 return 165 } 166 167 cmd.OrigMessage = codeFenceWrap(lang, cmd.OrigMessage) 168 cmd.Message = codeFenceWrap(lang, cmd.Message) 169 } 170 171 func codeFenceWrap(lang, msg string) string { 172 return "\n```" + lang + "\n" + msg + "\n```\n" 173 } 174 175 func isValidLang(lang string) (found bool) { 176 for _, l := range langs { 177 if lang == l[0] { 178 return true 179 } 180 } 181 return 182 }