Skip to content

Commit

Permalink
add sorting functionality (RecentStickers)
Browse files Browse the repository at this point in the history
  • Loading branch information
koenigskraut committed Aug 5, 2023
1 parent 8970bc4 commit 7c8eb94
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 47 deletions.
66 changes: 55 additions & 11 deletions cmd/webapp/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
}
circle#radio-circle {
transition: all 0.2s;
transform: translateX(22%);
}
</style>
<script>
Expand All @@ -161,8 +162,10 @@
let container = null;
let stickerNumber = 0;
const stickerTypes = {"webp": "s", "tgs": "l", "webm": "v"};
webApp.MainButton.text = "Сохранить порядок";
webApp.MainButton.text = "Сохранить изменения";
let animations = {};
let sessionGlobal = {};
let newFirst = false;

function registerContainer() {
container = document.getElementById("gridDemo");
Expand Down Expand Up @@ -232,22 +235,56 @@
});
}

function registerRadio() {
const button = document.getElementById("radio-svg");
function registerRadio(isNewFirst) {
const radio = document.getElementById("radio-svg");
const circle = document.getElementById("radio-circle");
button.addEventListener("click", function (){
if (isNewFirst) {
circle.style.transform = "translateX(0%)";
} else {
circle.style.transform = "translateX(44%)";
}
radio.addEventListener("click", function (){
if (circle.style.transform === "translateX(44%)") {
circle.style.transform = "translateX(0%)";
newFirst = true;
} else {
circle.style.transform = "translateX(44%)";
newFirst = false;
}
webApp.MainButton.show();
});
}

function registerMainButton() {
webApp.onEvent("mainButtonClicked", async () => {
const parent = document.getElementById("gridDemo");
let order = [];
for (const item of parent.children) {
if (item.hasAttribute("data-sticker-id"))
order.push(item.getAttribute("data-sticker-id"));
}
// apparently 64-bit integer handling is too much for JS
let strJSON = '{"session":' + JSON.stringify(sessionGlobal)+',';
strJSON += '"order":[' + order.join(",") + '],';
strJSON += newFirst ? '"new_first":true}' : '"new_first":false}';
console.log(strJSON);
await fetch("/updateOrder", {
method: "POST",
body: strJSON
}).then(async res => {
if (!res.ok) {
console.error(await res.text());
return;
}
webApp.MainButton.hide();
});
});
}

async function foo() {
window.Telegram.WebApp.ready();
const overlay = document.getElementById("overlay");
registerRadio();
registerMainButton();
registerContainer();
const searchString = window.location.search.substring(1);
const requestBody = searchString ? searchString : webApp.initData;
Expand All @@ -259,35 +296,42 @@
console.error(await res.text());
return;
}
let resp = await res.json();
console.log(resp);
sessionGlobal = await res.json();
console.log("got " + JSON.stringify(sessionGlobal));

let socket = new WebSocket(wsPath);
socket.onopen = () => {
console.log("socket opened");
socket.send(JSON.stringify(resp));
console.log("TO SERVER: \"" + JSON.stringify(resp) + "\"");
socket.send(JSON.stringify(sessionGlobal));
console.log("TO SERVER: \"" + JSON.stringify(sessionGlobal) + "\"");
};
socket.onerror = (ev) => console.error(ev);
socket.onmessage = (ev) => {
if (ev.data.startsWith("newFirst")) {
newFirst = (ev.data[10] === "1");
registerRadio(newFirst);
return;
}
let elem = document.createElement("div");
elem.className = "grid-square";
elem.id = "elem" + String(stickerNumber+1);
const stickerID = ev.data.substring(1);
elem.setAttribute("data-sticker-id", stickerID);
if (ev.data[0] === stickerTypes.tgs) {
animations[elem.id] = bodymovin.loadAnimation({
container: elem,
renderer: 'svg',
loop: false,
autoplay: false,
path: "/stickers/" + ev.data.substring(1),
path: "/stickers/" + stickerID,
rendererSettings: {
className: 'square-img',
progressiveLoad: true,
}
});
} else {
let elemChild = document.createElement("img");
elemChild.src = "/stickers/" + ev.data.substring(1);
elemChild.src = "/stickers/" + stickerID;
elemChild.className = "square-img";
elem.appendChild(elemChild);
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/webapp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
WebAppHashPath = "/hash"
WebAppWsPath = "/ws"
WebAppStickersPath = "/stickers/"
WebAppOrderUpdate = "/updateOrder"
)

//go:embed index.html
Expand Down Expand Up @@ -59,6 +60,7 @@ func main() {
http.HandleFunc(WebAppHashPath, handleVerification)
http.HandleFunc(WebAppWsPath, handleWS)
http.Handle(WebAppStickersPath, http.StripPrefix(WebAppStickersPath, http.FileServer(http.Dir(stickerPath))))
http.HandleFunc(WebAppOrderUpdate, handleOrderUpdate)

go func() {
err := http.ListenAndServeTLS(address, certFile, keyFile, nil)
Expand Down
71 changes: 71 additions & 0 deletions cmd/webapp/order.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package main

import (
"encoding/json"
"fmt"
db "github.com/koenigskraut/piktagbot/database"
"github.com/koenigskraut/piktagbot/util"
"net/http"
)

type TagOrderUpdate struct {
UserSession Session `json:"session"`
Order []int64 `json:"order"`
NewFirst bool `json:"new_first"`
}

func handleOrderUpdate(writer http.ResponseWriter, request *http.Request) {
fmt.Println("order called")
var update TagOrderUpdate
dec := json.NewDecoder(request.Body)
if dec.Decode(&update) != nil {
http.Error(writer, "malformed json", http.StatusBadRequest)
return
}
// verification
hashCompareTo := update.UserSession.Hash
update.UserSession.Hash = ""
hashCalculated, err := util.HashOfJSON(update.UserSession)
if err != nil {
http.Error(writer, "server error", http.StatusInternalServerError)
return
}
if hashCalculated != hashCompareTo {
http.Error(writer, "hash mismatch", http.StatusBadRequest)
return
}
// get session
verifiedSession, ok := oneTimeSessions.peek(update.UserSession.SessionID)
if !ok {
http.Error(writer, "session expired", http.StatusBadRequest)
return
}
// check ip and user-agent
userHash := util.HashOfRequestUser(request)
if verifiedSession.clientHash != userHash {
http.Error(writer, "bad user", http.StatusBadRequest)
return
}
// get user
dbUser := db.User{UserID: verifiedSession.userID}
if _, err := dbUser.Get(); err != nil {
http.Error(writer, "server error", http.StatusInternalServerError)
return
}

order, err := dbUser.GetOrder("")
if err != nil {
http.Error(writer, "server error", http.StatusInternalServerError)
return
}
b, err := db.WriteTagOrder(update.Order)
if err != nil {
http.Error(writer, "server error", http.StatusInternalServerError)
return
}
order.TagOrder, order.NewFirst = b, update.NewFirst
if err := order.Save(); err != nil {
http.Error(writer, "server error", http.StatusInternalServerError)
return
}
}
23 changes: 19 additions & 4 deletions cmd/webapp/ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ func handleWS(writer http.ResponseWriter, request *http.Request) {
log.Println(err)
return
}
recentStickers, _ := dbUser.RecentStickers()
recentStickers, err := dbUser.RecentStickers()
if err != nil {
log.Println(err)
return
}
locations := make([]InputDocumentMimeTyped, 0, len(recentStickers))
for _, r := range recentStickers {
locations = append(locations, InputDocumentMimeTyped{
Expand All @@ -75,17 +79,28 @@ func handleWS(writer http.ResponseWriter, request *http.Request) {
},
})
}
fmt.Println(locations)
var newFirst []byte
newFirstBool, err := dbUser.GetNewFirst("")
if err != nil {
log.Println(err)
return
}
if newFirstBool {
newFirst = []byte("newFirst: 1")
} else {
newFirst = []byte("newFirst: 0")
}

myChan := make(chan string)
myFiles := receiveFiles{
files: locations,
ch: myChan,
}
downloadChan <- &myFiles
fmt.Println("sent")
if err := wsutil.WriteServerText(conn, newFirst); err != nil {
log.Println(3, err)
}
for r := range myChan {
fmt.Println(r)
if err := wsutil.WriteServerText(conn, []byte(r)); err != nil {
log.Println(3, err)
}
Expand Down
104 changes: 104 additions & 0 deletions database/stickerOrder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package database

import (
"bytes"
"encoding/binary"
"fmt"
)

// mysql> describe sticker_orders;
// +-----------+--------------+------+-----+---------+----------------+
// | Field | Type | Null | Key | Default | Extra |
// +-----------+--------------+------+-----+---------+----------------+
// | id | int unsigned | NO | PRI | NULL | auto_increment |
// | user | bigint | NO | | NULL | |
// | tag | text | YES | | NULL | |
// | new_first | tinyint(1) | NO | | 1 | |
// | tag_order | blob | YES | | NULL | |
// +-----------+--------------+------+-----+---------+----------------+

type StickerOrder struct {
ID uint64 `gorm:"primaryKey"`
User int64
Tag string
NewFirst bool
TagOrder []byte
}

func readTagOrder(serialized []byte) (tagOrder []int64, err error) {
tagOrder = make([]int64, len(serialized)/8)
buf := bytes.NewReader(serialized)
err = binary.Read(buf, binary.LittleEndian, tagOrder)
return
}

func writeTagOrder(order []int64) ([]byte, error) {
buf := &bytes.Buffer{}
err := binary.Write(buf, binary.LittleEndian, order)
return buf.Bytes(), err
}

func ReadTagOrder(serialized []byte) (tagOrder []int64, err error) {
return readTagOrder(serialized)
}

func WriteTagOrder(order []int64) (serialized []byte, err error) {
return writeTagOrder(order)
}

func sortByOrder(toSort []*StickerTag, order []int64, unorderedFirst bool) {
sortedMap := make(map[int64]*StickerTag)
for _, s := range toSort {
sortedMap[s.Sticker.DocumentID] = s
}
results := make([]*StickerTag, 0, len(toSort))
for _, o := range order {
if s, ok := sortedMap[o]; ok {
results = append(results, s)
delete(sortedMap, o)
}
}
i := 0
for _, v := range toSort {
if s, ok := sortedMap[v.Sticker.DocumentID]; ok {
toSort[i] = s
i++
}
}
if unorderedFirst {
copy(toSort[i:], results)
} else {
copy(toSort[len(toSort)-i:], toSort[:i])
copy(toSort, results)
}
}

func (so *StickerOrder) SortStickers(stickers []*StickerTag) error {
order, err := readTagOrder(so.TagOrder)
if err != nil {
fmt.Println(err)
return err
}
sortByOrder(stickers, order, so.NewFirst)
return nil
}

func (so *StickerOrder) Save() error {
return DB.Save(so).Error
}

func (so *StickerOrder) UpdateFromOrder(order []int64) error {
serialized, err := writeTagOrder(order)
if err != nil {
return err
}
return DB.Model(so).Where(&StickerOrder{ID: so.ID}).Update("tag_order", serialized).Error
}

func (so *StickerOrder) UpdateFromStickers(stickers []*StickerTag) error {
order := make([]int64, len(stickers))
for i, s := range stickers {
order[i] = s.Sticker.DocumentID
}
return so.UpdateFromOrder(order)
}
Loading

0 comments on commit 7c8eb94

Please sign in to comment.