diff --git a/src/snek/app.py b/src/snek/app.py index 80be7b5..f242090 100644 --- a/src/snek/app.py +++ b/src/snek/app.py @@ -22,6 +22,7 @@ from snek.view.index import IndexView from snek.view.login import LoginView from snek.view.logout import LogoutView from snek.view.register import RegisterView +from snek.view.rpc import RPCView from snek.view.status import StatusView from snek.view.web import WebView @@ -77,7 +78,7 @@ class Application(BaseApplication): self.router.add_view("/register.json", RegisterView) self.router.add_get("/http-get", self.handle_http_get) self.router.add_get("/http-photo", self.handle_http_photo) - + self.router.add_get("/rpc.ws",RPCView) self.add_subapp( "/docs", DocsApplication(path=pathlib.Path(__file__).parent.joinpath("docs")), diff --git a/src/snek/mapper/notification.py b/src/snek/mapper/notification.py index e69de29..05e62d8 100644 --- a/src/snek/mapper/notification.py +++ b/src/snek/mapper/notification.py @@ -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 \ No newline at end of file diff --git a/src/snek/service/__init__.py b/src/snek/service/__init__.py index c81a456..db4a00f 100644 --- a/src/snek/service/__init__.py +++ b/src/snek/service/__init__.py @@ -2,6 +2,9 @@ import functools from snek.service.channel import ChannelService 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.system.object import Object @@ -13,6 +16,9 @@ def get_services(app): "user": UserService(app=app), "channel_member": ChannelMemberService(app=app), "channel": ChannelService(app=app), + "channel_message": ChannelMessageService(app=app), + "chat": ChatService(app=app), + "socket": SocketService(app=app), } ) diff --git a/src/snek/service/channel_member.py b/src/snek/service/channel_member.py index adbcded..a1eec8c 100644 --- a/src/snek/service/channel_member.py +++ b/src/snek/service/channel_member.py @@ -28,6 +28,7 @@ class ChannelMemberService(BaseService): model["is_read_only"] = is_read_only model["is_muted"] = is_muted model["is_banned"] = is_banned + print(model.record,flush=True) if await self.save(model): return model raise Exception(f"Failed to create channel member: {model.errors}.") diff --git a/src/snek/static/app.js b/src/snek/static/app.js index 133cbcd..2be1c09 100644 --- a/src/snek/static/app.js +++ b/src/snek/static/app.js @@ -1,6 +1,6 @@ -class Message { +/*class Message { uid = null author = null avatar = null @@ -38,7 +38,7 @@ class Message { } return result } -} +}*/ class Messages { @@ -204,17 +204,153 @@ class Chat extends EventHandler { } - -class App { - rooms = [] +class Socket extends EventHandler { + ws = null + isConnected = null + isConnecting = null + connectPromises = [] 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.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 post(url, 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) + }) + }) + })) + + } + return await Promise.all(promises) } -} \ No newline at end of file +} + +const app = new App() \ No newline at end of file diff --git a/src/snek/static/base.css b/src/snek/static/base.css index 263f1eb..b674b8d 100644 --- a/src/snek/static/base.css +++ b/src/snek/static/base.css @@ -109,6 +109,12 @@ main { background: #1a1a1a; } +.message-list-manager { + flex: 1; + overflow-y: auto; + background: #1a1a1a; +} + .chat-messages .message { display: flex; align-items: flex-start; diff --git a/src/snek/system/view.py b/src/snek/system/view.py index 8b775e0..cd9112d 100644 --- a/src/snek/system/view.py +++ b/src/snek/system/view.py @@ -19,6 +19,10 @@ class BaseView(web.View): @property def db(self): return self.app.db + + @property + def services(self): + return self.app.services async def json_response(self, data, **kwargs): return web.json_response(data, **kwargs) diff --git a/src/snek/templates/base.html b/src/snek/templates/base.html index cb8fc5d..36a012c 100644 --- a/src/snek/templates/base.html +++ b/src/snek/templates/base.html @@ -5,6 +5,7 @@