Progress.

This commit is contained in:
retoor 2025-01-26 22:48:58 +01:00
parent f25feeeca3
commit 488afdcc74
9 changed files with 190 additions and 31 deletions

View File

@ -22,6 +22,7 @@ from snek.view.index import IndexView
from snek.view.login import LoginView from snek.view.login import LoginView
from snek.view.logout import LogoutView from snek.view.logout import LogoutView
from snek.view.register import RegisterView from snek.view.register import RegisterView
from snek.view.rpc import RPCView
from snek.view.status import StatusView from snek.view.status import StatusView
from snek.view.web import WebView from snek.view.web import WebView
@ -77,7 +78,7 @@ class Application(BaseApplication):
self.router.add_view("/register.json", RegisterView) self.router.add_view("/register.json", RegisterView)
self.router.add_get("/http-get", self.handle_http_get) self.router.add_get("/http-get", self.handle_http_get)
self.router.add_get("/http-photo", self.handle_http_photo) self.router.add_get("/http-photo", self.handle_http_photo)
self.router.add_get("/rpc.ws",RPCView)
self.add_subapp( self.add_subapp(
"/docs", "/docs",
DocsApplication(path=pathlib.Path(__file__).parent.joinpath("docs")), DocsApplication(path=pathlib.Path(__file__).parent.joinpath("docs")),

View File

@ -0,0 +1,9 @@
from snek.model.notification import NotificationModel
from snek.system.mapper import BaseMapper
class NotificationMapper(BaseMapper):
table_name = "notification"
model_class = NotificationModel

View File

@ -2,6 +2,9 @@ import functools
from snek.service.channel import ChannelService from snek.service.channel import ChannelService
from snek.service.channel_member import ChannelMemberService from snek.service.channel_member import ChannelMemberService
from snek.service.channel_message import ChannelMessageService
from snek.service.chat import ChatService
from snek.service.socket import SocketService
from snek.service.user import UserService from snek.service.user import UserService
from snek.system.object import Object from snek.system.object import Object
@ -13,6 +16,9 @@ def get_services(app):
"user": UserService(app=app), "user": UserService(app=app),
"channel_member": ChannelMemberService(app=app), "channel_member": ChannelMemberService(app=app),
"channel": ChannelService(app=app), "channel": ChannelService(app=app),
"channel_message": ChannelMessageService(app=app),
"chat": ChatService(app=app),
"socket": SocketService(app=app),
} }
) )

View File

@ -28,6 +28,7 @@ class ChannelMemberService(BaseService):
model["is_read_only"] = is_read_only model["is_read_only"] = is_read_only
model["is_muted"] = is_muted model["is_muted"] = is_muted
model["is_banned"] = is_banned model["is_banned"] = is_banned
print(model.record,flush=True)
if await self.save(model): if await self.save(model):
return model return model
raise Exception(f"Failed to create channel member: {model.errors}.") raise Exception(f"Failed to create channel member: {model.errors}.")

View File

