comparison libervia.py @ 33:e70521e6d803

browser side, misc stuffs - dropCell are now keep the dragover style if the mouse go on an other widget inside them - uniBox moved to panels.py - chatPanel now partially managed MUC Room - room joined open in a new tab (as Tarot games) - MainPanel & MainTabPanel are now dynamically sized in pixels - fixed stupid bug in json callbacks management
author Goffi <goffi@goffi.org>
date Sat, 14 May 2011 01:11:08 +0200
parents 258dfaa1035f
children d43d6e4b9dc8
comparison
equal deleted inserted replaced
32:6b8da70b0799 33:e70521e6d803
18 You should have received a copy of the GNU Affero General Public License 18 You should have received a copy of the GNU Affero General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. 19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """ 20 """
21 21
22 import pyjd # this is dummy in pyjs 22 import pyjd # this is dummy in pyjs
23 from pyjamas.ui.SimplePanel import SimplePanel
24 from pyjamas.ui.RootPanel import RootPanel 23 from pyjamas.ui.RootPanel import RootPanel
25 from pyjamas.ui.AutoComplete import AutoCompleteTextBox
26 from pyjamas.ui.PopupPanel import PopupPanel
27 from pyjamas.ui.HTML import HTML 24 from pyjamas.ui.HTML import HTML
28 from pyjamas.Timer import Timer
29 from pyjamas import Window 25 from pyjamas import Window
30 from pyjamas.JSONService import JSONProxy 26 from pyjamas.JSONService import JSONProxy
31 from pyjamas.ui.KeyboardListener import KEY_ENTER
32 from browser_side.register import RegisterPanel, RegisterBox 27 from browser_side.register import RegisterPanel, RegisterBox
33 from browser_side.contact import ContactPanel 28 from browser_side.contact import ContactPanel
34 from browser_side.panels import MainPanel, EmptyPanel, MicroblogPanel, ChatPanel, StatusPanel 29 from browser_side.panels import MainPanel, EmptyPanel, MicroblogPanel, ChatPanel, StatusPanel
35 from browser_side.jid import JID 30 from browser_side.jid import JID
36 31
40 JSONProxy.__init__(self, *args, **kwargs) 35 JSONProxy.__init__(self, *args, **kwargs)
41 self.handler=self 36 self.handler=self
42 self.cb={} 37 self.cb={}
43 38
44 def call(self, method, cb, *args): 39 def call(self, method, cb, *args):
40 id = self.callMethod(method,args)
45 if cb: 41 if cb:
46 self.cb[method] = cb 42 self.cb[id] = cb
47 self.callMethod(method,args)
48 43
49 def onRemoteResponse(self, response, request_info): 44 def onRemoteResponse(self, response, request_info):
50 if self.cb.has_key(request_info.method): 45 if self.cb.has_key(request_info.id):
51 self.cb[request_info.method](response) 46 self.cb[request_info.id](response)
52 del self.cb[request_info.method] 47 del self.cb[request_info.id]
53 48
54 def onRemoteError(self, code, errobj, request_info): 49 def onRemoteError(self, code, errobj, request_info):
55 if code != 0: 50 if code != 0:
56 Window.alert("Internal server error") 51 Window.alert("Internal server error")
57 else: 52 else:
68 self.cb={} 63 self.cb={}
69 64
70 class BridgeCall(LiberviaJsonProxy): 65 class BridgeCall(LiberviaJsonProxy):
71 def __init__(self): 66 def __init__(self):
72 LiberviaJsonProxy.__init__(self, "/json_api", 67 LiberviaJsonProxy.__init__(self, "/json_api",
73 ["getContacts", "sendMessage", "sendMblog", "getMblogNodes", "getProfileJid", "getHistory", "getPresenceStatus", "launchTarotGame"]) 68 ["getContacts", "sendMessage", "sendMblog", "getMblogNodes", "getProfileJid", "getHistory",
69 "getPresenceStatus", "getRoomJoined", "launchTarotGame"])
74 70
75 class BridgeSignals(LiberviaJsonProxy): 71 class BridgeSignals(LiberviaJsonProxy):
76 def __init__(self): 72 def __init__(self):
77 LiberviaJsonProxy.__init__(self, "/json_signal_api", 73 LiberviaJsonProxy.__init__(self, "/json_signal_api",
78 ["getSignals"]) 74 ["getSignals"])
79
80
81 class UniBox(AutoCompleteTextBox):
82 """This text box is used as a main typing point, for message, microblog, etc"""
83
84 def __init__(self, host):
85 AutoCompleteTextBox.__init__(self)
86 self._popup = None
87 self._timer = Timer(notify=self._timeCb)
88 self.host = host
89
90 def addKey(self, key):
91 self.getCompletionItems().completions.append(key)
92
93 def showWarning(self, target_data):
94 type, target = target_data
95 if type == "PUBLIC":
96 msg = "This message will be PUBLIC and everybody will be able to see it, even people you don't know"
97 style = "targetPublic"
98 elif type == "GROUP":
99 msg = "This message will be published for all the people of the group <span class='warningTarget'>%s</span>" % (target or '')
100 style = "targetGroup"
101 elif type == "STATUS":
102 msg = "This will be your new status message"
103 style = "targetStatus"
104 elif type == "ONE2ONE":
105 msg = "This message will be sent to your contact <span class='warningTarget'>%s</span>" % target
106 style = "targetOne2One"
107 else:
108 print "WARNING: undetermined target for this message"
109 return
110 contents = HTML(msg)
111
112 self._popup = PopupPanel(autoHide=False, modal=False)
113 self._popup.target_data = target_data
114 self._popup.add(contents)
115 self._popup.setStyleName("warningPopup")
116 if style:
117 self._popup.addStyleName(style)
118
119 left = 0
120 top = 0 #max(0, self.getAbsoluteTop() - contents.getOffsetHeight() - 2)
121 self._popup.setPopupPosition(left, top)
122 self._popup.setPopupPosition(left, top)
123 self._popup.show()
124
125 def _timeCb(self, timer):
126 if self._popup:
127 self._popup.hide()
128 del self._popup
129 self._popup = None
130
131 def _getTarget(self, txt):
132 """Say who will receive the messsage
133 Return a tuple (target_type, target info)"""
134 type = None
135 target = None
136 if txt.startswith('@@: '):
137 type = "PUBLIC"
138 elif txt.startswith('@'):
139 type = "GROUP"
140 _end = txt.find(': ')
141 if _end == -1:
142 type = "STATUS"
143 else:
144 target = txt[1:_end] #only one target group is managed for the moment
145 if not target in self.host.contact_panel.getGroups():
146 target = None
147 elif self.host.selected == None:
148 type = "STATUS"
149 elif isinstance(self.host.selected, ChatPanel):
150 type = "ONE2ONE"
151 target = str(self.host.selected.target)
152 else:
153 print self.host.selected
154 type = "UNKNOWN"
155 return (type, target)
156
157 def onKeyPress(self, sender, keycode, modifiers):
158 _txt = self.getText()
159 if not self._popup:
160 self.showWarning(self._getTarget(_txt))
161 else:
162 _target = self._getTarget(_txt)
163 if _target != self._popup.target_data:
164 self._timeCb(None) #we remove the popup
165 self.showWarning(_target)
166
167 self._timer.schedule(2000)
168
169 if keycode == KEY_ENTER and not self.visible:
170 if _txt:
171 if _txt.startswith('@'):
172 self.host.bridge.call('sendMblog', None, self.getText())
173 elif self.host.selected == None:
174 self.host.bridge.call('setStatus', None, _txt)
175 elif isinstance(self.host.selected, ChatPanel):
176 _chat = self.host.selected
177 self.host.bridge.call('sendMessage', None, str(_chat.target), _txt, '', 'chat')
178 self.setText('')
179 self._timeCb(None) #we remove the popup
180
181 def complete(self):
182 #self.visible=False #XXX: self.visible is not unset in pyjamas when ENTER is pressed and a completion is done
183 #XXX: fixed directly on pyjamas, if the patch is accepted, no need to walk around this
184 return AutoCompleteTextBox.complete(self)
185
186 75
187 class SatWebFrontend: 76 class SatWebFrontend:
188 def onModuleLoad(self): 77 def onModuleLoad(self):
189 self.whoami = None 78 self.whoami = None
190 self.bridge = BridgeCall() 79 self.bridge = BridgeCall()
191 self.bridge_signals = BridgeSignals() 80 self.bridge_signals = BridgeSignals()
192 self.selected = None 81 self.selected = None
193 self.uni_box = UniBox(self) 82 self.uni_box = None
194 self.uni_box.addKey("@@: ")
195 self.status_panel = StatusPanel(self) 83 self.status_panel = StatusPanel(self)
196 self.contact_panel = ContactPanel(self) 84 self.contact_panel = ContactPanel(self)
197 self.panel = MainPanel(self) 85 self.panel = MainPanel(self)
198 self.discuss_panel = self.panel.discuss_panel 86 self.discuss_panel = self.panel.discuss_panel
199 self.tab_panel = self.panel.tab_panel 87 self.tab_panel = self.panel.tab_panel
200 self.mpanels = [EmptyPanel(self), MicroblogPanel(self, accept_all=True), EmptyPanel(self)] 88 self.mpanels = [EmptyPanel(self), MicroblogPanel(self, accept_all=True), EmptyPanel(self)]
89 self.other_panels = [] #panels not on the main tab #FIXME: temporary, need to be changed
90 self.room_list = set() #set of rooms
201 self.discuss_panel.changePanel(0,self.mpanels[0]) 91 self.discuss_panel.changePanel(0,self.mpanels[0])
202 self.discuss_panel.changePanel(1,self.mpanels[1]) 92 self.discuss_panel.changePanel(1,self.mpanels[1])
203 self.discuss_panel.changePanel(2,self.mpanels[2]) 93 self.discuss_panel.changePanel(2,self.mpanels[2])
204 self._dialog = None 94 self._dialog = None
205 RootPanel().add(self.panel) 95 RootPanel().add(self.panel)
96 self.resize()
206 self._register = RegisterCall() 97 self._register = RegisterCall()
207 self._register.call('isRegistered',self._isRegisteredCB) 98 self._register.call('isRegistered',self._isRegisteredCB)
99
100 def resize(self):
101 """Resize elements"""
102 Window.onResize()
103
104 def setUniBox(self, unibox):
105 """register the unibox widget"""
106 self.uni_box = unibox
107 self.uni_box.addKey("@@: ")
208 108
209 def select(self, widget): 109 def select(self, widget):
210 """Define the selected widget""" 110 """Define the selected widget"""
211 if self.selected: 111 if self.selected:
212 self.selected.removeStyleName('selected_widget') 112 self.selected.removeStyleName('selected_widget')
213 self.selected = widget 113 self.selected = widget
214 if widget: 114 if widget:
215 self.selected.addStyleName('selected_widget') 115 self.selected.addStyleName('selected_widget')
116
117 def addTab(self, panel, label):
118 """Add a panel in a tab
119 @param panel: panel to add
120 @param label: label of the tab"""
121 self.tab_panel.add(panel, label)
122 self.other_panels.append(panel)
216 123
217 def _isRegisteredCB(self, registered): 124 def _isRegisteredCB(self, registered):
218 if not registered: 125 if not registered:
219 self._dialog = RegisterBox(self.logged,centered=True) 126 self._dialog = RegisterBox(self.logged,centered=True)
220 self._dialog.show() 127 self._dialog.show()
254 self._newMessageCb(*args) 161 self._newMessageCb(*args)
255 elif name == 'presenceUpdate': 162 elif name == 'presenceUpdate':
256 self._presenceUpdateCb(*args) 163 self._presenceUpdateCb(*args)
257 elif name == 'roomJoined': 164 elif name == 'roomJoined':
258 self._roomJoinedCb(*args) 165 self._roomJoinedCb(*args)
166 elif name == 'roomUserJoined':
167 self._roomUserJoinedCb(*args)
168 elif name == 'roomUserLeft':
169 self._roomUserLeftCb(*args)
259 170
260 def _getProfileJidCB(self, jid): 171 def _getProfileJidCB(self, jid):
261 self.whoami = JID(jid) 172 self.whoami = JID(jid)
262 #we can now ask our status 173 #we can now ask our status
263 self.bridge.call('getPresenceStatus', self._getPresenceStatusCB) 174 self.bridge.call('getPresenceStatus', self._getPresenceStatusCB)
175 #and the rooms where we are
176 self.bridge.call('getRoomJoined', self._getRoomJoinedCB)
264 177
265 178
266 179
267 ## Signals callbacks ## 180 ## Signals callbacks ##
268 181
283 panel.addEntry(content, author, timestamp) 196 panel.addEntry(content, author, timestamp)
284 197
285 def _newMessageCb(self, from_jid, msg, msg_type, to_jid): 198 def _newMessageCb(self, from_jid, msg, msg_type, to_jid):
286 _from = JID(from_jid) 199 _from = JID(from_jid)
287 _to = JID(to_jid) 200 _to = JID(to_jid)
288 for panel in self.mpanels: 201 for panel in self.mpanels + self.other_panels:
289 if isinstance(panel,ChatPanel) and (panel.target.bare == _from.bare or panel.target.bare == _to.bare): 202 if isinstance(panel,ChatPanel) and (panel.target.bare == _from.bare or panel.target.bare == _to.bare):
290 panel.printMessage(_from, msg) 203 panel.printMessage(_from, msg)
291 204
292 def _presenceUpdateCb(self, entity, show, priority, statuses): 205 def _presenceUpdateCb(self, entity, show, priority, statuses):
293 _entity = JID(entity) 206 _entity = JID(entity)
294 #XXX: QnD way to get our status 207 #XXX: QnD way to get our status
295 if self.whoami and self.whoami.bare == _entity.bare and statuses: 208 if self.whoami and self.whoami.bare == _entity.bare and statuses:
296 self.status_panel.changeStatus(statuses.values()[0]) 209 self.status_panel.changeStatus(statuses.values()[0])
297 if not self.whoami or self.whoami.bare != _entity.bare: 210 if (not self.whoami or self.whoami.bare != _entity.bare):
298 self.contact_panel.setConnected(_entity.bare, _entity.resource, show, priority, statuses) 211 self.contact_panel.setConnected(_entity.bare, _entity.resource, show, priority, statuses)
299 212
300 def _roomJoinedCb(self, room_id, room_service, room_nicks, user_nick, profile): 213 def _roomJoinedCb(self, room_id, room_service, room_nicks, user_nick):
301 print "roomJoined"
302 print room_id
303 _target = JID("%s@%s" % (room_id,room_service)) 214 _target = JID("%s@%s" % (room_id,room_service))
215 self.room_list.add(_target)
216 chat_panel = ChatPanel(self, _target, type='group')
217 chat_panel.setUserNick(user_nick)
304 if room_id.startswith('sat_tarot_'): #XXX: it's not really beautiful, but it works :) 218 if room_id.startswith('sat_tarot_'): #XXX: it's not really beautiful, but it works :)
305 self.tab_panel.add(ChatPanel(self, _target), "Tarot") 219 self.addTab(chat_panel, "Tarot")
306 else: 220 else:
307 self.tab_panel.add(ChatPanel(self, _target), str(_target)) 221 self.addTab(chat_panel, str(_target))
222 chat_panel.setPresents(room_nicks)
223 chat_panel.historyPrint()
224
225 def _roomUserJoinedCb(room_id, room_service, room_nicks, user_nick):
226 pass
227
228 def _roomUserLeftCb(room_id, room_service, room_nicks, user_nick):
229 pass
308 230
309 231
310 def _getPresenceStatusCB(self, presence_data): 232 def _getPresenceStatusCB(self, presence_data):
311 for entity in presence_data: 233 for entity in presence_data:
312 for resource in presence_data[entity]: 234 for resource in presence_data[entity]:
313 args = presence_data[entity][resource] 235 args = presence_data[entity][resource]
314 self._presenceUpdateCb("%s/%s" % (entity, resource), *args) 236 self._presenceUpdateCb("%s/%s" % (entity, resource), *args)
315 237
238 def _getRoomJoinedCB(self, room_data):
239 for room in room_data:
240 self._roomJoinedCb(*room)
241
316 if __name__ == '__main__': 242 if __name__ == '__main__':
317 pyjd.setup("http://localhost:8080/libervia.html") 243 pyjd.setup("http://localhost:8080/libervia.html")
318 app = SatWebFrontend() 244 app = SatWebFrontend()
319 app.onModuleLoad() 245 app.onModuleLoad()
320 pyjd.run() 246 pyjd.run()