@ -1,6 +1,6 @@
class Message { /*class Message {
uid = null uid = null
author = null author = null
avatar = null avatar = null
@ -38,7 +38,7 @@ class Message {
} }
return result return result
} }
} }*/
class Messages { class Messages {
@ -204,17 +204,153 @@ class Chat extends EventHandler {
} }
class Socket extends EventHandler {
class App { ws = null
rooms = [] isConnected = null
isConnecting = null
connectPromises = []
constructor() { constructor() {
super()
this.ensureConnection()
}
_camelToSnake(str) {
return str
.replace(/([a-z])([A-Z])/g, '$1_$2')
.toLowerCase();
}
get client() {
const me = this
const proxy = new Proxy(
{},
{
get(target, prop) {
return (...args) => {
let functionName = me._camelToSnake(prop)
return me.call(functionName, ...args);
};
},
}
);
return proxy
}
ensureConnection(){
return this.connect()
}
generateUniqueId() {
return 'id-' + Math.random().toString(36).substr(2, 9);
}
connect(){
const me = this
if(!this.isConnected && !this.isConnecting){
this.isConnecting = true
}else if (this.isConnecting){
return new Promise((resolve,reject)=>{
me.connectPromises.push(resolve)
})
}else if(this.isConnected){
return new Promise((resolve,reject)=>{
resolve(me)
})
}
return new Promise((resolve,reject)=>{
me.connectPromises.push(resolve)
const ws = new WebSocket("ws://localhost:8081/rpc.ws")
ws.onopen = (event) => {
me.ws = ws
me.isConnected = true
me.isConnecting = false
ws.onmessage = (event) => {
me.onData(JSON.parse(event.data))
}
ws.onclose = (event) =>{
me.onClose()
}
me.connectPromises.forEach(resolve=>{
resolve(me)
})
}
})
}
onData(data){
console.debug("Data received",data)
if(data.callId){
this.emit(data.callId, data.data)
}
if(data.channel_uid){
this.emit(data.channel_uid,data.data)
this.emit("channel-message",data)
}
}
async sendJson(data){
return await this.connect().then((api)=>{
api.ws.send(JSON.stringify(data))
})
}
async call(method,...args){
const call= {
callId: this.generateUniqueId(),
method: method,
args: args
}
const me = this
return new Promise(async(resolve,reject)=>{
me.addEventListener(call.callId,(data)=>{
resolve(data)
})
await me.sendJson(call)
})
}
onClose(){
console.info("Connection lost. Reconnecting.")
this.isConnected = false
this.isConnecting = false
this.ensureConnection().then(()=>{
console.info("Reconnected.")
})
}
}
class App extends EventHandler {
rooms = []
rest = rest
ws = null
rpc = null
constructor() {
super()
this.rooms.push(new Room("General")) this.rooms.push(new Room("General"))
this.ws = new Socket()
this.rpc = this.ws.client
const me = this
this.ws.addEventListener("channel-message", (data) => {
console.debug("App channel message!",data)
me.emit(data.channel_uid,data)
})
}
async benchMark(times) {
if(!times)
times = 100
let promises = []
const me = this
for(let i = 0; i < times; i++){
promises.push(this.rpc.getChannels().then(channels=>{
channels.forEach(channel=>{
me.rpc.sendMessage(channel.uid,`Haha ${i}`).then(data=>{
console.info(data)
})
})
}))
} }
async post(url, data){ return await Promise.all(promises)
} }
} }
const app = new App()

View File

@ -109,6 +109,12 @@ main {
background: #1a1a1a; background: #1a1a1a;
} }
.message-list-manager {
flex: 1;
overflow-y: auto;
background: #1a1a1a;
}
.chat-messages .message { .chat-messages .message {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;

View File

@ -20,6 +20,10 @@ class BaseView(web.View):
def db(self): def db(self):
return self.app.db return self.app.db
@property
def services(self):
return self.app.services
async def json_response(self, data, **kwargs): async def json_response(self, data, **kwargs):
return web.json_response(data, **kwargs) return web.json_response(data, **kwargs)

View File

@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% endblock %}</title> <title>{% block title %}{% endblock %}</title>
<script src="/app.js"></script> <script src="/app.js"></script>
<script src="/message-list.js"></script>
<style>{{ highlight_styles }}</style> <style>{{ highlight_styles }}</style>
<link rel="stylesheet" href="/style.css"> <link rel="stylesheet" href="/style.css">
<script src="/fancy-button.js"></script> <script src="/fancy-button.js"></script>

View File

@ -5,6 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snek</title> <title>Snek</title>
<script src="/app.js"></script> <script src="/app.js"></script>
<script src="/models.js"></script>
<script src="/message-list.js"></script>
<script src="/message-list-manager.js"></script>
<link rel="stylesheet" href="base.css"> <link rel="stylesheet" href="base.css">
</head> </head>
<body> <body>
@ -31,31 +34,23 @@
<div class="chat-header"> <div class="chat-header">
<h2>General</h2> <h2>General</h2>
</div> </div>
<div class="chat-messages"> <message-list-manager class="message-list-manager"></message-list-manager>
<div class="message">
<div class="avatar">A</div>
<div class="message-content">
<div class="author">Alice</div>
<div class="text">Hello, everyone!</div>
<div class="time">10:45 AM</div>
</div>
</div>
<html-frame class="html-frame" url="/register"></html-frame>
<div class="message">
<div class="avatar">B</div>
<div class="message-content">
<div class="author">Bob</div>
<div class="text">Hi Alice! How are you?</div>
<div class="time">10:46 AM</div>
</div>
</div>
</div>
<div class="chat-input"> <div class="chat-input">
<textarea placeholder="Type a message..." rows="2"></textarea> <textarea placeholder="Type a message..." rows="2"></textarea>
<button>Send</button> <button>Send</button>
</div> </div>
</section> </section>
</main> </main>
<script>
document.addEventListener("DOMContentLoaded",()=>{
setTimeout(()=>{
app.benchMark(3).then(result=>{
console.info("Benchmarked")
})
},1000)
})
</script>
</body> </body>
</html> </html>